• poj 1639 Picnic Planning 最小度限制生成树 (模版)


    题解最小度限制生成树 ,详解减:

    http://wenku.baidu.com/view/70ef0e00eff9aef8941e06db.html   

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

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

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

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

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

    具体参见 汪汀2004年的国家集训队论文,讲的很详细。

    View Code
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<set>
    #include<map>
    #define Min(a,b)  a>b?b:a
    #define Max(a,b)  a>b?a:b
    #define CL(a,num)  memset(a,num,sizeof(a));
    #define inf 9999999
    #define maxn 30
    using namespace std;
    
    struct node
    {
        int u;
        int cost;
        node(int x,int y):u(x),cost(y){}
        friend bool operator < (node a,node b)
        {
            return a.cost>b.cost;
        }
    };
    map<string ,int >map1;
    
    priority_queue<node>que;
    int dis[maxn],pre[maxn],clo[maxn],near[maxn],mat[maxn][maxn];
    int max_side[maxn];
    int res,k,n;
    int h;
    int prim(int x,int id)//运用优先队列 求最小生成树
    {
    
    
        dis[x]=0;
        while(!que.empty())que.pop();
        que.push(node(x,0));
    
        int res=0,i;
        while(!que.empty())
        {
           node a=que.top();que.pop();
            int u=a.u;
            int cost=a.cost;
            if(!clo[u])
            {
                clo[u]=id;
                res+=dis[u];
                for(i=1;i<n;i++)
                {
    
                    if(!clo[i]&&mat[u][i]!=0&&mat[u][i]<dis[i])
                    {
                        pre[i]=u;
                        dis[i]=mat[u][i];
                        que.push(node(i,dis[i]));
    
                    }
                }
    
    
            }
    
    
    
    
        }
        return res;
    
    
    
    
    }
    void update(int cur,int last,int maxside)//  dfs 更新最大边
    
    {
        max_side[cur]=maxside>mat[cur][last]?maxside:mat[cur][last];
        for(int i=1;i<n;i++)
        {
            if(i!=last&&mat[cur][i]!=0&&(pre[cur]==i||pre[i]==cur))//pre[]将其他边 和生成树的边分离开
              update(i,cur,max_side[cur]);
        }
    }
    void solve()
    {
        int i;
    
        for(i=0;i<n;i++)
        {
          dis[i]=inf;
          clo[i]=pre[i]=near[i]=0;
        }
        int res=0;
        int cnt=1;
        clo[0]=1;
        h=0;
        for(i=1;i<n;i++)//将 0 点 叙其他点断开 求生成树
        {
            if(!clo[i])res+=prim(i,cnt++);
        }
        for(i=1;i<n;i++)//near[i],记录每个生成树距离 0 点,最近的点
        {
            int id=clo[i];
            if(mat[0][i]!=0&&(!near[id]||mat[0][near[id]]>mat[0][i]))
             near[id]=i;
        }
        
        for(i=1;i<cnt;i++)////把m个生成树上和根节点相连的边加入res,得到关于Park的最小m度生成树  
        {
            res+=mat[0][near[i]];
            mat[0][near[i]]=mat[near[i]][0]=0;
            update(near[i],0,0);
        }
        k=k-cnt+1;
       
      /* 
        添删操作:将根节点和生成树中一个点相连,会产生一个环,将这个环上(除刚添的那条边外)权值最大 
        的边删去.由于每次操作都会给总权值带来影响 d=max_side[tmp]-mat[0][tmp],我们需要得到最小生 
        成树,所以我们就要求 d 尽量大 
        */  
        while(k--)
        {
    
            int tmp=0;
            for(i=1;i<n;i++)//找 d最大的点 极 max_side[i]-mat[0][i],.最大
            {
                if(mat[0][i]!=0&&(tmp==0||max_side[tmp]-mat[0][tmp]<max_side[i]-mat[0][i]))
                  tmp=i;
    
            }
    
            if(max_side[tmp]<=mat[0][tmp])break;//总权值 不再减小
            res-=max_side[tmp]-mat[0][tmp];
            mat[0][tmp]=mat[tmp][0]=0;
            int p=0;
            for(i=tmp;pre[i]!=0;i=pre[i])//找到 ,最大边的始发点
            {
                if(p==0||mat[p][pre[p]]<mat[i][pre[i]])p=i;
    
            }
            pre[p]=0;//断开
            update(tmp,0,0);//更新最大边
    
        }
        printf("Total miles driven: %d\n",res);
    }
    int main()
    {
        int m,i;
        string a,b;
        int d;
        //freopen("int.txt","r",stdin);
        while(scanf("%d",&m)!=EOF)
        {
            map1.clear();
            map1["Park"]=0;
          memset(mat,0,sizeof(mat));
            n=1;
            for(i=0;i<m;i++)
            {
                cin>>a>>b>>d;
                if(!map1.count(a))map1[a]=n++;
    
                if(!map1.count(b))map1[b]=n++;
    
                int x=map1[a];
                int y=map1[b];
                if(!mat[x][y]||mat[x][y]>d)
                mat[x][y]=mat[y][x]=d;
    
    
    
    
            }
    
            scanf("%d",&k);
    
            solve();
        }
    }
  • 相关阅读:
    枚举8项素数和环
    登录过滤器
    线程调度
    回溯素数环
    centos 6.5 samba简单配置
    区间k大数查询
    Centos安装arm-linux-gcc等交叉工具链
    centos7安装tftp服务器
    八皇后问题
    输出1——n的排列(深度优先搜索)
  • 原文地址:https://www.cnblogs.com/acSzz/p/2613115.html
Copyright © 2020-2023  润新知