1. 程式人生 > >POJ 3164 Command Network(最小樹形圖模板題+詳解)

POJ 3164 Command Network(最小樹形圖模板題+詳解)

noop clu html 建立 eof std const temp pri

http://poj.org/problem?id=3164

題意:

求最小樹形圖。

思路:

套模板。

引用一下來自大神博客的講解:http://www.cnblogs.com/acjiumeng/p/7136604.html

算法步驟如下:

1.判斷圖的連通性,若不連通直接無解,否則一定有解。

2.為除了根節點以外的所有點選擇一個權值最小的入邊,假設用pre數組記錄前驅,f數組記錄選擇的邊長,記所選邊權和為temp。

3.(可利用並查集)判斷選擇的的邊是否構成環,若沒有則直接$ans+=temp$並輸出ans,若有,則進行下一步操作。

4.對該環實施縮點操作,設該環上有點$V1,V2……Vi……Vn$,縮成的點為node ,對於所有不在環中的點P進行如下更改:

(1) 點P到node的距離為min{$a[p,Vi]-f[Vi]$} (a為邊集數組)

(2)點node到p的距離為min{$a[Vi,p]$}

操作(1)的理解:先假設環上所有邊均選上,若下次選擇某一條邊進入該環,則可以斷開進入點與進入點的前驅之間的邊,即斷開F[進入點],所以等效為直接把$a[p,node]$賦值為min{$a[p,Vi]-f[Vi]$}。

特別提醒:本題有自環,可以提前刪掉,因為它沒有用。

技術分享

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4
#include<cstdio> 5 #include<sstream> 6 #include<vector> 7 #include<stack> 8 #include<queue> 9 #include<cmath> 10 #include<map> 11 #include<set> 12 using namespace std; 13 typedef long long ll; 14 typedef pair<int,ll> pll;
15 const int inf = 0x3f3f3f3f; 16 const int maxn=1000+5; 17 const int mod=1e9+7; 18 19 int n, m; 20 21 struct point 22 { 23 double x,y; 24 }p[maxn]; 25 26 struct node 27 { 28 int u,v; 29 double w; 30 }edge[maxn*maxn]; 31 32 int pre[maxn],id[maxn],use[maxn]; 33 double in[maxn]; 34 35 double dis(point a,point b) 36 { 37 return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); 38 } 39 40 double mini_tree(int root,int n,int m)//分別是樹根,節點數,邊數,序號從1開始 41 { 42 double ans=0; 43 int u; 44 while(true) 45 { 46 for(int i=1;i<=n;i++) in[i]=inf; 47 for(int i=1;i<=m;i++) 48 { 49 int u=edge[i].u; 50 int v=edge[i].v; 51 if(edge[i].w<in[v]&&u!=v) 52 { 53 in[v]=edge[i].w; 54 pre[v]=u; 55 } 56 }//找最小的入邊 57 for(int i=1;i<=n;i++) 58 { 59 if(i==root)continue; 60 ans+=in[i];//把邊權加起來 61 if(in[i]==inf)//如果存在沒有入弧的點則不存在最小樹形圖 62 return -1; 63 } 64 memset(id,-1,sizeof(id)); 65 memset(use,-1,sizeof(use)); 66 int cnt=0; 67 for(int i=1;i<=n;i++)//枚舉每個點,搜索找環 68 { 69 int v=i; 70 while(v!=root&&use[v]!=i&&id[v]==-1) 71 { 72 use[v]=i; 73 v=pre[v]; 74 } 75 if(v!=root&&id[v]==-1)//當找到環的時候縮點編號 76 { 77 ++cnt; 78 id[v]=cnt; 79 for(u=pre[v];u!=v;u=pre[u]) 80 id[u]=cnt; 81 } 82 } 83 if(cnt==0)//如果沒有環結束程序 84 break; 85 for(int i=1;i<=n;i++)//把余下的不在環裏的點編號 86 if(id[i]==-1) 87 id[i]=++cnt; 88 for(int i=1;i<=m;i++)//建立新的圖 89 { 90 int u=edge[i].u; 91 int v=edge[i].v; 92 edge[i].u=id[u]; 93 edge[i].v=id[v]; 94 if(edge[i].u!=edge[i].v) 95 edge[i].w-=in[v]; 96 } 97 n=cnt;//更新節點數和根節點的編號 98 root=id[root]; 99 } 100 return ans; 101 } 102 103 int main() 104 { 105 //freopen("in.txt","r",stdin); 106 while(~scanf("%d%d",&n,&m)) 107 { 108 for(int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); 109 for(int i=1;i<=m;i++) 110 { 111 scanf("%d%d",&edge[i].u,&edge[i].v); 112 if(edge[i].u!=edge[i].v) 113 edge[i].w=dis(p[edge[i].u],p[edge[i].v]); 114 else edge[i].w=inf; 115 } 116 double ans=mini_tree(1,n,m); 117 if(ans==-1) printf("poor snoopy\n"); 118 else printf("%.2f\n",ans); 119 } 120 return 0; 121 }

POJ 3164 Command Network(最小樹形圖模板題+詳解)