poj3164(最小树形图朱刘算法模板)
題目鏈接:http://poj.org/problem?id=3164
?
題意:第一行為n, m,接下來n行為n個點的二維坐標, 再接下來m行每行輸入兩個數u, v,表點u到點v是單向可達的,求這個有向圖的最小生成樹即求最小樹形圖;
?
思路: 這是一道最小樹形圖模板題;
我們可以用朱劉算法來解:
朱劉算法只有3步,然后不斷循環。
1:找到每個點的最小入邊。既然是生成樹,那么對于每個點來說,只要選一個權值最小的入邊就可以了。
貪心思想。因為如果不是最小入邊,那么它肯定不是最小樹形圖的一條邊,考慮它是沒有意義的。
2:找環。找環找的是最小入邊構成的新圖的環。如果沒找到環,那么一棵樹就已經形成了,
因為樹就是沒有環的圖。再因為邊權都是最小的,因此此時最小樹形圖就已經出來了,停止循環。
?
3:如果第2步中找到了環,那么這個環就可以縮成一個點。然后構造新圖,更新邊權。更新邊權的方法是:
假設某點u在該環上,并設這個環中指向u的邊權是in[u],那么對于每條從u出發的邊(u, i, w),在新圖中連接(new, i, w)的邊,其中new為新加的人工頂點;對于每條進入u的邊(i, u, w),在新圖中建立邊(i, new, w-in[u])的邊。之所以是w-in[u]的原因是如果選擇了w,那么那個in[u]在樹中就是多余的,完全可以刪除,所以需要減去,然后再后面的總費用累加中會體現出刪掉了這個權值,不理解的畫個圖就明白了。
?
代碼:
1 #include <iostream> 2 #include <stdio.h> 3 #include <math.h> 4 #include <string.h> 5 #include <algorithm> 6 #include <iomanip> 7 #define MAXN 210 8 using namespace std; 9 10 const int inf=0x3f3f3f3f; 11 struct node{ 12 int u, v; 13 double w; //**u到v的單向距離 14 }edge[MAXN*100]; 15 16 struct Point{ //**記錄每個點的坐標 17 double x, y; 18 }point[MAXN]; 19 20 double in_dist[MAXN]; //記錄i點的最短入邊距離 21 int pre[MAXN]; //**記錄i節點的前繼節點 22 int id[MAXN]; //**記錄新圖點節點 23 int vis[MAXN]; //**判環 24 int k=1; //k為非自環邊的數目 25 26 double dist(Point a, Point b){ 27 return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); 28 } 29 30 void add(int u, int v, double dist){ //***加邊 31 edge[k].u=u; 32 edge[k].v=v; 33 edge[k++].w=dist; 34 } 35 36 double zhuliu(int root, int n){ 37 double ans=0; 38 while(true){ 39 for(int i=1; i<=n; i++){ 40 in_dist[i]=inf; 41 } 42 for(int i=1; i<k; i++){ //***找點i的最小入邊 43 int u=edge[i].u; 44 int v=edge[i].v; 45 double w=edge[i].w; 46 if(u!=v&&in_dist[v]>w){//**注意這里新圖也可能出現自環的情況,若不加u!=v的話會tle 47 in_dist[v]=w; 48 pre[v]=u; //**記錄v的前繼節點 49 } 50 } 51 for(int i=1; i<=n; i++){ 52 if(i==root){ 53 continue; 54 } 55 ans+=in_dist[i]; 56 if(in_dist[i]>=inf){ 57 return -1; //***根節點外還有孤立點,不存在最小樹形圖 58 } 59 } 60 memset(id, -1, sizeof(id)); 61 memset(vis, -1, sizeof(vis)); 62 int cnt=0; 63 for(int i=1; i<=n; i++){//***枚舉每個點,找環 64 int v=i; 65 while(v!=root&&vis[v]!=i&&id[v]==-1){//**上溯父節點找環 66 vis[v]=i; 67 v=pre[v]; 68 } 69 if(v!=root&&id[v]==-1){ //***找到了環,縮點并且從新編號 70 ++cnt; 71 id[v]=cnt; 72 for(int u=pre[v]; u!=v; u=pre[u]){ 73 id[u]=cnt; 74 } 75 } 76 } 77 if(!cnt){ //***沒有出現環 78 break; 79 } 80 for(int i=1; i<=n; i++){//***將余下的不在環中的點也重新編號 81 if(id[i]==-1){ 82 id[i]=++cnt; 83 } 84 } 85 for(int i=1; i<k; i++){//***建新圖 86 int u=edge[i].u; 87 int v=edge[i].v; 88 edge[i].u=id[u]; 89 edge[i].v=id[v]; 90 if(edge[i].u!=edge[i].v){ 91 edge[i].w-=in_dist[v]; 92 } 93 } 94 n=cnt; //**更新節點數目 95 root=id[root]; //**更新根節點編號 96 } 97 return ans; 98 } 99 100 int main(void){ 101 // freopen("test.in", "r", stdin); 102 // freopen("test.out", "w", stdout); 103 int u, v, n, m; 104 while(~scanf("%d%d", &n, &m)){ 105 k=1; 106 for(int i=1; i<=n; i++){ 107 scanf("%lf%lf", &point[i].x, &point[i].y); 108 } 109 while(m--){ 110 scanf("%d%d", &u, &v); 111 if(u!=v){ //***刪除自環 112 add(u, v, dist(point[u], point[v])); 113 } 114 } 115 int root=1; //**本題中沒有指定根節點,任取一個即可 116 double ans=zhuliu(root, n); 117 if(ans==-1){ 118 puts("poor snoopy"); 119 }else{ 120 printf("%.2f\n", ans); //**poj輸出用lf會wa╭(╯^╰)╮ 121 } 122 } 123 return 0; 124 } View Code?
轉載于:https://www.cnblogs.com/geloutingyu/p/6537566.html
總結
以上是生活随笔為你收集整理的poj3164(最小树形图朱刘算法模板)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringData_JpaReposi
- 下一篇: js 原型prototype继承模式