• 次小生成树(POJ1679/CDOJ1959)


    POJ1679

    首先求出最小生成树,记录权值之和为MinST。然后枚举添加边(u,v),加上后必形成一个环,找到环上非(u,v)边的权值最大的边,把它删除,计算当前生成树的权值之和,取所有枚举加边后生成树权值之和的最小值

    思路: 

    最小生成树唯一性判断,求次小生成树的val再与最小生成树比较。
    1.先用prim算法求出最小生成树。并在其过程中保存加入到MST中的Max[i][j]( i 到 j 路径中的最大边 ) 
    2.对未加入MST的边进行遍历:从MST中去掉i j路径中的最大边,加入未访问边G[i][j],如果得到的生成树的权值和MST的相等,则存在次小生成树的权值=MST的权值和

     1 #include <algorithm>
     2 #include <iostream>
     3 #include <cstdio>
     4 #include <cmath>
     5 #include <cstring>
     6 using namespace std;
     7 #define INF 0x3f3f3f3f
     8 
     9 const int N = 1e3+6;
    10 int G[N][N]; // graph matrix
    11 int f[N];    // pre node
    12 int vis[N];  // visited node
    13 int dis[N];  // main matrix: the edges(set-u)
    14 int mark[N][N];
    15 int Max[N][N]; // path from i to j max edge
    16 int n,m,best;
    17 
    18 int prim(int v)
    19 {
    20     int ans=0;
    21     vis[v]=1;
    22     for(int i=1;i<=n;i++)
    23     {
    24         dis[i]=G[v][i];
    25         f[i]=v;
    26     }
    27     dis[v]=0;
    28     for(int i=1;i<n;i++) // n-1
    29     {
    30         int u=-1;
    31         for(int j=1;j<=n;j++)
    32             if(!vis[j]&&(u==-1||dis[j]<dis[u]))u=j;
    33         if(u==-1)break;
    34         vis[u]=1;
    35         mark[u][f[u]] = mark[f[u]][u] = 1;
    36         ans += dis[u];
    37         for(int j=1;j<=n;j++)
    38         {
    39             // compare with visited node record path max edge
    40             if(vis[j])Max[u][j]=Max[j][u]=max(Max[j][f[u]],dis[u]);
    41             if(!vis[j]&&dis[j]>G[u][j])
    42             {
    43                 dis[j]=G[u][j];
    44                 f[j]=u;
    45             }
    46         }
    47     }
    48     return ans;
    49 }
    50 
    51 int SMST()
    52 {
    53     int mini=INF;
    54     for(int i=1;i<=n;i++)
    55         for(int j=1;j<=n;j++)  //or for(int j=i+1;j<=n;j++)
    56         {
    57             if(i!=j&&!mark[i][j])
    58                 mini = min(mini,best+G[i][j]-Max[i][j]);
    59         }
    60     return mini;
    61 }
    62 
    63 int main()
    64 {
    65     int x,y,w,T;
    66     scanf("%d",&T);
    67     while(T--)
    68     {
    69         memset( vis, 0 ,sizeof(vis) );
    70         memset( Max, 0 ,sizeof(Max) );
    71         memset( f, 0 ,sizeof(f) );
    72         memset( dis, 0 ,sizeof(dis) );
    73         memset( mark, 0 ,sizeof(mark) );
    74         scanf("%d%d",&n,&m);
    75         for(int i=1;i<=n;i++)
    76             for(int j=1;j<=n;j++)
    77             {
    78                 if(i==j)G[i][j]==0;
    79                 else G[i][j]=INF;
    80             }
    81         for(int i=0;i<m;i++)
    82         {
    83             scanf("%d%d%d",&x,&y,&w);
    84             G[x][y]=w;
    85             G[y][x]=w;
    86         }
    87         best = prim(1);
    88         int temp = SMST();
    89         if(temp == best)
    90             printf("Not Unique!
    ");
    91         else
    92             printf("%d
    ",best);
    93     }
    94     return 0;
    95 }

    CDOJ 1959

    思路:这道题会出现重边,用Kruskal算法处理起来比较方便。(用邻接表方便,邻接矩阵还要考虑同两点之间的多条权值相同的边。)

    次小生成树的权值如果等于最小生成树的权值, 其替换边只会是权值相同的边,于是可以把权值相同边放在一起考虑,分别判断全部没有合并时有多少可以合并(cnt1记录的是可以加入集合的边数),边合并边判断有多少可以合并(cnt2记录的是选中一条加入到集合),如果可选的大于构成最小生成树所需要的,那么就存在一种相同的边权可以替换原来的,从而最小生成树不唯一。

     1 #include <bits/stdc++.h>
     2 #define LL long long
     3 using namespace std;
     4 const int N = 2e5+6;
     5 struct Node
     6 {
     7     int u,v;
     8     LL w;
     9 }G[N];
    10 
    11 int n,m;
    12 int f[2002];
    13 int find(int x)
    14 {
    15     return x==f[x]?x:f[x]=find(f[x]);
    16 }
    17 
    18 bool cmp(Node & a, Node & b)
    19 {
    20     return a.w<b.w;
    21 }
    22 
    23 void kruskal()
    24 {
    25     int cnt1=0;
    26     int cnt2=0;
    27     for(int i=1;i<=n;i++)f[i]=i;
    28     sort(G,G+m,cmp);
    29     for(int i=0;i<m;)
    30     {
    31         int j=i;
    32         while(j<m&&G[j].w==G[i].w)
    33         {
    34             int u = find(G[j].u);
    35             int v = find(G[j].v);
    36             if(u!=v)cnt1++;
    37             j++;
    38         }
    39         j=i;
    40         while(j<m&&G[j].w==G[i].w)
    41         {
    42             int u = find(G[j].u);
    43             int v = find(G[j].v);
    44             if(u!=v){
    45                 cnt2++;
    46                 f[u]=v;
    47             }
    48             j++;
    49         }
    50         i=j;
    51         if(cnt1>cnt2)break;
    52     }
    53     if( cnt1 > cnt2 ){
    54         printf("zin
    ");
    55     }else{
    56         printf("ogisosetsuna
    ");
    57     }
    58 }
    59 
    60 int main()
    61 {
    62     int x,y;
    63     LL w;
    64     cin>>n>>m;
    65     for(int i=0;i<m;i++)
    66     {
    67         cin>>x>>y>>w;
    68         G[i].u=x;
    69         G[i].v=y;
    70         G[i].w=w;
    71     }
    72     kruskal();
    73     return 0;
    74 }
  • 相关阅读:
    ZOJ 2770 Burn the Linked Camp 差分约束
    作业04 一个简单的扑克牌游戏
    C++友元
    ZOJ 3645高斯消元
    CodeForces 55D 数位统计
    03类的设计和使用
    HDU 4522
    POJ 2559单调栈
    PL/SQL REPORT 开发模拟登陆
    修改报表心得
  • 原文地址:https://www.cnblogs.com/demian/p/9188918.html
Copyright © 2020-2023  润新知