• poj1639(k限制最小生成树)


    题意:

    有n个兄弟去野餐,目的地为Park。每个人可以选择直接去Park,也可以选择去其他人家,和他一起坐车去Park。

    每个人家的停车位没有限制,但是Park的停车数不能超过k。问所有人的最短路程。

    思路:

    假设Park的停车数没有限制,那么这题就是一道最小生成树了。

    但是本题限制Park的停车数不能超过k,把Park看做根节点记为V0,那么就是说它的度数不能超过k。

    得到一棵k度限制生成树的步骤:

    1. 忽略根节点做一次kruskal,此时得到的是一个森林,包含了m个最小生成树。

    2. 对于每一颗最小生成树树,选择其中离根节点最近的点,向根节点连一条边,此时得到了一棵m度的最小生成树。

    3. 由m度生成树得到m+1度生成树:

        (1). 用dp预处理出当前生成树中从V0到点i的路径上与V0无关联的权值最大的边,记为dp[i].d,该边的两端点记为dp[i].u和dp[i].v。

        (2). 对于一个不在生成树中的边<V0, v>, 如果将该边加入生成树中,则一定会得到一个环。

               此时我们删掉环中权值最大的边,即(1)中预处理得到的dp[v],得到一棵m+1度的生成树。

        (3). 对于(2)枚举每一个v,记minnum=min{(V0, v) - dist[v].d },使minnum得到最小值的点v就是这次选择的点。连接V0, v,删去dp[v]。

    4. 重复步骤3直到得到一棵k度限制生成树。本题要求度数不超过k,所以在某一步中,minnum>=0,就可以输出答案了。

    关于minnum的含义:

    minnum为在从m度生成树得到m+1度生成树的过程中,选择一个点v(连接V0, v,删去dp[v])可以得到的最大利益,即生成树的值最多可以减少多少。

    minnum为负数,表示选择点v可以生成树的值减少,那么使minnum最小的点就是可以使生成树的值减少最多的点,这次我们便选择它。

    如果minnum>=0,说明得到m+1度生成树不会获得任何利益,就不用继续下去,直接输出答案即可。

    关于最小度数限制生成树详情参考2004国家集训队汪汀的论文。

    (转)

    #include<cstdio>
    #include<cstring>
    #include<map>
    #include<string>
    #include<algorithm>
    #include<iostream>
    using namespace std;

    struct my{
           int u,v,w;
    };

    bool cmp(const my &a,const my &b){
         return a.w<b.w;
    }

    const int maxn=100;
    const int nil=0x3f3f3f3f;

    map<string,int>Map;
    string s1,s2;
    bool intree[maxn][maxn];
    int tree[maxn][maxn],fa[maxn*10],cnt,ans,n,m,mintree[maxn*10],keypoint[maxn*10],k;
    my edge[maxn*10],dp[maxn*10];

    void init(){
         for (int i=0;i<maxn*10;i++) fa[i]=i;
         memset(tree,0x3f,sizeof(tree));
         memset(mintree,0x3f,sizeof(mintree));
         Map["Park"]=1;
         cnt=1;
    }

    int getfa(int x){
        if(x==fa[x]) return x;
        return fa[x]=getfa(fa[x]);
    }

    void kus(){
         sort(edge+1,edge+1+n,cmp);
         for (int i=1;i<=n;i++){
            int u=edge[i].u;
            int v=edge[i].v;
            int x=getfa(u);
            int y=getfa(v);
            if(x==y||u==1||v==1) continue;
            ans+=edge[i].w;
            fa[x]=y;
            intree[u][v]=intree[v][u]=true;
         }
    }

    void dfs(int cur,int pre){
         for (int i=2;i<=cnt;i++){
            if(i==pre||!intree[cur][i]) continue;//保证边是在最小生成树之中
            if(dp[i].w==-1){
            if(dp[cur].w>tree[cur][i]) dp[i]=dp[cur];
            else {
                dp[i].w=tree[cur][i];
                dp[i].u=cur;
                dp[i].v=i;
            }
         }
         dfs(i,cur);
        }
    }

    void solve(){
         for(int i=2;i<=cnt;i++){
             if(tree[1][i]==nil) continue;
             int color=getfa(i);
             if(mintree[color]>tree[1][i]){
                keypoint[color]=i;
                mintree[color]=tree[1][i];
             }//找各个最小生成树与1相连的最短边
         }
         for (int i=1;i<=cnt;i++){
            if(mintree[i]!=nil){
                m++;
                ans+=tree[1][keypoint[i]];//将最短边的权值加到ans中,构造m限制生成树
                intree[keypoint[i]][1]=intree[1][keypoint[i]]=true;
            }
         }
         for (int i=m+1;i<=k;i++){
              memset(dp,-1,sizeof(dp));
              dp[1].w=-nil;
              for (int j=2;j<=cnt;j++) if(intree[1][j]) dp[j].w=-nil;//和1相邻的树边不能算,dp求的是与1不相连边的最大值
              dfs(1,-1);
              int idx,minnum=nil;
              for (int j=2;j<=cnt;j++){
                if(tree[1][j]!=nil||intree[1][j]){//找所有和1相连的且在最小生成树中的边,看删哪一条
                    if(minnum>tree[1][j]-dp[j].w) minnum=tree[1][j]-dp[j].w,idx=j;
                }
              }
              if(minnum>=0) break;
              ans+=minnum;
              intree[1][idx]=intree[idx][1]=true;
              intree[dp[idx].u][dp[idx].v]=intree[dp[idx].v][dp[idx].u]=false;
         }
    }

    int main(){
        init();
        int w;
        scanf("%d",&n);
        for (int i=1;i<=n;i++){
            cin>>s1>>s2>>w;
            if(!Map[s1]) Map[s1]=++cnt;
            if(!Map[s2]) Map[s2]=++cnt;
            int u=Map[s1],v=Map[s2];
            edge[i].u=u,edge[i].v=v,edge[i].w=w;
            tree[u][v]=tree[v][u]=min(tree[u][v],w);
        }
        scanf("%d",&k);
        kus();//先求最小生成树
        solve();
        printf("Total miles driven: %d ",ans);
    return 0;
    }
  • 相关阅读:
    AngularJS双向绑定,手动实施观察
    AngularJS的Hello World
    LESS碎语
    chrome浏览器调试报错:Failed to load resource: the server responsed width a status of 404 (Not Found)…http://127.0.0.1:5099/favicon.ico
    AngularJS报错:[$compile:tpload]
    Javascript中的依赖注入
    使用HTML5和CSS3碎语
    在Brackets中使用Emmet
    使用Brackets
    Bootstrap碎语
  • 原文地址:https://www.cnblogs.com/lmjer/p/9353359.html
Copyright © 2020-2023  润新知