UVA 11865 Stream My Contest

来源:互联网 发布:学霸有多努力知乎 编辑:程序博客网 时间:2024/05/17 01:57

题意:需要用不超过cost元来建立一个能到所有点的网络,每条边都有花费和带宽,要求一颗能到所有点的树,使得整棵树的最小带宽(即该图中所有点的带宽的最小值)最大。

解析:如果已知最小带宽了,问题转化为:如果仅用小于此带宽的网线,是否可以再给定花费内成功搭建网络,则明显应该求最小生成树,因为是有向图,所以就是最小树形图,可以用朱刘算法来求。朱刘算法详解:http://blog.sina.com.cn/s/blog_6af663940100ls4h.html

我的代码里注释还是挺详细的,如果还有不懂得话,大家可以自己画个图模拟一下过程就行了

然后可以二分最小带宽,删去图中比最小带宽的带宽还小的边。
当最小带宽越小,删除的边就越少,就越容易构成最小树形图。因为要求最小带宽的最大值,所以就可以二分这个最小带宽

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define MEM(x,y) memset(x,y,sizeof(x))#define Pmax 100#define Emax 11000#define Max 1000000000struct edge{int from,to;int len;//长度int band;//带宽}e[5*Emax];int pre[Pmax],vis[Pmax];//pre[]用来存点的前驱点,vis[]用来记录在最小树形图中该点是由那个点遍历得到的int id[Pmax];//id[]用来存缩环之后点合并的坐标int in[Pmax];//in[]存进入该点的最短入边int N,M,C;int cnt;void addedge(int a,int b,int c,int d)//x代表从哪开始存{e[cnt].band = c;e[cnt].from = a;e[cnt].to = b;e[cnt].len = d;e[cnt+M].band = c;//因为每一次在Directed_MST里都会改变e[],所以把最初的存在M->2*M-1里e[cnt+M].from = a;e[cnt+M].to = b;e[cnt+M].len = d;cnt++;}bool Directed_MST(int root ,int Band,int n)//Band是这次所求的最小带宽,n是点的个数{long long ans = 0;while(1){//求每个点的最小入边MEM(pre,-1);for(int i = 0 ; i < n ; i ++)in[i] = Max;for(int i = 0 ; i < M; i++){int from = e[i].from;int to = e[i].to;if(from != to && e[i].band >= Band && e[i].len < in[to])//3个条件:没有自环;大于最小带宽;求in[i]最小值{in[to] = e[i].len;pre[to] = from;}}//判断有没有为树for(int i = 0 ; i < n;i++){if(i == root)continue;if(in[i]==Max)//代表有的点没到,即不可能是最小树形图了return false;}//找到环int subnode = 0;//用来记录缩环后的环的替代点MEM(vis,-1);MEM(id,-1);in[root] = 0;//根节点没有入边,所以边权为0for(int i = 0 ; i < n;i++){ans += in[i];//注意此时加上所有的点的入边权值,在后面有相应的对应int temp = i;while(vis[temp] != i && id[temp] == -1 && temp != root){vis[temp] = i;//temp由i点遍历得到temp = pre[temp];}//i点向父节点遍历如果有环的话,temp肯定是环中的某个点,不一定是i点if(temp != root && id[temp] == -1){int u = pre[temp];for( ;u != temp; u = pre[u])id[u] = subnode;//同一个环中的所有点都是用这个替代点表示id[temp] = subnode;subnode++;}}if(subnode == 0)//代表没有环了,也就是说现在已经是最小树形图了break;for(int i = 0 ; i < n; i ++)if(id[i] == -1)//对于不在环中的点,也给他们重新找替代点id[i] = subnode++;//缩环,重新标记for(int i = 0 ; i < M ; i ++){int temp = e[i].to;e[i].from = id[e[i].from];//全部更新为新的替代点e[i].to = id[e[i].to];if(e[i].from  != e[i].to)//如果不是在同一个环内的话,就要减少边长e[i].len -= in[temp];//对于指向环的边,这么减没问题;对于从环内点指向外面的边//之所以这么减,是因为上面ans已经把所有点的入点边权都加上了,对应上面//也就是说如果ans表示的仅仅是环内的边权话,那么就只是入边减,出边不减了}root = id[root];//最后更新根节点和点的个数n = subnode;}if(ans <= C)return true;elsereturn false;}int main(){int T;scanf("%d",&T);while(T--){scanf("%d%d%d",&N,&M,&C);int maxb = 0,minb = Max;//用来存最大最小带宽,后面二分用cnt = 0;for(int i = 1; i <= M ; i ++){int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);addedge(a,b,c,d);maxb = max(maxb,c);minb = min(minb,c);}if(Directed_MST(0,minb,N) == false)//如果最小带宽取最小值时(即图中的每条边都没删)都不能构成最小树形图printf("streaming not possible.\n");else{int l = minb,r = maxb;int mid;while(l<r)//二分过程{mid = l+(r-l+1)/2;for(int i = 0 ; i < M; i ++)e[i] = e[i+M];if(Directed_MST(0,mid,N) == true)//如果最小带宽满足,就可以取更大的带宽l = mid;elser = mid-1;}printf("%d kbps\n",r);}}return 0;}



原创粉丝点击