hdu2121 Ice_cream’s world II 最小树形图(难)
这题比HDU4009要难一些。做了4009,大概知道了最小树形图的解法。拿到这题,最直接的想法是暴力。n个点试过去,每个都拿来做一次根。最后WA了,估计是超时了。(很多题都是TLE说成WA,用了G++才知道是TLE,不断修改代码也看不出来错哪了)。
网上的正解是添加一个虚拟根(树根),使得它与n个点都有边相连。HDU4009题这样得到的边有实际的意义,好理解。这题这样得到的边不好理解,并且边权应该是多少也有讲究。别人的解题报告中是把这些边的权值设为原本所有边的权值之和加1。这样做的目的,是为了完全把这些边与原本的边区分开。最后得到的最小树形图有且仅有一条这样的边,并且这条边的终点就是所求的设立首都的点。从这里就可以看出边权设置的作用了。
从4009和2121这两题来看,解无根的最小树形图问题的套路是:添加一个虚根,虚根与其他n个点要连边。其中比较重要的巧妙地使这些的权值有意义。这样就是“无根”转换为“有根”。然后就可以用模版了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N = 1010, M=10005,INF=0x3f3f3f3f; 7 int pre[N],id[N],in[N],vis[N]; 8 int tot, Minr;//边数 9 struct node 10 { 11 int u,v,w; 12 }e[M]; 13 void adde(int i,int j,int k) 14 { 15 e[tot].u=i;e[tot].v=j;e[tot++].w=k; 16 } 17 long long zhuliu(int root ,int vn) 18 { 19 long long ans=0; 20 int cnt; 21 while(1) 22 { 23 for(int i=0;i<vn;i++) 24 in[i]=INF,id[i]=-1,vis[i]=-1; 25 for(int i=0;i<tot;i++) 26 { 27 if(in[e[i].v]>e[i].w && e[i].u!=e[i].v) 28 { 29 pre[e[i].v]=e[i].u; 30 if(e[i].u==root) Minr=i; 31 in[e[i].v]=e[i].w; 32 } 33 } 34 in[root]=0; 35 pre[root]=root; 36 for(int i=0;i<vn;i++) 37 { 38 ans+=in[i]; 39 if(in[i]==INF) 40 return -1; 41 } 42 cnt=0; 43 for(int i=0;i<vn;i++) 44 { 45 if(vis[i]==-1) 46 { 47 int t=i; 48 while(vis[t]==-1) 49 { 50 vis[t]=i; 51 t=pre[t]; 52 } 53 if(vis[t]!=i || t==root) continue; 54 for(int j=pre[t];j!=t;j=pre[j]) 55 id[j]=cnt; 56 id[t]=cnt++; 57 } 58 } 59 if(cnt==0) break; 60 for(int i=0;i<vn;i++) 61 if(id[i]==-1) 62 id[i]=cnt++; 63 for(int i=0;i<tot;i++) 64 { 65 int u,v; 66 u=e[i].u; 67 v=e[i].v; 68 e[i].u=id[u]; 69 e[i].v=id[v]; 70 e[i].w-=in[v]; 71 } 72 vn=cnt; 73 root=id[root]; 74 } 75 return ans; 76 } 77 78 int main() 79 { 80 //freopen("test.txt","r",stdin); 81 int n,m,i,j,a,b,c,sum; 82 while(scanf("%d%d",&n,&m)!=EOF) 83 { 84 tot=0; 85 sum=0; 86 for(i=0;i<m;i++) 87 { 88 scanf("%d%d%d",&a,&b,&c); 89 adde(a,b,c); 90 sum+=c; 91 } 92 sum++; 93 for(i=0;i<n;i++) 94 { 95 adde(n,i,sum); 96 } 97 a=zhuliu(n,n+1); 98 Minr-=m; 99 if(a==-1||a>=2*sum) printf("impossible\n"); 100 else printf("%d %d\n",a-sum,Minr); 101 printf("\n"); 102 } 103 return 0; 104 }