POJ T1679 The Unique MST

版权声明:希望能帮到弱校的ACMer成长,因为自己是弱校菜鸡~~~~ https://blog.csdn.net/Mr__Charles/article/details/82352599

                          POJ T1679 The Unique MST

 


题解:

    次小生成树,一开始看其他大牛的博客的时候,怎么去求次小是看的懂得,但是看到大牛们的代码之后就其中有个点卡了好久,因为用到了动态规划的思想。在经过队友疏导后,终于是懂了。(咸鱼还得继续加油)

主要思想:

先求出最小生成树,在求出次小生成树,如果两者最后结果相等,即最小生成树不唯一。

次小生成树的求法:先开辟三个数组做准备

①used【MAXN】(用于标记最小生成树用过的边)

②pre【MAXN】(用于记录每个点对应的前驱,相当于父节点

③Max【MAXN】(稍后详解)

如何求次小生成树呢,因为在求出最小生成树后,如果连接两点(此边不在最小生成树中),必定会形成环,此时我们就要去掉一条边吗,使得其依旧为生成树且次小的话就要删除一条在最小生成树中最长的边。只有这样最小生成树和当前所求生成树的差值才能最小化,才能是次小生成树。

可是点这么多,任一连接两个点,都是不一样的情况,怎么求当前情况的最小生成树最长边呢?

这里就用到了动态规划的思想,而Max这个数组就是用来记录最长边的,就是代码中的这句话:

Max[j][p] = Max[p][j] = max(dis[p],Max[j][pre[p]]);

这是在求最小生成树的过程中,将每种情况的最小生成树最长边求出并记录。

看不懂动态规划思想的鄙人这里直接上图:

接下来就靠你们自己悟了.......

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 105
#define INF 0x3f3f3f3f

int mst,smst;
int n,m,maps[MAXN][MAXN],dis[MAXN];
int pre[MAXN],Max[MAXN][MAXN];
bool vis[MAXN],used[MAXN][MAXN];

void init(){
	mst = 0;
	smst = INF;
	for(int i = 0; i <= n; ++i){
		vis[i] = false;
	    for(int j = 0; j <= i; ++j){
            i == j ? maps[i][j] = 0 : maps[i][j] = maps[j][i] = INF;
            Max[i][j] = Max[j][i] = 0;
            used[i][j] = used[j][i] = false;
	    }
	}
}

void Prim(int s){
	vis[s] = true;
	for(int i = 1 ; i <= n; ++i){
		dis[i] = maps[s][i];
		pre[i] = s;
	}

	for(int i = 1; i < n; ++i){
		int p = -1,minc = INF;
		for(int j = 1; j <= n; ++j)
			if(!vis[j] && dis[j] < minc){
				minc = dis[j];
				p = j;
			}
		
		vis[p] = true;
		mst += dis[p];
		used[p][pre[p]] = used[pre[p]][p] = true;
		
		for(int j = 1; j <= n; ++j){
			if(vis[j] && j != p)
			    Max[j][p] = Max[p][j] = max(dis[p],Max[j][pre[p]]);  //重点 
			
			if(!vis[j] && dis[j] > maps[p][j]){
				dis[j] = maps[p][j];
				pre[j] = p;
			}
		}
	}
}

void Second_tree(){
	for(int i = 1; i <= n; ++i)
	    for(int j = 1; j < i; ++j)
	        if(!used[i][j] && maps[i][j] != INF)
	            smst = min(smst,mst + maps[i][j] - Max[i][j]); //连接任意边,删除最长边 
}


int main(){
	int t,a,b,c;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		init();
		for(int i = 0; i < m; ++i){
			scanf("%d%d%d",&a,&b,&c);
			maps[b][a] = maps[a][b] = min(maps[a][b],c);
		}
		Prim(1);
		Second_tree();
		if(mst != smst)
		    printf("%d\n",mst);
		else
		    puts("Not Unique!");
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/Mr__Charles/article/details/82352599