• vijos次小生成树


    xiaomengxian的哥哥是一个游戏迷,他喜欢研究各种游戏。这天,xiaomengxian到他家玩,他便拿出了自己最近正在研究的一个游戏给xiaomengxian看。这个游戏是这样的:一个国家有N个城市,有些城市之间可以建设铁路,并且不同城市之间建设铁路的费用各不相同。问如何用最小的费用,使整个国家的各个城市之间能够互相到达。另外,铁路是双向的。xiaomengxian心想,这不是太简单了吗?这就是经典的MST问题。他的哥哥说,这个当然不算什么。关键是它还要求费用第二小的方案,这真是让人伤脑筋。xiaomengxian想了很久,也没有想出来,你能帮助他吗? 

    费用第二小的方案的定义为:与费用最小的方案不完全相同,且费用值除费用最小的方案外最小。

    同vijos 1070 网址:https://www.vijos.org/p/1070

    题解:

    次小生成树,就是求除了最小生成树之外最小的那个生成树。

    下面介绍一下利用prim求次小生成树的主要步骤。

    1.先求出来最小生成树。并将最小生成树任意两点之间路径当中的权值最大的那一条找出来,为什么要找最大的呢,因为生成树加入一条边之后一定构成了回路,那么肯定要去掉这个回路当中一条边才是生成树,那么,怎么去边才是次小的,那就去掉除了刚刚添加的一条边之外回路当中权值最大的一个,所以留下的就是最小的。

    2.枚举最小生成树外的每一条边。找出最小的就是次小生成树。

    代码如下,若还是不懂,代码中有注释,可自行参悟

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std; 
     4 int s,minn,g[501][501],m,x,y,z,ss,minn2,low[501],pre[501],low2[501][501]; 
     5 int i,mini,sum,n,t; bool b[501],used[501][501];
     6 int main() 
     7 {
     8      scanf("%d%d",&n,&m);
     9      for(i=1;i<=n;i++)         
    10      {
    11         g[i][i]=0;
    12         for(int j=i+1;j<=500;j++)             
    13             g[i][j]=g[j][i]=123456789;     
    14     }
    15     for(i=1;i<=m;i++)     
    16     {         
    17         scanf("%d%d%d",&x,&y,&z);     
    18         g[y][x]=g[x][y]=z;//双向边
    19     }     
    20     for(i=1;i<=n;i++)         
    21         low[i]=123456789,pre[i]=i;
    22     pre[1]=0;  
    23     low[1]=0;s=0;ss=923456789;
    24     int k,kk;
    25     while(1)     
    26     {
    27         minn=123456789;
    28         for(i=1;i<=n;i++)         
    29         {
    30             if(!b[i]&&low[i]<minn)             
    31             {                 
    32                 minn=low[i];
    33                 mini=i;
    34             }
    35         }
    36         if(minn==123456789)break;
    37         b[mini]=1;
    38         s=s+minn;
    39         used[mini][pre[mini]]=used[pre[mini]][mini]=1;
    40         for(i=1;i<=n;i++)         
    41         {
    42             if(b[i]&&i!=mini)low2[i][mini]=low2[mini][i]=max(low2[i][pre[mini]],low[mini]);//记录最小生成树上的路径最大值
    43             t=min(g[i][mini],g[mini][i]);
    44             if(!b[i]&&t<low[i])
    45                 low[i]=t,pre[i]=mini;//学过prim的都知道前半段是什么,后半句是记录路径
    46         }
    47     }
    48     printf("Cost: %d
    ",s);
    49     for(int i=1;i<=n;i++)
    50     {
    51         for(int j=i+1;j<=n;j++)
    52             if(g[i][j]!=123456789&&!used[i][j])
    53                 ss=min(ss,s+g[i][j]-low2[i][j]);//枚举每一条边,看看替换该边是否有价值
    54     }
    55     if(ss==923456789)ss=-1;//如果没有边可以替换,即没有最小生成树,输出-1
    56     printf("Cost: %d",ss);
    57 }
  • 相关阅读:
    Atcoder D
    51nod 1201 整数划分(dp)
    Atcoder D
    Atcoder C
    codeforces 812 E. Sagheer and Apple Tree(树+尼姆博弈)
    codeforces 811 D. Vladik and Favorite Game(bfs水题)
    codeforces 811 E. Vladik and Entertaining Flags(线段树+并查集)
    codeforces 811 C. Vladik and Memorable Trip(dp)
    1449 砝码称重(思维)
    SQL大量数据查询的优化 及 非用like不可时的处理方案
  • 原文地址:https://www.cnblogs.com/lcxer/p/9441794.html
Copyright © 2020-2023  润新知