POJ-1679 The Unique MST

来源:互联网 发布:剑三商城数据导入非法 编辑:程序博客网 时间:2024/05/06 10:23

题目链接:http://poj.org/problem?id=1679


题目大意:

给你一个有权值的无相图,判断最小生成树是否唯一。


解题思路:

网上的资料很多,但是没有关于次小生成树的概念。从名字上看,次小生成树就是在最小生成树的基础上,其中某些边大于最小生成树,但是大于其他的生成树,类似于第二小生成树的意思。。。。

这道题就是用到了这种思想。

首先,我们可以通过prim算法得到这个图的最小生成树MST。之后,我们通过某些操作就可以判断MST是否唯一。

整体思想就是,先得到MST。然后,我们可以在MST外选择一条边加入MST,这时,必然会形成一个环,那么我们就在MST上删去一条边(不能是加入的边),这样,我们就得到了另一个MST。如果这个MST的权值之和和原来的MST权值相同,就有多种构图形成MST。输出YES。


算法实现流程:

1.首先使用prim算法为基础,在此基础上加入数组max1[i][j],用于记录i到j路径上的权值最大的边。

2.加入stack[i]数组,用于记录加入到MST中的顶点

3.加入pre[i]数组,用于记录加入MST的顶点的相关联边的直接前驱。

4.在找到temp和k后,需要进行一次循环,用于更新新加入点到MST各点路径最大值(作用在于,如果我要在这个顶点加边形成环,我需要去掉这个环上权值最大的边)至于为什么要用

max(max1[stack[j]][pre[k]], temp)

比较的是pre[k],因为加入的k点如果要跟MST其他点连接,必须通过它的直接前驱,而它的前驱一定是MST中已经确定的到各个顶点都是最大值的记忆化状态,所以我们要比较的是pre[k],而后面之所以是temp因为,temp是MST和非MST两个集合相连的最小权值的边,但是你要加入MST,不能保证他是MST中权值最小的(甚至是最大的)所以加入时需要用temp和max1[stack[j][pre[k]]比较,来得到max1[i][j](i到j路径上权值最大的边)

5.保存一下加入到MST中的顶点

6.更新lowxost,并且记录下更新后的直接前驱

7.prim算法结束后,max1数组存放的就是MST中各个顶点之间的权值最大的边。这时,我们需要对MST外的边加入到MST中并删除最大边的操作。这时候我们需要一个双层循环,遍历每一个MST外的边,当然,必须保证是MST外的边,那个判断语句就是防止是MST种的边,至于为什么i != pre[j] 和 j != pre[i]同时存在,是为了防止出现i=3,j=2和j=2,i=3,这种情况。这个判断一定要正确,要不然就会出现错误。


代码如下:

#include<iostream>#include<cstdio>#include<cstring>#include<climits>#include<algorithm>using namespace std;#define N 510int map[N][N], lowcost[N], pre[N], max1[N][N], stack[N];bool visit[N];int n, m, sum;void prim() //默认1在MST中{int temp, k;int top; //保存最小生成树的结点memset(visit, false, sizeof(visit)); //初始化visit[1] = true;sum = 0;top = 0;for(int i = 1; i <= n; ++i){pre[i] = 1;lowcost[i] = map[1][i];}lowcost[1] = 0;stack[top++] = 1; //保存MST的结点for(int i = 1; i <= n; ++i){temp = INT_MAX;for(int j = 1; j <= n; ++j)if(!visit[j] && temp > lowcost[j])temp = lowcost[k = j];if(temp == INT_MAX)break;visit[k] = true;sum += temp;for(int j = 0; j < top; ++j) //新加入点到MST各点路径最大值max1[stack[j]][k] = max1[k][stack[j]] = max(max1[stack[j]][pre[k]], temp);stack[top++] = k; //保存MST的结点for(int j = 1; j <= n; ++j) //更新if(!visit[j] && lowcost[j] > map[k][j]){lowcost[j] = map[k][j];pre[j] = k; //记录直接前驱}}}int main(){int ncase;int start, end, cost;int minn;scanf("%d", &ncase);while(ncase--){for(int i = 1; i < N; ++i) //初始化不为0,1必须用循环。。。。for(int j = 1; j < N; ++j){map[i][j] = INT_MAX;max1[i][j] = 0;}scanf("%d%d", &n, &m);for(int i = 1; i <= m; ++i){scanf("%d%d%d", &start, &end, &cost);//if(cost < map[start][end])(POJ竟然出现重边的时候不选择最小的~~~)map[start][end] = map[end][start] = cost;}prim();minn = INT_MAX;for(int i = 1; i <= n; ++i)for(int j = 1; j <= n; ++j)if(i != j && i != pre[j] && j != pre[i]) //枚举MST以外的边minn = min(minn, map[i][j] - max1[i][j]); //求出{MST外加入边-MST环上权值最大边}最小值if(minn != 0)printf("No\n");elseprintf("Yes\n");}return 0;} 


原创粉丝点击