例题5.22 比赛网络 UVa11865

来源:互联网 发布:php 图片水印 编辑:程序博客网 时间:2024/04/28 07:23

1.题目描述:点击打开链接

2.解题思路:本题利用二分+最小树形图解决。根据题意显然应该用二分法,假设当前带宽为x,那么问题转化为:能否用带宽大于等于x的网线搭建起来网络?根据题意描述可知,这个网络是一个有向带权图,如果可以从根到达所有结点的话,那么其中一定存在最小树形图,我们可以用朱刘算法求解出这个最小树形图的权值之和,即最终需要的费用,如果这个费用小于等于给定的cost,那么说明有解,否则无解(因为最小树形图的权值之和一定是最小的)。

3.代码:

#include<iostream>#include<algorithm>#include<cassert>#include<string>#include<sstream>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>#include<queue>#include<deque>#include<cstdlib>#include<cstdio>#include<cstring>#include<cmath>#include<ctime>#include<cctype>#include<list>#include<complex>#include<functional>using namespace std;#define me(s) memset(s,0,sizeof(s))#define rep(i,n) for(int i=0;i<(n);i++)#define pb push_backtypedef long long ll;typedef pair <int,int> P;const int INF=1e9;const int maxn=100+10;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); //重边取最小的    }    int dfs(int s)//计算从s出发可以访问多少个结点    {        vis[s]=1;        int ans=1;        for(int i=0;i<n;i++)            if(!vis[i]&&w[s][i]<INF)            ans+=dfs(i);        return ans;    }    bool cycle(int u)//从u出发沿着pre指针找圈    {        max_cid++;         int v=u;        while(cid[v]!=max_cid){cid[v]=max_cid;v=pre[v];}        return v==u;    }    void update(int u)//计算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;        }    }    bool solve(int s)//根节点为s,去寻找最小树形图,如果无解返回false    {        me(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_circle=false;            for(int u=0;u<n;u++)                if(u!=s&&!removed[u]&&cycle(u))            {                have_circle=true;                int v=u;                do  //进行缩圈操作,圈上除了u之外,其他点都删除                {                    if(v!=u)                        removed[v]=1;                    ans+=iw[v];                    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]);//对于圈外点i,把i->v改成i->u;v->i改成u->i                        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_circle)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)//看用前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(){    int T;    scanf("%d",&T);    while(T--)    {        scanf("%d%d%d",&n,&m,&C);        for(int i=0;i<m;i++)            scanf("%d%d%d%d",&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-L)/2;            if(check(mid)){ans=edges[mid-1].b;R=mid-1;}            else L=mid+1;        }        if(ans<0)puts("streaming not possible.");        else printf("%d kbps\n",ans);    }}


0 0