题意(参考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)。
思路:度限制生成树。
算法框架:
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;
}