UVA 11865 Stream My Contest

题意:需要用不超过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 1000000000

struct 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;	//根节点没有入边,所以边权为0
		for(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;
	else
		return 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;
				else
					r = mid-1;
			}
			printf("%d kbps\n",r);
		}
	}
	return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值