poj1639:最小度限制生成树

题目: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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值