藍書(演算法競賽進階指南)刷題記錄——POJ1639 Picnic Planning(度限制最小生成樹)
阿新 • • 發佈:2018-11-10
題目:POJ1639.
題目大意:給定一張無向圖,求這張無向圖的最小生成樹,其中這棵最小生成樹滿足節點1的度小於等於s.
我們對於一張圖,先將點1去掉,剩下的聯通塊內的的最小生成樹都求出,然後我們在列舉與1關聯的邊,將所有聯通塊與1只連一條最小的邊,這樣我們就求出了一棵最小T度生成樹,其中T等於去掉點1後聯通塊數量.
我們求出最小T度生成樹之後,我們再求最小S度生成樹.注意,我們下面所說的最小k度指的是點1的度必須為k.
我們考慮最小k度生成樹如何轉移到最小k+1度生成樹.我們考慮列舉一條與1相連但沒有被選入最小生成樹的邊(1,x),然後我們會發現出現了一個環,其中這個環由一條非樹邊(1,x)和一條樹鏈(x,1)組成.
我們考慮暴力列舉樹鏈上的每一條,找到邊權最大的邊,看是否最優即可.
但是這樣做的時間複雜度最壞情況下為,我們考慮更加優秀的做法.
我們發現這個演算法的瓶頸在於列舉到一個點時需要在列舉至多級別條邊,那麼我們是否可以考慮優化這個過程.
我們考慮每一次轉移都先進行一個樹形DP,這個DP的狀態f[x]表示樹上的鏈(1,x)上的最大邊權,並且記錄這條邊的編號,方便刪除.
那麼時間複雜度為.
由於時限寬鬆,這裡寫了鄰接矩陣寫法的dfs,若要達到上述那個更優的時間複雜度,需要寫一個雙向連結串列形式的鄰接表.
程式碼如下:
#include<iostream> #include<cstdio> #include<map> #include<algorithm> using namespace std; #define Abigail inline void #define mt map<string,int>::iterator typedef long long LL; const int N=300,INF=(1<<29)-1; map<string,int>q; struct graph_side{ int x,y,v; bool operator < (const graph_side p)const{return v<p.v;} }e[N+9]; int m,n,s,ans; int fa[N+9]; int tr[N+9][N+9],g[N+9][N+9]; struct node{ int x,y,v; }dp[N+9]; int get(int u){return u^fa[u]?fa[u]=get(fa[u]):u;} int kruskal(){ int cnt=0; for (int i=1;i<=n;i++) fa[i]=i; sort(e+1,e+1+m); for (int i=1;i<=m;i++){ if (e[i].x==1||e[i].y==1) continue; int grx=get(e[i].x),gry=get(e[i].y); if (grx==gry) continue; fa[grx]=gry; tr[e[i].x][e[i].y]=tr[e[i].y][e[i].x]=1; ans+=e[i].v; } for (int i=1;i<=m;i++){ if (e[i].x^1&&e[i].y^1) continue; int grx=get(e[i].x),gry=get(e[i].y); if (grx==gry) continue; cnt++; fa[grx]=gry; tr[e[i].x][e[i].y]=tr[e[i].y][e[i].x]=1; ans+=e[i].v; } return cnt; } void dfs(int k,int fa){ for (int i=2;i<=n;i++) if (i^fa&&tr[k][i]){ dp[i].v=g[k][i];dp[i].x=k;dp[i].y=i; if (dp[i].v<dp[k].v) dp[i]=dp[k]; dfs(i,k); } } Abigail into(){ string s1,s2; mt it; int v; for (int i=1;i<=N;i++) for (int j=1;j<=N;j++) g[i][j]=INF; scanf("%d",&m); q.insert(make_pair("Park",n=1)); for (int i=1;i<=m;i++){ cin>>s1>>s2; it=q.find(s1); if (it==q.end()){ e[i].x=++n; q.insert(make_pair(s1,n)); }else e[i].x=q[s1]; it=q.find(s2); if (it==q.end()){ e[i].y=++n; q.insert(make_pair(s2,n)); }else e[i].y=q[s2]; scanf("%d",&e[i].v); g[e[i].x][e[i].y]=g[e[i].y][e[i].x]=min(e[i].v,g[e[i].x][e[i].y]); } scanf("%d",&s); } Abigail work(){ int cnt=kruskal(); for (int i=cnt+1;i<=s;i++){ dfs(1,0); int minn=INF,v=0; for (int j=2;j<=n;j++) if (!tr[1][j]&&minn>g[1][j]-dp[j].v) minn=g[1][j]-dp[j].v,v=j; if (minn>=0||!v) break; ans+=minn; tr[1][v]=tr[v][1]=1; tr[dp[v].x][dp[v].y]=tr[dp[v].y][dp[v].x]=0; } } Abigail outo(){ printf("Total miles driven: %d\n",ans); } int main(){ int T=1,_=0; while (T--){ into(); work(); outo(); } return 0>_<0; }