poj 1639 度限制生成树(小矮人野餐)

题意(参考http://www.cnblogs.com/SueMiller/archive/2011/08/10/2133871.html):矮人虽小却喜欢乘坐巨大的轿车,轿车大到可以装下无论多少矮人。某天,N个矮人打算到野外聚餐。为了集中到聚餐地点,矮人A要么开车到矮人B家中,留下自己的轿车在矮人B家,然后乘坐B的轿车同行;要么直接开车到聚餐地点,并将车停放在聚餐地。虽然矮人的家很大,可以停放无数量轿车,但是聚餐地点却最多只能停放K辆轿车。现在给定一张加权无向图,它描述了N个矮人的家和聚餐地点,要你求出所有矮人开车的最短总路程。
思路:度限制生成树。 
算法框架:
1. 先求出最小m度限制生成树;
2. 由最小m度限制生成树得到最小m+1度限制生成树;
3. 当dT(v0)=k时停止(即当V0的度为k的时候停止);

第一步:求解最小m度限制生成树:原图中去掉和V0相连的所有边,得到m个连通分量,而这m个连通分量必须通过v0来连接,所以,在图G 的所有生成树中dT(v0)≥m。也就是说,当k<m时,问题无解。对每个连通分量求一次最小生成树,对于每个连通分量V’,求一点v1,v1∈V',且ω(v0,v1)=min{ω(v0,v')|v'∈V'},则该连通分量通过边(v1,v0)与v0相连。于是,我们就得到了一个m度限制生成树,不难证明,这就是最小m度限制生成树。

第二步:由最小m度限制生成树,得到最小m+1度限制生成树,连接和V0相邻的点v,则可以知道一定会有一个环出现,只要找到这个环上的最大权边,用边(V0, v)替换掉,就可以得到一个m+1度限制生成树,枚举所有和V0相邻点v,找到替换后增加权值最小的一次替换,就可以求得m+1度限制生成树。。如果每添加一条边,都需要对环上的边一一枚举,时间复杂度将比较高,这里,动态规划就有了用武之地。设Best(v)为路径v0—v上与v0无关联且权值最大的边。定义father(v)为v的父结点,动态转移方程:Best(v)=max(Best(father(v)),ω(father(v),v)),边界条件为Best[v0]=-∞,Best[v’]=-∞| (v0,v’)∈E(T)。

第三步:当度为K的时候就可以退出了。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 30
#define INF 0x3fffffff
char name[N][12];
struct edge{
	int x,y,w;
}e[421],dis[N];//dis表示节点i到度限制点v0的距离
int n,m,res,now_d,constraind_degree,v0;
int g[N][N],tree[N][N],father[N],dis2v0[N],from[N];
void init(){
	int i,j;
	n = res = 0;
	for(i = 0;i<N;i++){
		father[i] = i;
		dis2v0[i] = INF;
	}
	for(i = 0;i<N;i++)
		for(j= 0;j<N;j++)
			g[i][j] = tree[i][j] = INF;
}
int cmp(const struct edge *a,const struct edge *b){
	return (*a).w - (*b).w;
}
int find(int x){
	if(father[x] == x)
		return x;
	return father[x] = find(father[x]);
}
int getnum(char s[12]){//n为顶点数,m为边数
	int i;
	for(i = 0;i<n;i++)
		if(!strcmp(s,name[i]))
			return i;
	strcpy(name[n],s);
	return n++;
}
void dfs(int i,int fa){//更新节点i距离v0的距离值,fa为祖先
	int j;
	if(i == fa)
		dis[i].w = 0;
	for(j = 0;j<n;j++){
		if(j==v0 || j==fa ||tree[i][j]==INF)
			continue;
		if(dis[i].w < tree[i][j]){//如果ij是圈中距离最大的边
			dis[j].w = tree[i][j];
			dis[j].x = i;
			dis[j].y = j;
		}else{//否则照搬节点i的信息
			dis[j].w = dis[i].w;
			dis[j].x = dis[i].x;
			dis[j].y = dis[i].y;
		}
		dfs(j,i);
	}
}
int main(){
	int i,j,pos,w,a,b,temp;
	char name1[12],name2[12];
	freopen("a.txt","r",stdin);
	init();
	scanf("%d",&m);
	for(i = 0;i<m;i++){//读图,边信息存入g数组
		scanf("%s %s %d",name1,name2,&w);
		a = getnum(name1);
		b = getnum(name2);
		e[i].x = a;
		e[i].y = b;
		e[i].w = w;
		g[a][b] = g[b][a] = w;
	}
	scanf("%d",&constraind_degree);
	for(i = 0;i<n;i++)//找到度限制节点,为v0
		if(!strcmp(name[i],"Park")){
			v0 = i;
			break;
		}
	qsort(e,m,sizeof(struct edge),cmp);//对边权排序,为用Kruskal求MST
	for(i = 0;i<m;i++){
		a = find(e[i].x);
		b = find(e[i].y);
		if(a!=v0 && b!=v0 && a!=b){//对删去v0的图求MST
			father[a] = b;
			res += e[i].w;
			tree[e[i].x][e[i].y] = tree[e[i].y][e[i].x] = e[i].w;
		}
	}
	for(i = 0;i<n;i++){//寻找每个连通分量中距离v0最近的点,并将其记录在from数组中
		a = find(i);
		if(g[i][v0] < dis2v0[a]){
			dis2v0[a] = g[i][v0];
			from[a] = i;
		}
	}
	for(i = 0,now_d = 0;i<n;i++)//v0向每个连通分量添加一条边,即求得k度限制生成树(k为去掉v0的连通分支个数)
		if(dis2v0[i] < INF){
			now_d ++;
			res += dis2v0[i];
			tree[from[i]][v0] = tree[v0][from[i]] = dis2v0[i];
			dfs(from[i],from[i]);//更新顶点距离v0的距离
		}
	for(i = now_d+1;i<=constraind_degree;i++){//至此求完now_d度限制MST,接下来扩展至要求的度数
		temp = 0;
		for(j = 0;j<n;j++)
			if(tree[v0][j]==INF && g[v0][j]<INF)//与v0直连但是没有选中的边
				if(g[v0][j]-dis[j].w < temp){//只有加入的边比圈中最大距离边小才有意义
					temp = g[v0][j] - dis[j].w;
					pos = j;
				}
		if(!temp)
			break;
		res += temp;
		tree[v0][pos] = tree[pos][v0] = g[v0][pos];//更新MST
		tree[dis[pos].x][dis[pos].y] = tree[dis[pos].y][dis[pos].x] = INF;
		for(j = 0;j<n;j++)//更新节点距离v0的距离信息dis
			if(tree[j][v0]<INF)
				dfs(j,j);
	}
	printf("Total miles driven: %d\n",res);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值