1. 程式人生 > >HDU-2121-無根(不定根)最小樹形圖模板

HDU-2121-無根(不定根)最小樹形圖模板

題目大意:給你N個城市,選擇一個城市建首都,城市編號為0~N-1,給你M條路,每條路包括u,v,w,即從u到v的花費為w,現在問你那個城市建首都的話,從這個城市到其餘城市的花費最小,最小花費是多少,如果有多個適合的城市,輸出編號小的城市。無法到達輸出impossible。

解題思路:這是無根最小樹形圖的題,我們可以先假定一個虛根,即源點,並設源點到每個城市的權值為   全圖所有權值之和大一點,然後就是樹形圖模板,細節在程式碼中。

程式碼如下:

[cpp] view plain copy print?
  1. //本題邊長都為__int64,其實int型也可以過,但長整型應該更廣,所以本題都為__int64
  2. #include <cstdio>
  3. #include<iostream>
  4. #include <cstring>
  5. #include <cstdlib>
  6. #include<cmath>
  7. usingnamespace std;  
  8. typedefdouble type;  
  9. #define INF 2000000000
  10. #define N 1005
  11. struct edge //圖的結構體
  12. {  
  13.     int u,v;__int64 w;  
  14. }e[10005];  
  15. int m,n,pre[N],id[N],visit[N],xroot;  
  16. __int64 in[N],sum;  
  17. //eCnt為圖中的邊數
  18. //n為圖中的頂點數
  19. //pre[i]為頂點i的前驅節點
  20. //id[i]為縮環,形成新圖的中間量
  21. //in[i]為點i的最小入邊
  22. //visit[i]遍歷圖時記錄頂點是否被訪問過
  23. __int64 directedMST(int root,int nv,int ne)  
  24. {  
  25.     __int64 ans=0;  
  26.     while(1)  
  27.     {  
  28.         //1.找最小入邊
  29.         for(int i=0;i<nv;i++) in[i]=INF;  
  30.         for(int i=0;i<ne;i++)  
  31.         {  
  32.             int u=e[i].u;  
  33.             int v=e[i].v;  
  34.             if
    (u!=v&&e[i].w<in[v])  
  35.             {  
  36.                 if(u==root)  //此處標記與源點相連的最小邊
  37.                     xroot=i;  
  38.                 in[v]=e[i].w;  
  39.                 pre[v]=u;  
  40.             }  
  41.         }  
  42.         for(int i=0;i<nv;i++)                          //判斷圖是否連通
  43.             if(i!=root&&in[i]==INF) return -1;  //除了跟以外有點沒有入邊,則根無法到達它
  44.          //2.找環
  45.         int nodeCnt=0;          //圖中環的數目
  46.         memset(id, -1, sizeof(id));  
  47.         memset(visit, -1, sizeof(visit));  
  48.         in[root]=0;  
  49.         for(int i=0;i<nv;i++)  
  50.         {  
  51.             ans+=in[i];  
  52.             int v=i;  
  53.             while(visit[v]!=i&&id[v]==-1&&v!=root)//每個點尋找其前序點,要麼最終尋找至根部,要麼找到一個環
  54.             {  
  55.                 visit[v]=i;  
  56.                 v=pre[v];  
  57.             }  
  58.             if(v!=root&&id[v]==-1)//縮點
  59.             {  
  60.                 for(int u=pre[v];u!=v;u=pre[u])  
  61.                     id[u]=nodeCnt;  
  62.                 id[v]=nodeCnt++;  
  63.             }  
  64.         }  
  65.         if(nodeCnt==0) break;//如果無環,跳出迴圈
  66.         for(int i=0; i<nv; i++)  
  67.             if(id[i]==-1)  
  68.                 id[i]=nodeCnt++;  
  69.         //3.縮點,重新標記
  70.         for(int i=0;i<ne;i++)  
  71.         {  
  72.             int v=e[i].v;  
  73.             e[i].u=id[e[i].u];  
  74.             e[i].v=id[e[i].v];  
  75.             if(e[i].u!=e[i].v)  
  76.                 e[i].w-=in[v];  
  77.         }  
  78.         nv=nodeCnt;  
  79.         root=id[root];  
  80.     }  
  81.     return ans;  
  82. }  
  83. int main()  
  84. {  
  85.     int m;  
  86.     while(scanf(“%d%d”,&n,&m)!=EOF)  
  87.     {  
  88.         sum=0;  
  89.         for(int i=0;i<m;i++)  
  90.         {  
  91.             scanf(”%d%d%I64d”,&e[i].u,&e[i].v,&e[i].w);  
  92.             e[i].u++;e[i].v++;     //都++之後,把0設為超級源點,聯通各點
  93.             sum+=e[i].w;  
  94.             if(e[i].u==e[i].v)  
  95.                 e[i].w=INF;//消除自環
  96.         }  
  97.         sum++;      //此處必須++,因為需要權值比總權值大,因為這個w幾次,,,
  98.         for(int i=m;i<n+m;i++)  
  99.         {  
  100.             e[i].u=0;  
  101.             e[i].v=i-m+1;  
  102.             e[i].w=sum;  
  103.         }  
  104.         __int64 ans=directedMST(0,n+1,m+n);  
  105.         if(ans==-1 || ans-sum>=sum) printf(“impossible\n”);//ans-sum是除去虛根的最小樹形圖的最短路徑,如果這個距離比所有的邊權值和sum還大,說明還有另外的邊由虛點發出,故說明此圖不連通
  106.         else printf(“%I64d %d\n”, ans-sum, xroot-m);  
  107.         printf(”\n”);  
  108.     }  
  109.     return 0;  
  110. }