POJ 3164 Command Network (最小樹形圖-朱劉演算法),poj3164
題目地址:POJ 3164
最小樹形圖第一發。
把一個v寫成u了。。。。。TLE了一晚上。。。(雖說今晚出去玩了。。)
剛開始看這個演算法的時看模板以為又是一個isap。。。。嚇得一個哆嗦。但是仔細看了看之後發現還是挺好理解的。寫下自己的理解。
朱劉演算法其實只有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]在樹中就是多餘的,完全可以刪除,所以需要減去,然後再後面的總費用累加中會體現出刪掉了這個權值,不理解的畫個圖就明白了。
至於實現方法還是看代碼吧。看不懂的可以留言提問。
代碼如下:
#include <iostream>#include <string.h>#include <math.h>#include <queue>#include <algorithm>#include <stdlib.h>#include <map>#include <set>#include <stdio.h>using namespace std;#define LL __int64#define pi acos(-1.0)//#pragma comment(linker, "/STACK:1024000000")const int mod=1e9+7;const int INF=0x3f3f3f3f;const double eqs=1e-9;const int MAXN=40000+10;int cnt, n;int color[110], id[110], pre[110];double in[200];struct Point{ double x, y;}fei[200];struct node{ int u, v; double w;}edge[11000];void add(int u, int v, double w){ edge[cnt].u=u; edge[cnt].v=v; edge[cnt++].w=w;}double dist(Point f1, Point f2){ return sqrt((f1.x-f2.x)*(f1.x-f2.x)*1.0+(f1.y-f2.y)*(f1.y-f2.y));}double dmst(int root){ double ans=0; int i, j, u, v, NV=n; while(1){ for(i=1;i<=NV;i++) in[i]=INF;//找最小入邊 for(i=0;i<cnt;i++){ u=edge[i].u; v=edge[i].v; if(v!=u&&edge[i].w<in[v]){//更新最下入邊,並設為前驅節點 in[v]=edge[i].w; pre[v]=u; } } for(i=1;i<=NV;i++){ if(i!=root&&in[i]==INF) return -1;//有孤立點,不可能產生樹 } memset(color,-1,sizeof(color)); memset(id,-1,sizeof(id)); in[root]=0; int tot=0; for(i=1;i<=NV;i++){ ans+=in[i]; u=i; while(color[u]!=i&&id[u]==-1&&u!=root){//找環 color[u]=i; u=pre[u]; } if(id[u]==-1&&u!=root){//驚現一隻環!快活捉他。。 id[u]=++tot;//對發現的環進行編號,相當於縮點後的那個點。 for(v=pre[u];v!=u;v=pre[v]){ id[v]=tot; } } } if(!tot) break;//沒找到環,說明大功告成。那就收拾東西走人吧。。 for(i=1;i<=NV;i++){ if(id[i]==-1) id[i]=++tot;//對所有點進行編號,一個都不能跑。。 } for(i=0;i<cnt;i++){//更新邊權,構造新圖 v=edge[i].v; edge[i].u=id[edge[i].u]; edge[i].v=id[edge[i].v]; if(edge[i].u!=edge[i].v){ edge[i].w-=in[v]; } } NV=tot; root=id[root];//不要忘記更新點數和樹根的編號。。 } return ans;}int main(){ int m, i, j, u, v; double ans; while(~scanf("%d%d",&n,&m)){ for(i=1;i<=n;i++){ scanf("%lf%lf",&fei[i].x,&fei[i].y); } cnt=0; while(m--){ scanf("%d%d",&u,&v); if(u!=v) add(u,v,dist(fei[u],fei[v])); } ans=dmst(1); if(ans==-1) puts("poor snoopy"); else printf("%.2f\n",ans); } return 0;}