【UVA 11865】 Stream My Contest (二分+MDST最小树形图)
【题意】
你需要花费不超过cost元来搭建一个比赛网络。网络中有n台机器,编号0~n-1,其中机器0为服务器,其他机器为客户机。一共有m条可以使用的网线,其中第i条网线的发送端是机器ui,接收端是机器vi(数据只能从机器ui单向传输到机器vi),带宽是bi Kbps,费用是ci元。每台客户机应当恰好从一台机器接收数据。你的任务是最大化网络中的最小带宽。
Input
First line of input contains T (≤ 50), the number of test cases. This is followed by T test cases.
Each test case starts with an integer N, M, C (1 ≤ N ≤ 60, 1 ≤ M ≤ 10^4, 1 ≤ C ≤ 10^9), the
number of universities and the number of possible links, and the budget for setting up the network.
Each university is identified by an integer between 0 and N − 1, where 0 is the server.
Next M lines each contain 4 integers, u, v, b, c (0 ≤ u, v < N, 1 ≤ b, c ≤ 10^6, u ̸= v), describing
a possible link from university u to university v, that has the bandwidth of b kbps and of cost c. All
links are unidirectional.
There is a blank line before each test case.
Output
For each test case, output the maximum bandwidth to stream.
If it’s not possible, output ‘streaming not possible.’.
Sample Input
3
3 4 300
0 1 128 100
1 2 256 200
2 1 256 200
0 2 512 300
3 4 500
0 1 128 100
1 2 256 200
2 1 256 200
0 2 512 300
3 4 100
0 1 128 100
1 2 256 200
2 1 256 200
0 2 512 300
Sample Output
128 kbps
256 kbps
streaming not possible.
【分析】
一开始看错题了ORZ,直接无视有向图,直接二分+MST。。
好吧看过来之后就是多了个方向而已。。
那就是MDST,要用朱-刘算法,,中国人发明的吧??
hhhh
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 #define Maxn 110 9 #define Maxm 10010 10 #define INF 0xfffffff 11 12 int n,m,mc; 13 14 struct node 15 { 16 int x,y,b,c; 17 }; 18 node t[Maxm],tt[Maxm]; 19 20 bool cmp(node x,node y) {return x.c<y.c;} 21 int mymin(int x,int y) {return x<y?x:y;} 22 int mymax(int x,int y) {return x>y?x:y;} 23 24 int fa[Maxn]; 25 26 int ffa(int x) 27 { 28 if(fa[x]!=x) fa[x]=ffa(fa[x]); 29 return fa[x]; 30 } 31 32 int in[Maxn],pre[Maxn],id[Maxn],vis[Maxn]; 33 //in -> 最小入边 边权 34 //pre -> 最小入边的发出点 35 //id -> 所在圈的编号 36 //vis 所在链顶端 37 38 int rt; 39 40 bool check(int mid) //MDST 41 { 42 rt=1; 43 int ans=0; 44 int nn=n; 45 for(int i=1;i<=m;i++) tt[i]=t[i]; 46 while(1) 47 { 48 //1、找最小入边 49 for(int i=1;i<=nn;i++) in[i]=INF; 50 for(int i=1;i<=m;i++) 51 { 52 int x=tt[i].x,y=tt[i].y; 53 if(tt[i].c<in[y]&&x!=y&&tt[i].b>=mid) //因为缩边,所以可能有自环 54 { 55 pre[y]=x; 56 in[y]=tt[i].c; 57 } 58 } 59 //2、判断是否有最小生成树 60 for(int i=1;i<=nn;i++) if(i!=rt) 61 { 62 if(in[i]==INF) return 0; 63 } 64 //3、找环 65 int cnt=0; 66 memset(id,-1,sizeof(id)); 67 memset(vis,-1,sizeof(vis)); 68 in[rt]=0; 69 for(int i=1;i<=nn;i++) 70 { 71 ans+=in[i]; 72 int now=i; 73 while(vis[now]!=i&&id[now]==-1&&now!=rt) 74 { 75 vis[now]=i; 76 now=pre[now]; 77 } 78 if(now!=rt&&id[now]==-1) 79 { 80 cnt++; 81 for(int j=pre[now];j!=now;j=pre[j]) 82 { 83 id[j]=cnt; 84 } 85 id[now]=cnt; 86 } 87 } 88 if(cnt==0) break;//没有圈啦233 89 for(int i=1;i<=nn;i++) if(id[i]==-1) 90 id[i]=++cnt; //相当于自己一个圈 91 //4、缩点,重新标记 92 for(int i=1;i<=m;i++) 93 { 94 int x=tt[i].x,y=tt[i].y; 95 tt[i].x=id[x];tt[i].y=id[y]; 96 if(tt[i].x!=tt[i].y) 97 { 98 tt[i].c-=in[y]; 99 } 100 } 101 nn=cnt; 102 rt=id[rt]; 103 } 104 return ans<=mc; 105 } 106 107 void ffind(int l,int r) 108 { 109 int ans=-1; 110 while(l<=r) 111 { 112 int mid=(l+r)>>1; 113 if(check(mid)) ans=mid,l=mid+1; 114 else r=mid-1; 115 } 116 if(ans==-1) printf("streaming not possible.\n"); 117 else printf("%d kbps\n",ans); 118 } 119 120 int main() 121 { 122 int T; 123 scanf("%d",&T); 124 while(T--) 125 { 126 scanf("%d%d%d",&n,&m,&mc); 127 int mn=INF,mx=0; 128 for(int i=1;i<=m;i++) 129 { 130 scanf("%d%d%d%d",&t[i].x,&t[i].y,&t[i].b,&t[i].c); 131 t[i].x++;t[i].y++; 132 mn=mymin(mn,t[i].b); 133 mx=mymax(mx,t[i].b); 134 } 135 sort(t+1,t+1+m,cmp); 136 ffind(mn,mx); 137 } 138 return 0; 139 }
// vis相当于一个不清0的bool
2016-11-01 10:43:31
记录一下MDST做法,求有向最小生成树,也叫最小树形图。
主过程:
1、给所有非根节点选一条权最小的入边。
2、如果有一个点没有入边,那么无解。
3、判断选出来的点是否构成圈,给每个点打上他所在的圈的编号标记(一定是i一个简单环,因为每个点只选一条入边)
4、缩点,求出新的图。
注意:
第4步,入边的权值不是单纯的原图权值,还要减去圈里面和他矛盾的边的权值。
时间复杂度:O(VE)
如果一开始没有给你根怎么破???????????
如果是不定根的情况我们可以虚拟一个根,让虚拟根到每个节点的距离为图上所有边的权值之和加一。这样找到最小树形图后再减掉所有边的权值之和加一就可以了。
搜刮网上MDST的解释:
图论是ACM竞赛中比较重要的组成部分,其模型广泛存在于现实生活之中。因其表述形象生动,思维方式抽象而不离具体,因此深受各类喜欢使劲YY的Acmer的喜爱。这篇文章引述图论中有关有向图最小生成树的部分,具体介绍朱刘算法的求解思路,并结合一系列coding技巧,实现最小树型图O(VE)的算法,并在最后提供了该算法的模版,以供参考。
关于最小生成树的概念,想必已然家喻户晓。给定一个连通图,要求得到一个包含所有顶点的树(原图的子图),使之所构成的边权值之和最小。在无向图中,由于边的无向性质,所以求解变得十分容易,使用经典的kruskal与prim算法已经足够解决这类问题。而在有向图中,则定义要稍微加上一点约束,即以根为起点,沿给定有向边,可以访问到所有的点,并使所构成的边权值之和最小,于是问题开始变得不一样了,我们称之为最小树型图问题。
该问题是由朱永津与刘振宏在上个世纪60年代解决的,值得一提的是,这2个人现在仍然健在,更另人敬佩的是,朱永津几乎可以说是一位盲人。解决最小树型图的算法后来被称作朱刘算法,也是当之无愧的。
首先我们来阐述下算法的流程:算法一开始先判断从固定根开始是否可达所有原图中的点,若不可,则一定不存在最小树形图。这一步是一个很随便的搜索,写多搓都行,不加废话。第二步,遍历所有的边,从中找出除根结点外各点的最小入边,累加权值,构成新图。接着判断该图是否存在环。若不存在,则该图便是所求最小树型图,当前权为最小权。否则对环缩点,然后回到第二步继续判断。
这里存在一系列细节上的实现问题,以确保能够达到VE的复杂度。首先是查环,对于新图来说只有n-1条入边,对于各条入边,其指向的顶点是唯一的,于是我们可以在边表中添加from,表示该边的出发点,并考虑到如果存在环,则对环上所有边反向,环是显然同构的,于是最多作V次dfs就能在新图中找到所有的环,并能在递归返回时对顶点重标号进行缩点,此步的重标号可以用hash数组映射。然后就是重要的改边法,对于所有不在环上的边,修改其权为w-min(v),w为当前边权,min(v)为当前连接v点的最小边权。其数学意义在于选择当前边的同时便放弃了原来的最小入边。我们可以知道,每次迭代选边操作O(E),缩点操作O(V),更新权操作O(E),至少使一个顶点归入生成树,于是能在V-1步内完成计算,其复杂度为O(VE)。
以上为定根最小树型图,对于无固定根最小树型图,只要虚拟一个根连所有的点的权为边权总和+1,最后的结果减去(边权+1)即可。另外对于求所定的根标号,只要搜索被选中的虚边就可以判断了。
View Code1 #include <cstdio> 2 #include <iostream> 3 #include <cmath> 4 #include <cstring> 5 6 using namespace std; 7 8 const int N=101,M=10001,inf=2147483647; 9 struct edge {int u,v,w;} e[M]; 10 int n,m,ans,id[N],pre[N],v[N],inw[N]; 11 12 void zhu_liu(int root) 13 { 14 int s,t,idx=n; 15 while (idx) 16 { 17 for (int i=1;i<=n;++i) inw[i]=inf,id[i]=-1,v[i]=-1; 18 for (int i=1;i<=m;++i) 19 { 20 s=e[i].u;t=e[i].v; 21 if (e[i].w>inw[t] || s==t) continue; 22 pre[t]=s; 23 inw[t]=e[i].w; 24 } 25 inw[root]=0;pre[root]=root; 26 for (int i=1;i<=n;++i) 27 { 28 if (inw[i]==inf) 29 { 30 printf("impossible\n"); 31 return; 32 } 33 ans+=inw[i]; 34 } 35 idx=0; 36 for (int i=1;i<=n;++i) 37 if (v[i]==-1) 38 { 39 t=i; 40 while (v[t]==-1) v[t]=i,t=pre[t]; 41 if (v[t]!=i || t==root) continue; 42 id[t]=++idx; 43 for (s=pre[t];s!=t;s=pre[s]) id[s]=idx; 44 } 45 if (idx==0) continue; 46 for (int i=1;i<=n;++i) 47 if (id[i]==-1) id[i]=++idx; 48 for (int i=1;i<=m;++i) 49 { 50 e[i].w-=inw[e[i].v]; 51 e[i].u=id[e[i].u]; 52 e[i].v=id[e[i].v]; 53 } 54 n=idx; 55 root=id[root]; 56 } 57 printf("%d\n",ans); 58 } 59 int main() 60 { 61 scanf("%d%d",&n,&m); 62 for (int i=1;i<=m;++i) 63 scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 64 zhu_liu(1); 65 return 0; 66 }