poj 1639 Picnic Planning 最小k度生成树

题目:点击打开链接

题意:

就是求最小限度生成树,不会做,我是参考这篇http://www.cnblogs.com/jackge/archive/2013/05/12/3073669.html博客做的

分析:

要求最小 k 度生成树,我们可以按照下面的步骤来做:

设有度限制的点为 V0 ,V0称为根节点

1,把所有与 V0 相连的边删去,图会分成多个子图(假设为 m 个,显然的,如果 m > k,那么问题无解),让他们分别求最小生成树;然后用最小的代价将 m 个最小生成树和 V0 连起来,那我们就得到了一棵关于 V0 的最小 m 度生成树。

2,在 m 度生成树中找一个点和 V0 相连(设这条边的权值为 a),会生成一个环,为了满足最小生成树的要求,我们必须删掉一条边(设这条边的权值为 b),以使总权值尽量小,那么就要求 a 尽量的小,b 尽量的大。

完成一次 2 的操作后得到的是 m+1 度最小生成树,以此类推,直到得到最小 k 度生成树。


#include <iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=30;
struct node
{
    int v,cap;
    node(){}
    node(int v,int cap):v(v),cap(cap){}
    bool operator <(const node &a)const{
        return a.cap<cap;
    }
};
map<string,int>mp;
int g[N][N],dis[N],col[N],pre[N],fst[N],maxside[N];
int n,m,k;
int Prim(int src,int id)
{
    priority_queue<node>q;
    while(!q.empty())q.pop();
    dis[src]=0;
    q.push(node(src,0));
    int ans=0;
    while(!q.empty()){
        node cur=q.top();
        q.pop();
        int u=cur.v;
        if(!col[u]){
            col[u]=id;
            ans+=dis[u];
            for(int i=1;i<n;i++){
                if(!col[i]&&g[u][i]!=0&&dis[i]>g[u][i]){
                    pre[i]=u;
                    dis[i]=g[u][i];
                    q.push(node(i,dis[i]));
                }
            }
        }
    }
    return ans;

}
void update(int cur,int last,int max_side)
{
    maxside[cur]=max_side>g[cur][last]?max_side:g[cur][last];
    for(int i=1;i<n;i++)
        if(i!=last&&g[cur][i]!=0&&(pre[cur]==i||pre[i]==cur))
        update(i,cur,maxside[cur]);
}
void test2()
{
    cout<<endl;
    for(int i=0;i<n;i++)cout<<maxside[i]<<' ';
    cout<<endl;
}
void solve()
{
    for(int i=0;i<n;i++){
        dis[i]=INF;
        col[i]=pre[i]=fst[i]=0;
    }
    int res=0,cnt=1;
    for(int i=1;i<n;i++)
        if(!col[i])res+=Prim(i,cnt++);
   // update(2,0,0);
    //test2();
    for(int i=1;i<n;i++){
        int id=col[i];
        if(g[0][i]!=0&&(!fst[id]||g[0][i]<g[0][fst[id]]))fst[id]=i;
    }
    //cout<<fst[1]<<endl;
    for(int i=1;i<cnt;i++){
        res+=g[0][fst[i]];
        g[0][fst[i]]=g[fst[i]][0]=0;
        update(fst[i],0,0);
    }
  //  test2();
    k=k-cnt+1;
    while(k--){
        int tmp=0;
        for(int i=1;i<n;i++){
            if(g[0][i]!=0&&(tmp==0||maxside[tmp]-g[0][tmp]<maxside[i]-g[0][i]))
                tmp=i;
        }
        if(maxside[tmp]<=g[0][tmp])break;
        res=res-maxside[tmp]+g[0][tmp];
        g[0][tmp]=g[tmp][0]=0;
        int p=0;
        for(int i=tmp;pre[i]!=0;i=pre[i]){
            if(p==0||g[p][pre[p]]<g[i][pre[i]])p=i;
        }
        pre[p]=0;
        update(tmp,0,0);
      //  test2();
    }
    printf("Total miles driven: %d\n",res);
}
int main()
{
   // freopen("f.txt","r",stdin);
    char s1[20],s2[20];
    int cap;
    while(~scanf("%d",&m)){
        mp["Park"]=0;
        memset(g,0,sizeof(g));
        n=1;
        while(m--){
            scanf("%s%s%d",s1,s2,&cap);
            if(!mp.count(s1))mp[s1]=n++;
            if(!mp.count(s2))mp[s2]=n++;
            int u=mp[s1],v=mp[s2];
            if(!g[u][v]||g[u][v]>cap)g[u][v]=g[v][u]=cap;
        }
        scanf("%d",&k);
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值