次小生成树

Description: 次小生成树是次于最小生成树的一个生成树,也就是说存在一种大于且仅仅大于最小生成树的一种连接所有点的方式来连通一个图。
Solution: 我们知道,次小生成树可以由图中不属于最小生成树的边替换最小生成树上一条最大的边来获得。这是为什么呢。我们知道在已经生成了最小生成树的图中,添加一条不属于最小生成树的边势必会形成一个环,那么这个时候我们就要在最小生成树上删掉一条边。这样就可以得到一个生成树。最后我们以此类推去替换任意两点的边。来获得替换的边于删除的边的最小差值。这样就可以得到次小生成树;那么问题又来了,要替换生成树上某一条路劲上最大的边,这个最大的边如何获得呢?我们采用DP的方式。在Prim中得到dis中最小的值tmp后同时也得到了这个点,那么我们可以枚举这个时候已经在生成树集合中的点,得到这个点于集合中的点路径中最大权值的一条边,这条边要么是tmp,要么是dp[pre[k]][stack[j]].(dp[i][j]的意思是i到j的路劲上权值最大的一条边)。最后就是二重枚举任意两个点了。多看看代码就会明白了。
Code(C++):

#include <stdio.h>
#include <string.h>

#define MAX(a,b) ((a)>(b)? (a):(b))
#define MIN(a,b) ((a)<(b)? (a):(b))

const int M=105;

const int INF=0x3f3f3f3f;

int map[M][M];
int n,m;

int dis[M];
bool used[M];

int dp[M][M];
int pre[M];

int stack[M];

int prim(int src)
{
    int top=0;
    int ans=0;

    for(int i=0;i<M;i++){
        for(int j=0;j<M;j++)
            dp[i][j]=0;
        pre[i]=src;
        used[i]=false;
        dis[i]=map[src][i];
    }
    dis[src]=0;
    used[src]=true;

    for(int i=1;i<n;i++){
        int tmp=INF,k=src;
        for(int j=1;j<=n;j++)
            if(!used[j]&&dis[j]<tmp)
                tmp=dis[j],k=j;

        if(k==src)
            break;
        ans+=tmp;
        used[k]=true;

        for(int j=1;j<top;j++)
            dp[k][stack[j]]=dp[stack[j]][k]=MAX(tmp,dp[pre[k]][stack[j]]);
        stack[top++]=k;

        for(int j=1;j<=n;j++)
            if(!used[j]&&dis[j]>map[k][j])
                pre[j]=k,
                dis[j]=map[k][j];
    }

    return ans;
}

int next_prim(src)
{
    int ans=prim(src);

    int flag=INF;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i!=j&&i!=pre[j]&&j!=pre[i])
                flag=MIN(flag,map[i][j]-dp[i][j]);
    return ans+flag;
}

int main()
{
    int N;
    for(scanf("%d",&N);N--;){
        scanf("%d%d",&n,&m);

        for(int i=1;i<n;i++)
            for(int j=i+1;j<=n;j++)
                map[i][j]=map[j][i]=INF;
        for(int i=0;i<m;i++){
            int x,y,c;
            scanf("%d%d%d",&x,&y,&c);
            map[x][y]=map[y][x]=c;
        }

        int ans=next_prim();

        printf("%d\n",ans);

    }
    return 0;
}

你可能感兴趣的:(次小生成树)