1. 程式人生 > >poj 1639 最小k度限制生成樹

poj 1639 最小k度限制生成樹

題目連結:https://vjudge.net/problem

題意:

 

參考部落格:最小k度限制生成樹 - chty - 部落格園  https://www.cnblogs.com/chty/p/5934669.html

程式碼:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include
<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long LL; #define eps 1e-8 #define INF 0xffffff #define maxn 105 map<string,int>mp; struct node{ int u,v,w,next; }edge[maxn*maxn],dp[maxn]; int n,m,k,t,id,cnt; int sum,md;//邊權和,最小的度
int pre[maxn],check[maxn][maxn],dis[maxn][maxn],minn[maxn],temp[maxn]; //集合的祖先,check[i][j]表示在生成樹裡面i和j是否有邊相連,dis[i][j]表示i和j之間的邊權 //minn[i]表示以點i為祖先節點的點集裡和樹根直接相連的最小邊權,temp記錄這個點的下標 void init(){ id=1; sum=md=0; memset(check,0,sizeof(check)); for(int i=1;i<maxn;i++){ for(int j=1;j<maxn;j++){ dis[i][j]
=INF; } } for(int i=1;i<maxn;i++) pre[i]=i; } void read(){ init(); cin>>m; string u,v; int w; mp["Park"]=1; for(int i=1;i<=m;i++){ cin>>u>>v>>w; if(mp[u]==0) mp[u]=++id; if(mp[v]==0) mp[v]=++id; edge[i].u=mp[u]; edge[i].v=mp[v]; edge[i].w=w; dis[mp[u]][mp[v]]=dis[mp[v]][mp[u]]=min(dis[mp[u]][mp[v]],w); } cin>>k; } bool cmp(node a,node b){ return a.w<b.w; } int find(int a){ return pre[a]==a?a:pre[a]=find(pre[a]); } void cal_m_tree(){//計算度為m的最小生成樹 sort(edge+1,edge+1+m,cmp); for(int i=1;i<=m;i++){ int u=edge[i].u; int v=edge[i].v; int w=edge[i].w; if(u==1||v==1) continue; int x=find(u); int y=find(v); if(x!=y){ pre[x]=y; sum+=w;//把邊權加上 check[u][v]=check[v][u]=1;//標記連結u,v的邊已經走過 } } for(int i=1;i<=id;i++) minn[i]=INF; for(int i=2;i<=id;i++){//尋找各個連通塊裡面和樹根直接相連的最小邊 int f=find(i); if(dis[i][1]!=INF&&minn[f]>dis[i][1]){//更新連通快f的值並記錄點的下標 minn[f]=dis[i][1];// temp[f]=i; } } for(int i=2;i<=id;i++){ if(minn[i]!=INF){ md++;//度增加 sum+=minn[i];//權值增加 check[1][temp[i]]=check[temp[i]][1]=1;//標記邊 } } } void DFS(int u,int fa){//用動態規劃來計算從樹根到生成樹上每一個點的1路徑上面的最大邊權 //同時記錄這條邊所連線的兩點 for(int i=2;i<=id;i++){ if(i==fa) continue; if(check[i][u]==1){//動態規劃,到點i的最大邊權dp[i].w=max{dp[u].w,dis[i][u]} if(dp[u].w<dis[i][u]&&u!=1){ dp[i].w=dis[i][u]; dp[i].u=u; dp[i].v=i; } else{ dp[i]=dp[u]; } DFS(i,u); } } } void cal_k_tree(){//計算度為m+1到k的最小生成樹,可以直接跳出 for(int i=md+1;i<=k;i++){ for(int j=1;j<=id;j++) dp[j].w=-INF; DFS(1,-1); int ans=INF,u;//ans記錄接下來度為i的生成樹和之前生成樹的權值之差的最小值,如果ans<0,說明還可以 //使邊權變小,否則在度數增加時邊權和只會變大,所以可以直接跳出 for(int j=2;j<=id;j++){ if(check[j][1]==0&&dis[1][j]!=INF){ if(ans>dis[1][j]-dp[j].w){ ans=dis[1][j]-dp[j].w; u=j; } } } if(ans>=0)//無法增廣,直接跳出 break; int a=dp[u].u; int b=dp[u].v; check[a][b]=check[b][a]=0;//去掉一條邊 check[1][u]=check[u][1]=1;//增加一條邊 sum+=ans;//邊權減少 } } int main() { read(); cal_m_tree(); cal_k_tree(); printf("Total miles driven: %d\n",sum); return 0; }