算法流程:
1.将该点(以下用v0表示)从图中删除,将得到m个连通分量。
2.对每个连通分量求最小生成树,假设m个。
3.从每个连通分量中找与v0关联的权值最小的边,与v0相连接,这样将得到v0的最小m度生成树
4.如果 k < m 那么这种树是不存在的。
5.如果 k >=m ,那么考虑构建 m+1度 最小生成树 ,将与v0关联的且不在当前的树中的边
6.如果将其加入树中 ,必然会存在一个环,那么删掉该环中与v0不关联的权值最大边,将得到加入该边后的最小生成树,且是m+1的。
7.枚举上述6的边找树权值最小,那么即是m+1度限制的最小生成树。
8.重复5.6.7,直到k 度最小生成树出现。
CODE:
/*K限度最小生成树*/
/*
(1):求某一点限度<=K的最小生成树
*/
/*AC代码:0ms*/
#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#include <map>
#include <string>
#include <queue>
#define MAXN 100
#define INF 1e8
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;
struct edge
{
int u,v,w,next;
bool flag;
}E[200000],E1[20000],E2[20000];
//E[]:存整体的K限度生成树
//E1[]:存原始的输入边
//E2[]:存与vo相关联的边
int head[MAXN],ecnt,vcnt;;
int N,K,cnt,vo,now;
map<string,int>hash;
int p[MAXN];
int pre[MAXN];
bool vis[MAXN];
bool ok;
void Insert(int u,int v,int w)
{
E[ecnt].u=u;
E[ecnt].v=v;
E[ecnt].w=w;
E[ecnt].flag=true;
E[ecnt].next=head[u];
head[u]=ecnt++;
}
int cmp(const void *p1,const void *p2)
{
return ((struct edge *)p1)->w-((struct edge *)p2)->w;
}
int find_set(int x)
{
if(x!=p[x])
p[x]=find_set(p[x]);
return p[x];
}
void Init()
{
int i,u,v,w;
string a,b;
hash.clear();
cnt=0;
for(i=1;i<=N;i++)
{
cin>>a>>b>>w;
if(!hash[a]) hash[a]=++cnt;
if(!hash[b]) hash[b]=++cnt;
u=hash[a];v=hash[b];
E1[i].u=u;E1[i].v=v;
E1[i].w=w;
}
scanf("%d",&K);
string c="Park";
vo=hash[c];
qsort(E1+1,N,sizeof(E1[1]),cmp);
/*
printf("&&&%d\n",vo);
for(i=1;i<=N;i++)
printf("%d %d %d\n",E1[i].u,E1[i].v,E1[i].w);
printf("\n");
*/
}
void swap(int &a,int &b)
{
int t;
if(b==vo)
{t=a;a=b;b=t;}
}
void Run()
{
int i,u,v,w;
now=0;
for(i=0;i<vcnt;i++)
{
u=E2[i].u;
v=E2[i].v;
w=E2[i].w;
int du=find_set(u);
int dv=find_set(v);
if(du!=dv)
{
E2[i].flag=true;
now++;
p[du]=dv;
Insert(u,v,w);
Insert(v,u,w);
}
}
}
queue<int>Q;
int bfs(int s,int e,int ith,bool ok)
{
int i,u,v,w;
while(!Q.empty()) Q.pop();
memset(vis,false,sizeof(vis));
memset(pre,-1,sizeof(pre));
Q.push(s);
vis[s]=true;
while(!Q.empty())
{
//printf("&\n");
u=Q.front();Q.pop();
for(i=head[u];i!=-1;i=E[i].next)
{
if(!E[i].flag) continue;//已经被标记掉的
v=E[i].v;
if(!vis[v])
{
pre[v]=i;
if(v==e) break;
vis[v]=true;
Q.push(v);
}
}
}
int id,Index,res=0;
u=e;
while(true)
{
//printf("##\n");
id=pre[u];
//删掉环中与vo不关联的权值最大的边
if((E[id].u!=vo&&E[id].v!=vo)&&E[id].w>res)
{
res=E[id].w;
Index=id;
}
u=E[id].u;
if(u==s) break;
}
if(ok)
{
E[Index].flag=E[Index^1].flag=false;
Insert(s,e,E2[ith].w);
Insert(e,s,E2[ith].w);
E2[ith].flag=true;//标记为树边
}
return E2[ith].w-res;
}
void ff()
{
int i,u,v,w,temp;
int id=-1,res=INF;
//枚举那些与vo关联但不在当前生成树内的边
for(i=0;i<vcnt;i++)
{
//printf("**\n");
if(!E2[i].flag)
{
temp=bfs(vo,E2[i].v,i,false);
if(temp<res)
{
id=i;
res=temp;
}
}
}
if(res>=0)
{ok=false;return;}
bfs(vo,E2[id].v,id,true);
}
int get_ans()
{
int res=0,i;
for(i=0;i<ecnt;i+=2)
{
if(E[i].flag)
res+=E[i].w;
}
return res;
}
void Solve()
{
int i,u,v,w;
memset(head,-1,sizeof(head));ecnt=0;
for(i=1;i<=cnt;i++)
p[i]=i;
vcnt=0;
for(i=1;i<=N;i++)
{
u=E1[i].u;
v=E1[i].v;
swap(u,v);
w=E1[i].w;
if(u==vo||v==vo)
{
E2[vcnt].u=u;
E2[vcnt].v=v;
E2[vcnt].flag=false;
E2[vcnt].w=w;
vcnt++;
continue;
}
int du=find_set(u);
int dv=find_set(v);
if(du!=dv)
{
p[du]=dv;
Insert(u,v,w);
Insert(v,u,w);
}
}
Run();
/*
printf("*\n");
for(i=0;i<vcnt;i++)
printf("%d %d %d\n",E2[i].u,E2[i].v,E2[i].w);
printf("\n");
printf("^%d\n",now);
*/
//扩展
ok=true;
for(i=now;i<K&&ok;i++)
ff();
int ans=get_ans();
printf("Total miles driven: %d\n",ans);
}
int main()
{
//freopen("A.21.dat","r",stdin);
//freopen("1639.txt","w",stdout);
while(scanf("%d",&N)!=EOF)
{
Init();
Solve();
}
return 0;
}
/*
7
Jonesy Park 3
J Jonesy 1
Jonesy James 1
Jonesy Jimmy 1
J Park 10
Park James 10
Jimmy Park 10
4
Total miles driven: 6
*/