题目:http://poj.org/problem?id=1639
题目大意:
所有的车要去同一地点,而此地点对车子有一个最大容量K,所以车子在行驶的过程中,可以相互合并,求所有车子到达目的地的距离和的最小值;
思路:
我们可以现将出目的地以外的车子建立最小生成树,然后从目的地向各子树连接最短边形成一棵树;
然后向子树中连接一条边,这样必定会形成一个环,那么就要把这个环中最大的那个边去掉,直到k=0或者可变化值小于等于零
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
char s[101][101],s1[109],s2[109];
int n,k,g[101][101],d[101],w,num,kk,pre[109],sum=0;
bool flag[101],link[101][101];
struct node
{
int u,v,maxn;//保存从park到当前点路径中边权值最大的边
} mm[109];
void prim(int t)
{
memset(pre,-1,sizeof pre);
for(int i=1; i<num; i++)
d[i]=g[t][i],pre[i]=t;
while(1)
{
int v=-1;
for(int i=1; i<num; i++)
if(!flag[i]&&d[i]!=-1&&(v==-1||d[i]<d[v])) v=i;//d[i]不可为-1,且此点未加入到树中
if(v==-1) break;
if(g[0][v]!=-1&&(g[0][kk]==-1||g[0][kk]>g[0][v])) kk=v;//找寻此子树中到park的最短边
sum+=d[v];
link[pre[v]][v]=link[v][pre[v]]=true;//记录前驱结点,是为了以后的标记方便
flag[v]=true;
for(int i=1; i<num; i++)
if(!flag[i]&&g[i][v]!=-1&&(d[i]==-1||d[i]>g[v][i])) pre[i]=v,d[i]=g[v][i];
}
}
void dfs(int t,int pt,int u,int v)//t为当前查询节点,pt为上一次的查询的节点,u,v为最大边权的两个端点
{
for(int i=1; i<num; i++)
if(link[t][i]&&pt!=i)//首先必须得要有连接,其次不可为i,是为了保证查询的一直向下一层进行
{
if(pt==-1||g[t][i]>g[u][v])
{
mm[i].u=t,mm[i].v=i,mm[i].maxn=g[t][i];
dfs(i,t,t,i);
}
else
{
mm[i].u=u,mm[i].v=v,mm[i].maxn=g[u][v];
dfs(i,t,u,v);
}
}
}
int main()
{
scanf("%d",&n);
strcpy(s[0],"Park");
int j,ii,jj;
memset(g,-1,sizeof g);
num=1;
for(int i=0; i<n; i++)
{
scanf("%s%s%d",s1,s2,&w);
for( j=0; j<num; j++) if(strcmp(s[j],s1)==0) break;//由于字符串建图不方便,将其对应成数字
ii=j;
if(j==num) strcpy(s[num++],s1);
for(j=0; j<num; j++) if(strcmp(s[j],s2)==0) break;
jj=j;
if(j==num) strcpy(s[num++],s2);
if(g[ii][jj]==-1||w<g[ii][jj])//注意保存同一边的最小边权值
g[ii][jj]=g[jj][ii]=w;
}
scanf("%d",&k);
memset(flag,false,sizeof flag);
memset(link,false,sizeof link);
sum=0;
for(int i=1; i<num; i++)//将除了park以外的点进行生成最小生成树
if(!flag[i])
{
k--;//生成一棵将会与park 有一个边相连接
flag[i]=true;
kk=i;
prim(i);
sum+=g[0][kk];//加上从此子树可连接的最小边
link[0][kk]=link[kk][0]=true;
dfs(kk,-1,-1,-1);//更新此子树中到kk的最大边权值
}
while(k--)//从子树中向park连接边
{
int tmp=0,index;
for(int i=1; i<num; i++)
{
if(g[0][i]==-1||link[0][i]) continue;
if(tmp<mm[i].maxn-g[0][i])//要使可减小的值最大
tmp=mm[i].maxn-g[0][i],index=i;
}
if(tmp<=0) continue;
sum-=tmp;
link[mm[index].v][mm[index].u]=link[mm[index].u][mm[index].v]=false;//拆边
link[0][index]=link[index][0]=true;//连边
dfs(index,0,-1,-1);//更新路径中的最大值
}
printf("Total miles driven: %d\n",sum);
return 0;
}