POJ 3164 Command Network (最小树形图)
生活随笔
收集整理的這篇文章主要介紹了
POJ 3164 Command Network (最小树形图)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
【題目鏈接】http://poj.org/problem?id=3164
【解題思路】百度百科:最小樹形圖 】里面有詳細(xì)的解釋,而Notonlysucess有精簡的模板,下文有對其模板的一點解釋,前提是對朱劉算法有所了解
【PS】這題沒必要寫題解,學(xué)了朱劉算法并不是表示你就鍛煉到了思維了,在看最小樹形圖的形成時,對縮點那部分內(nèi)容的算法和思路感嘆不已,我想這就是算法的魅力!!
要理解的話,最好結(jié)合那張轉(zhuǎn)載得很瘋狂的圖,但單看不行,自己將每個過程手動筆畫一下更容易理解,開始搜題解的時候幾乎都是用了模板,本來也想理解之后靠模板過了就算了以后如果遇到了這類題,二話不說直接上模板,后來自己還是手動的敲了一下,在OJ上提交了不差20次吧~~
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<cstdlib> 5 #define SIZE 102 6 #define MAXN 1 << 14 7 8 using namespace std; 9 10 const double inf = 1 << 30; 11 const double eps = 1e-8; 12 int nv, m, ne, root, cnt; 13 14 struct Edge{ 15 int u, v; 16 double cost; 17 }; 18 struct Edge edge[MAXN]; 19 double x[SIZE], y[SIZE]; 20 int vis[SIZE]; 21 int circle_id[SIZE]; 22 double node[SIZE][SIZE]; 23 double in[SIZE]; 24 int pre[SIZE]; 25 26 double dis(int a, int b) 27 { 28 return sqrt((x[a]-x[b])*(x[a]-x[b]) + (y[a]-y[b])*(y[a]-y[b])); 29 } 30 31 void dfs(int cur) 32 { 33 vis[cur] = 1; 34 for(int i=1; i<=nv; ++i) 35 if(!vis[i] && node[cur][i] > inf) 36 dfs(i); 37 } 38 39 bool exit_circle(double& res) 40 { 41 int update_id = 1; 42 memset(vis, -1, sizeof(vis)); 43 memset(circle_id, -1, sizeof(circle_id)); 44 in[root] = 0; 45 for(int i=1; i<=nv; ++i) 46 { 47 res += in[i]; 48 int v = i; 49 while(vis[v] != i && circle_id[v] == -1 && v != root) 50 { 51 vis[v] = i; 52 v = pre[v]; 53 } 54 if(vis[v] == i) 55 { 56 for(int u = pre[v]; u != v; u = pre[u]) 57 circle_id[u] = update_id; 58 circle_id[v] = update_id++; 59 } 60 } 61 if(update_id == 1) return true; 62 for(int i=1; i <= nv; ++i) 63 if(circle_id[i] == -1) 64 circle_id[i] = update_id++; 65 66 for(int i=0; i < ne; ++i) 67 { 68 int u = edge[i].u; 69 int v = edge[i].v; 70 edge[i].u = circle_id[u]; 71 edge[i].v = circle_id[v]; 72 if(edge[i].u != edge[i].v) edge[i].cost -= in[v]; 73 } 74 nv = update_id - 1; 75 root = circle_id[root]; 76 return false; 77 } 78 79 bool insert() 80 { 81 for(int i=1; i<=nv; ++i) in[i] = inf; 82 for(int i=0; i<ne; ++i) 83 { 84 int& e = edge[i].v; 85 if(e == root) continue; 86 if(e != edge[i].u && in[e] > edge[i].cost) 87 { 88 in[e] = edge[i].cost; 89 pre[e] = edge[i].u; 90 } 91 } 92 for(int i=1; i <= nv; ++i) 93 if(i != root && inf - in[i] < eps) return false; 94 return true; 95 } 96 97 int main() 98 { 99 while(scanf("%d%d", &nv, &ne) != EOF) 100 { 101 for(int i=1; i<=nv; ++i) 102 scanf("%lf%lf", &x[i], &y[i]); 103 cnt = 0; 104 memset(node, 0, sizeof(node)); 105 for(int i=0; i<ne; ++i) 106 { 107 int u, v; 108 scanf("%d%d", &u, &v); 109 if(u != v) 110 edge[i].cost = dis(u, v); 111 else 112 edge[i].cost = inf; 113 edge[i].u = u; 114 edge[i].v = v; 115 } 116 root = 1; 117 bool flag = false; 118 double ans = 0; 119 do 120 { 121 if(!insert()) 122 { 123 flag = true; 124 break; 125 } 126 }while(!exit_circle(ans)); 127 if(flag) printf("poor snoopy\n"); 128 else printf("%.2f\n", ans); 129 } 130 return 0; 131 } 1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #define SIZE 104 5 #define MAXN 10002 6 7 using namespace std; 8 9 const double esp = 1e-10; 10 const double inf = 1<<30; 11 12 int nv, ne; 13 14 struct Edge{ 15 int v, u; 16 double cost; 17 }edge[MAXN]; 18 19 int vis[SIZE], circle_id[SIZE]; 20 int pre[SIZE]; 21 double in[SIZE]; 22 double x[SIZE], y[SIZE]; 23 24 double dis(int v, int u) 25 { 26 return sqrt((x[v]-x[u])*(x[v]-x[u]) + (y[v]-y[u])*(y[v]-y[u])); 27 } 28 29 bool Traverse(double& res) 30 {//基本來自于模板 31 int root = 1; 32 while(true) 33 { 34 for(int i = 1; i <= nv; ++i) in[i] = inf+SIZE; 35 for(int i = 0; i < ne; ++i) 36 {//集當(dāng)前結(jié)點的各自最小的入邊 37 int& u = edge[i].u; 38 if(in[u] > edge[i].cost && u != edge[i].v) 39 { 40 in[u] = edge[i].cost; 41 pre[u] = edge[i].v; 42 } 43 } 44 //在當(dāng)前情況下如果有任何一個點沒有最小邊,說明不能形成最小樹形圖 45 //但這里沒必要每次都判斷,只在第一次進(jìn)行判斷即可 46 for(int i = 1; i <= nv; ++i) 47 if(i != root && in[i] > inf) return false; 48 49 int credit = 1; 50 in[root] = 0; 51 memset(vis, -1, sizeof(vis)); 52 memset(circle_id, -1, sizeof(vis)); 53 for(int i = 1; i <= nv; ++i) 54 {//res為什么可以一直在加,首先第一次其就將有環(huán)沒環(huán)的【結(jié)點最小邊】的權(quán)值都加了,但就像縮點的理由所說的一樣 55 //環(huán)中沒必要加的一條邊再后來的減掉了,看下面的 ‘##’處 56 res += in[i]; 57 int v = i; 58 while(vis[v] != i && circle_id[v] == -1 && v != root) 59 {//這里并不能將 circle_id[v] == -1 這個條件提取出來提前判斷 60 //一個結(jié)點假設(shè)其不在環(huán)內(nèi),那么其結(jié)果是退后到根點或者退后到一個結(jié)點是環(huán)內(nèi)點(已判斷其為環(huán)內(nèi)的點) 61 vis[v] = i; 62 v = pre[v]; 63 } 64 if(vis[v] == i) 65 {//其實這里已經(jīng)在縮點,將環(huán)內(nèi)的點寫入一個統(tǒng)一的結(jié)點編號 66 for(int u = pre[v]; u != v; u = pre[u]) 67 circle_id[u] = credit; 68 circle_id[v] = credit++; 69 } 70 } 71 if(credit == 1) return true; 72 for(int i = 1; i <= nv; ++i) 73 if(circle_id[i] == -1) circle_id[i] = credit++; 74 for(int i = 0; i < ne; ++i) 75 {//如果更新后兩個結(jié)點有相同的編號,其作用體現(xiàn)在上面求最小邊中 :u != edge[i].v的判斷 76 int u = edge[i].u; 77 int v = edge[i].v; 78 edge[i].u = circle_id[u]; 79 edge[i].v = circle_id[v]; 80 // ## 如果這條最小邊不在環(huán)內(nèi)(即這里判斷的意義),因為之前加了最小邊的值,那就得減去其值。 81 //那么減掉之后就可能變成了零或比之前短 ;其實這里不用判斷條件的 如果是在環(huán)內(nèi) 82 //其因為兩端的結(jié)點相同對后來沒有了影響,這是剩下最后一種情況了,就是一結(jié)點在環(huán)內(nèi)(能縮點的條件) 83 if(edge[i].v != edge[i].u) 84 edge[i].cost -= in[u]; 85 } 86 nv = credit - 1; 87 root = circle_id[root]; 88 } 89 return true; 90 } 91 92 93 int main() 94 { 95 while(scanf("%d%d", &nv, &ne) != EOF) 96 { 97 for(int i = 1; i <= nv; ++i) 98 scanf("%lf%lf", &x[i], &y[i]); 99 for(int i = 0; i < ne; ++i) 100 {//這里就開始處理掉自環(huán)的情況,把自環(huán)的距離設(shè)置得比預(yù)定的最大值還大 101 scanf("%d%d", &edge[i].v, &edge[i].u); 102 edge[i].cost = edge[i].v == edge[i].u ? inf+MAXN : dis(edge[i].v, edge[i].u); 103 } 104 double res = 0; 105 if(Traverse(res)) printf("%.2f\n", res); //這里用f好像跟提交的方式有關(guān),FAQ里有解釋 106 else printf("poor snoopy\n"); 107 } 108 return 0; 109 }?
轉(zhuǎn)載于:https://www.cnblogs.com/liaoguifa/p/3217274.html
總結(jié)
以上是生活随笔為你收集整理的POJ 3164 Command Network (最小树形图)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常见HTTP状态(304,200等)
- 下一篇: poj 3295 Tautology