标签:mile net 动态规划 树根 ext park int 计算 直接
题目链接: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; }
标签:mile net 动态规划 树根 ext park int 计算 直接
原文地址:https://www.cnblogs.com/6262369sss/p/10066109.html