Picnic Planning poj 1639 k度限制生成树

来源:互联网 发布:蓝桥杯c语言试题及答案 编辑:程序博客网 时间:2024/05/16 01:20

传送门:poj1639

题意就很晦涩,看了半天才看懂。。我就不解释了,没看懂的再去看看吧。。

重点的重点是本题用到的算法:K度限制最小生成树

即求一个图的最小生成树,且给定点v的度数必须为k。

这也是我第一次接触这个名词,百度了一些资料罗列在下面:

国家集训队2004论文集_汪汀:http://wenku.baidu.com/view/8abefb175f0e7cd1842536aa.html

大牛们的博客:http://www.cnblogs.com/Missa/archive/2013/04/07/3005880.html

http://www.cnblogs.com/jackge/archive/2013/05/12/3073669.html

http://blog.csdn.net/qq_33951440/article/details/53115853

这个算法给我的感觉就是特别容易懂思路,但是并不好实现,尤其是其中的dfs更是神来之笔(弱搞了好久才明白)。。。

#include <iostream>#include <cstdio>#include <cstring>#include <map>#include <queue>#include <algorithm>#define inf 0x3f3f3f3fusing namespace std;int k,n,cnt;int g[25][25];int col[25];//记录每颗子树的序号 int dis[25];//prim中距离数组 int pre[25];//记录每个点的前驱 int max_side[25];//记录每个点到主根节点的路径中的最长边int min_side[25];//记录每颗子树中到主根节点的最短边对应的点 struct node{int v,w;bool friend operator<(node a,node b){return a.w>b.w;}};int prim(int t,int i){priority_queue<node> q;while(!q.empty())q.pop();q.push((node){t,0});dis[t]=0;int sum=0;while(!q.empty()){node a=q.top();q.pop();if(!col[a.v]){col[a.v]=i;sum+=dis[a.v];for(int i=1;i<cnt;i++){if(!col[i]&&g[a.v][i]&&g[a.v][i]<dis[i]){pre[i]=a.v;dis[i]=g[a.v][i];q.push((node){i,g[a.v][i]});}}} }return sum;}int dfs(int cur,int fa,int maxw)//求每个点到主根的路径中的最长边 {max_side[cur]=max(maxw,g[cur][fa]);for(int i=1;i<cnt;i++)if(g[cur][i]&&i!=fa&&(pre[i]==cur||pre[cur]==i)) dfs(i,cur,max_side[cur]);}void solve(){for(int i=0;i<cnt;i++){dis[i]=inf;pre[i]=col[i]=min_side[i]=0; }int ans=0,t=1;for(int i=1;i<cnt;i++){if(!col[i])ans+=prim(i,t++);}for(int i=1;i<cnt;i++){int j=col[i];if(g[0][i]&&(!min_side[j]||g[0][i]<g[0][min_side[j]]))min_side[j]=i; }for(int i=1;i<t;i++){ans+=g[0][min_side[i]];g[0][min_side[i]]=g[min_side[i]][0]=0;//删除这条边(以后不再允许使用) dfs(min_side[i],0,0);}k=k-t+1;while(k--){int tmp=0;for(int i=1;i<cnt;i++){if(g[0][i]&&(!tmp||max_side[i]-g[0][i]>max_side[tmp]-g[0][tmp]))tmp=i;}if(max_side[tmp]-g[0][tmp]<=0)break;ans-=(max_side[tmp]-g[0][tmp]);g[0][tmp]=g[tmp][0]=0;int temp=0;for(int i=tmp;pre[i]!=0;i=pre[i])//找到形成的环内最长的边对应的顶点 {if(g[i][pre[i]]&&(!temp||g[temp][pre[temp]]<g[i][pre[i]]))temp=i;}pre[temp]=0;//将最长边的父节点改为0,个人认为是要删除这条边,但是又不能直接将g数组中的值归零 dfs(tmp,0,0);}printf("Total miles driven: %d\n",ans); }int main(){int w;char a[15],b[15];map<string,int>mp;while(~scanf("%d",&n)){mp.clear();memset(g,0,sizeof(g)); mp["Park"]=0;cnt=1;while(n--){scanf("%s%s%d",a,b,&w);if(!mp.count(a))mp[a]=cnt++;if(!mp.count(b))mp[b]=cnt++;int i=mp[a],j=mp[b];if(!g[i][j]||g[i][j]>w)g[i][j]=g[j][i]=w;}scanf("%d",&k);solve();}return 0;}


0 0