训练指南 UVA- 11865(有向最小生成树 + 朱刘算法 + 二分)

Posted luowentao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了训练指南 UVA- 11865(有向最小生成树 + 朱刘算法 + 二分)相关的知识,希望对你有一定的参考价值。


layout: post
title: 训练指南 UVA- 11865(有向最小生成树 + 朱刘算法 + 二分)
author: "luowentaoaa"
catalog: true
mathjax: true
tags:
- 最小生成树
- 图论
- 训练指南


Stream My Contest

UVA - 11865

二分带宽,然后判断最小生成树是否小于cost值。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=100+50;
const ll inf=1000000000;;

/// 固定根的最小树型图,邻接矩阵写法
struct MDST{
    int n;
    int w[maxn][maxn];    ///边权
    int vis[maxn];        ///访问标记,仅用来判断无解
    int ans;              ///计算答案
    int removed[maxn];    ///每个点是否被删除
    int cid[maxn];        ///所在圈编号
    int pre[maxn];        ///最小入边的起点
    int iw[maxn];         ///最小入边的权值
    int max_cid;          ///最大圈编号

    void init(int n){
        this->n=n;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)w[i][j]=inf;
    }
    void AddEdge(int u,int v,int cost){
        w[u][v]=min(w[u][v],cost);  ///重边取权值最小的
    }

    ///从s出发能到达多少个结点
    int dfs(int s){
        int ans=1;
        vis[s]=1;
        for(int i=0;i<n;i++)if(!vis[i]&&w[s][i]<inf)ans+=dfs(i);
        return ans;
    }
    ///从u出发沿着pre指针找圈
    bool cycle(int u){
        max_cid++;
        int v=u;
        while(cid[v]!=max_cid){cid[v]=max_cid;v=pre[v];}
        return v==u;
    }
    /// 计算u的最小入弧,入弧起点不得在圈c中
    void update(int u){
        iw[u]=inf;
        for(int i=0;i<n;i++)
        if(!removed[i]&&w[i][u]<iw[u]){
            iw[u]=w[i][u];
            pre[u]=i;
        }
    }
    ///根节点为s,如果失败返回false
    bool solve(int s){
        memset(vis,0,sizeof(vis));
        if(dfs(s)!=n)return false;
        memset(removed,0,sizeof(removed));
        memset(cid,0,sizeof(cid));
        for(int u=0;u<n;u++)update(u);
        pre[s]=s;iw[s]=0;      /// 根结点特殊处理
        ans=max_cid=0;
        for(;;){
            bool have_cycle=false;
            for(int u=0;u<n;u++)if(u!=s&&!removed[u]&&cycle(u)){
                have_cycle=true;
                /// 以下代码缩圈,圈上除了u之外的结点均删除
                int v=u;
                do{
                    if(v!=u)removed[v]=1;
                    ans+=iw[v];
                    /// 对于圈外点i,把边i->v改成i->u(并调整权值);v->i改为u->i
                    /// 注意圈上可能还有一个v'使得i->v'或者v'->i存在,因此只保留权值最小的i->u和u->i
                    for(int i=0;i<n;i++)if(cid[i]!=cid[u]&&!removed[i]){
                        if(w[i][v]<inf)w[i][u]=min(w[i][u],w[i][v]-iw[v]);
                        w[u][i]=min(w[u][i],w[v][i]);
                        if(pre[i]==v)pre[i]=u;
                    }
                    v=pre[v];
                }while(v!=u);
                update(u);
                break;
            }
            if(!have_cycle)break;
        }
        for(int i=0;i<n;i++)
            if(!removed[i])ans+=iw[i];
        return true;
    }
};
MDST solver;
struct Edge{
    int u,v,b,c;
    bool operator <(const Edge& rhs)const{
        return b>rhs.b;
    }
};
const int maxm=10000+10;
int n,m,C;
Edge edges[maxm];
bool check(int cnt){
    solver.init(n);
    for(int i=0;i<cnt;i++)
        solver.AddEdge(edges[i].u,edges[i].v,edges[i].c);
    if(!solver.solve(0))return false;
    return solver.ans<=C;
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int t;
  /*  int a=inf;
    cout<<a<<endl<<1000000000<<endl;*/
    cin>>t;
    while(t--){
        cin>>n>>m>>C;
        for(int i=0;i<m;i++)cin>>edges[i].u>>edges[i].v>>edges[i].b>>edges[i].c;
        sort(edges,edges+m);
        int l=1,r=m,ans=-1;
        while(l<=r){
            int mid=(l+r)/2;
            if(check(mid)){ans=edges[mid-1].b;r=mid-1;}
            else l=mid+1;
        }
        if(ans<0)cout<<"streaming not possible."<<endl;
        else cout<<ans<<" kbps"<<endl;
    }
    return 0;
}

以上是关于训练指南 UVA- 11865(有向最小生成树 + 朱刘算法 + 二分)的主要内容,如果未能解决你的问题,请参考以下文章

算法竞赛训练指南11.2 最小生成树

UVA:11183:Teen Girl Squad (有向图的最小生成树)

训练指南 网络流题集

Stream My Contest UVA - 11865(带权最小树形图+二分最小值最大化)

训练指南 UVA - 11419(二分图最小覆盖数)

训练指南 UVA - 11478(最短路BellmanFord+ 二分+ 差分约束)