题意:
给一个无向图连通图,求它的最小生成树,生成树满足条件点v0的度小于等于limit。
分析:
一般有度限制的最小生成树问题是np完全的,但单点度限制就比较简单了,先在原图上求不含v0的最小生成森林,然后再将森林中每一棵树选一条最短的边连到v0上,设最小生成森林中有k0棵树,如果k0>limit自然无解,否则每次再选增量最小的边连到v0上,同时删除因为加边形成环上的最大边。
代码:
//poj 1639
//sepNINE
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
const int maxN=32;
const int maxM=1024;
map<string,int> name;
int e,parkE,park,n;
int vis[maxN];
int f[maxN],used[maxM],usedParkE[maxM];
int g[maxN][maxN],parent[maxN];
struct Edge
{
int u,v,w;
}edge[maxM],parkEdge[maxM];
int cmp(Edge a,Edge b)
{
return a.w<b.w;
}
int find(int u)
{
return f[u]==u?u:f[u]=find(f[u]);
}
void dfs(int u)
{
for(int i=1;i<=n;++i)
if(g[u][i]>0&&parent[i]==0){
parent[i]=u;
dfs(i);
}
}
void updateTree()
{
memset(parent,0,sizeof(parent));
parent[park]=-1;
dfs(park);
}
int findmax(int v)
{
int maxx=-1,x;
while(v!=park){
if(maxx<g[v][parent[v]]){
maxx=g[v][parent[v]];
x=v;
}
v=parent[v];
}
return x;
}
int main()
{
int i,m,limit;
n=0;
e=parkE=0;
park=-1;
cin>>m;
while(m--){
string a,b;
int w;
cin>>a>>b>>w;
if(name[a]==0) name[a]=++n;
if(name[b]==0) name[b]=++n;
if(a=="Park") park=name[a];
if(b=="Park") park=name[b];
int u=name[a],v=name[b];
if(park==u||park==v){
parkEdge[parkE].u=u;
parkEdge[parkE].v=v;
parkEdge[parkE].w=w;
++parkE;
}
else{
edge[e].u=u;edge[e].v=v;edge[e].w=w;
++e;
}
}
cin>>limit;
sort(edge,edge+e,cmp);
for(i=1;i<=n;++i) f[i]=i;
int sum=0;
memset(used,0,sizeof(used));
memset(g,0,sizeof(g));
for(i=0;i<e;++i){
int u=edge[i].u,v=edge[i].v;
int pa=find(u);
int pb=find(v);
if(pa==pb)
continue;
used[i]=1;
g[u][v]=g[v][u]=edge[i].w;
f[pa]=pb;
sum+=edge[i].w;
}
memset(vis,0,sizeof(vis));
sort(parkEdge,parkEdge+parkE,cmp);
memset(usedParkE,0,sizeof(usedParkE));
int cnt=0;
for(i=0;i<parkE;++i){
int u=parkEdge[i].u;
int v=parkEdge[i].v;
int w=parkEdge[i].w;
if(u!=park) swap(u,v);
if(vis[find(v)]==0){
++cnt;
vis[find(v)]=1;
usedParkE[i]=1;
sum+=w;
g[u][v]=g[v][u]=w;
}
}
updateTree();
while(cnt<limit){
int deta=0,minI;
for(i=0;i<parkE;++i){
if(usedParkE[i]==1)
continue;
int u=parkEdge[i].u,v=parkEdge[i].v,w=parkEdge[i].w;
if(u!=park) swap(u,v);
int changeP=findmax(v);
int pFather=parent[changeP];
if(pFather==park)
continue;
int t=w-g[changeP][pFather];
if(t<deta){
deta=t;
minI=i;
}
}
if(deta==0)
break;
else{
int u=parkEdge[minI].u;
int v=parkEdge[minI].v;
int w=parkEdge[minI].w;
if(u!=park) swap(u,v);
int changeP=findmax(v);
int pFather=parent[changeP];
g[changeP][pFather]=g[pFather][changeP]=0;
g[u][v]=g[v][u]=w;
updateTree();
usedParkE[minI]=1;
sum+=deta;
++cnt;
}
}
printf("Total miles driven: %d",sum);
return 0;
}