POJ1639 Picnic Planning (限制入度最小生成树)
作者:互联网
节点1是有度数限制的,把节点1去掉,就会形成若干个连通块,在每个连通块内部求最小生成树(prim算法实现),并求出每个连通块与1相连的最短的边,这样形成了初始状态的生成树。
假设(1,x)这条边没在生成树中,如果在生成树中从1到x的路径中的最大边权大于(1,x),考虑加上(1,x),去掉这条最大边权的边,答案就更加优秀了,若干次重复这样的操作,直到达到度数限制,就可以得到最优解。
记录最大边权的思路基于DP,回溯打标记。
完成一次拓展后,加入了(1,x)这条边,修改x的f[ ],fx[ ],fy[ ],给1打上标记,从x开始DP,因为只是修改了x所在的连通块,没有必要再从1遍历一次。(可以画图理解一下)。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,s,deg,ans; 4 int a[32][32],d[32],conn[32]; 5 bool v[32],c[32]; 6 int tree[32][32]; 7 int ver[32],p; 8 int f[32],fx[32],fy[32];//1-x的路径最大边权是f[x],两个端点是fx[x],fy[x] 9 10 void read(){ 11 map<string,int> h; 12 cin>>m; 13 h["Park"]=1;n=1; 14 memset(a,0x3f,sizeof(a)); 15 for(int i=1;i<=m;i++){ 16 int x,y,z; 17 char sx[12],sy[12]; 18 scanf("%s%s%d",sx,sy,&z); 19 if(!h[sx]) h[sx]=++n; 20 if(!h[sy]) h[sy]=++n; 21 x=h[sx],y=h[sy]; 22 a[x][y]=min(a[x][y],z); 23 a[y][x]=min(a[y][x],z); 24 } 25 cin>>s; 26 } 27 28 void prim(int root){ 29 d[root]=0; 30 for(int i=1;i<=p;i++){ 31 int x=0; 32 for(int j=1;j<=p;j++) 33 if(!v[ver[j]]&&(x==0||d[ver[j]]<d[x])) x=ver[j]; 34 v[x]=true; 35 for(int j=1;j<=p;j++){ 36 int y=ver[j]; 37 if(!v[y]&&d[y]>a[x][y]) 38 d[y]=a[x][y],conn[y]=x; 39 } 40 }//prim算法模板 41 int closest=root; 42 for(int i=1;i<=p;i++){ 43 int x=ver[i]; 44 if(x==root) continue; 45 ans+=d[x]; 46 tree[conn[x]][x]=tree[x][conn[x]]=d[x]; 47 if(a[1][x]<a[1][closest]) closest=x;//记录连通块中和1相连最短的边 48 } 49 deg++; 50 ans+=a[1][closest]; 51 tree[1][closest]=tree[closest][1]=a[1][closest]; 52 } 53 54 void dfs(int x){ 55 ver[++p]=x; 56 c[x]=true; 57 for(int y=2;y<=n;y++) 58 if(a[x][y]!=0x3f3f3f3f&&!c[y]) dfs(y); 59 } 60 61 void prim_for_all_comp(){ 62 memset(d,0x3f,sizeof(d)); 63 memset(v,0,sizeof(v)); 64 memset(tree,0x3f,sizeof(tree)); 65 c[1]=true; 66 for(int i=2;i<=n;i++){ 67 if(!c[i]){ 68 p=0; 69 dfs(i); 70 prim(i); 71 } 72 } 73 } 74 75 void dp(int x){ 76 v[x]=true; 77 for(int y=2;y<=n;y++){ 78 if(tree[x][y]!=0x3f3f3f3f&&!v[y]){ 79 if(f[x]>tree[x][y]){ 80 f[y]=f[x]; 81 fx[y]=fx[x],fy[y]=fy[x]; 82 }else{ 83 f[y]=tree[x][y]; 84 fx[y]=x,fy[y]=y; 85 } 86 dp(y); 87 } 88 } 89 v[x]=false;//回溯 90 } 91 92 bool solve(){ 93 int min_val=1<<30,mini; 94 for(int i=2;i<=n;i++){//枚举从1出发的非树边(1,i),看加哪一条 95 if(tree[1][i]!=0x3f3f3f3f||a[1][i]==0x3f3f3f3f) continue; 96 //加入非树边(1,i),删去树边(fx[i],fy[i]) 97 if(a[1][i]-tree[fx[i]][fy[i]]<min_val){ 98 min_val=a[1][i]-tree[fx[i]][fy[i]]; 99 mini=i; 100 } 101 } 102 if(min_val>=0) return false; 103 ans+=min_val; 104 tree[1][mini]=tree[mini][1]=a[1][mini]; 105 tree[fx[mini]][fy[mini]]=tree[fy[mini]][fx[mini]]=0x3f3f3f3f;//删去原边,增加新边 106 f[mini]=a[1][mini]; 107 fx[mini]=1,fy[mini]=mini; 108 v[1]=true; 109 dp(mini);//重新计算以mini为根的子树的dp状态 110 return true; 111 } 112 113 int main(){ 114 read(); 115 prim_for_all_comp(); 116 memset(v,0,sizeof(v)); 117 dp(1); 118 while(deg<s){ 119 if(!solve()) break; 120 deg++; 121 } 122 printf("Total miles driven: "); 123 cout<<ans<<endl; 124 }
标签:mini,Picnic,int,32,入度,tree,fx,Planning,fy 来源: https://www.cnblogs.com/yhxnoerror/p/16489928.html