POJ1639顶点度限制最小生成树
题目:http://poj.org/problem?id=1639
见汪汀的《最小生成树问题的拓展》。
大体是先忽略与根节点相连的边,做一遍kruscal,得到几个连通块和一个根节点;
然后根节点和每个连通块连一条边,当然是尽量小的,这时连的边就是根的最少度;
然后用增广的思路多给根连边,具体证明见论文;当最优的交换值也是负的时候break。
每次都要从根开始重弄dp值是因为要赋一遍初值。
#include<iostream> #include<cstdio> #include<cstring> #include<string>// #include<algorithm> #include<map> using namespace std; typedef long long ll; const int N=250; const ll INF=0x7fffffff; int n,m,K,s,fa[N],xnt,tmp[N],rd,rnt; bool b[N][N]; ll f[N][N],d[N],ans; map<string,int> mp; struct Bh{ int x,y; Bh(int a=0,int b=0):x(a),y(b) {} }bh[N],r[N]; bool cmp(int a,int b){return f[s][a]<f[s][b];} bool cmp2(Bh a,Bh b){return f[a.x][a.y]<f[b.x][b.y];} int find(int a) { if(fa[a]==a)return a; return fa[a]=find(fa[a]); } void dfs(int cur,int fa) { // printf("(%d)",cur); for(int i=1;i<=n;i++) if(b[cur][i]&&i!=fa) { if(d[i]<d[cur])d[i]=d[cur],bh[i]=bh[cur]; if(d[i]<f[cur][i])d[i]=f[cur][i],bh[i]=Bh(cur,i); dfs(i,cur); } } int main() { scanf("%d",&m); memset(f,1,sizeof f); string x,y;ll z; for(int i=1;i<=m;i++) { cin>>x>>y>>z;// if(!mp[x])mp[x]=++n;if(!mp[y])mp[y]=++n; f[mp[x]][mp[y]]=f[mp[y]][mp[x]]=min(f[mp[x]][mp[y]],z); } s=mp["Park"]; scanf("%d",&K); for(int i=1;i<=n;i++) { fa[i]=i; for(int j=i+1;j<=n;j++) if(f[i][j]<INF)r[++rnt]=Bh(i,j); } sort(r+1,r+rnt+1,cmp2); // for(int u=1,i,j;u<=rnt;u++) if(find(i=r[u].x)!=find(j=r[u].y)&&i!=s&&j!=s) { fa[find(i)]=find(j); b[i][j]=b[j][i]=1; } for(int i=1;i<=n;i++) if(f[s][i]<INF&&i!=s)tmp[++xnt]=i; sort(tmp+1,tmp+n,cmp); for(int i=1,v;i<=n;i++) if(find(v=tmp[i])!=s) { fa[find(v)]=s; b[v][s]=b[s][v]=1; rd++; } memset(d,-2,sizeof d); dfs(s,0); for(;rd<=K;rd++) { ll mx=-INF;int u=0; for(int i=1,v;i<=xnt;i++) if(!b[v=tmp[i]][s]&&f[bh[v].x][bh[v].y]-f[s][v]>mx) mx=f[bh[v].x][bh[v].y]-f[s][v],u=v; if(mx<=0)break;// b[s][u]=b[u][s]=1; b[bh[u].x][bh[u].y]=b[bh[u].y][bh[u].x]=0; memset(d,-2,sizeof d);// dfs(s,0); } for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) ans+=b[i][j]?f[i][j]:0; printf("Total miles driven: %lld",ans); return 0; }