POJ_1679A TheUniqueMST ( 最小生成树 | 次小生成树 )

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44510468/article/details/102679592

文章目录

题目连接 vj or poj

题意

给一个图, 求有几个最小生成树, 有一个最小生成树则输出权值和, 多个输出一个字符串

题解

先吐槽一下 poj的老年机, 都什么年代了, 还连c11都不支持, ce的心态蹦了, 各种改回去

特别注意, 排序以后, 需要标记是是否有权值相同的边( 标记边编号 ) 一是优化后面每次删边以后跑 Kruskal 的时间, 二是必须标记, 如果后面如果删了这条边的话,
如果样例给了 n 个顶点, n-1 条边的话, 此时是删了一条边会死循环是构成不了最小生成树的;
如果样例给了 n 个顶点 n 条边 的话, 此时删了一条边一定会产生一个更大的树
所以除了优化的作用, 也是必须要有的!

先跑一遍记录构成最小生成树的权值和, 同时记录所选最小生成树的的边编号,
再进项 m 次尝试, 寻找是否删掉了某条边还能够一个最小生成树 ( 删边有回溯的思想 )

附几个图, 加深理解
在这里插入图片描述

代码

// #include <bits/stdc++.h>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int MAXM = 1e4+100; // 题目没给边的范围, 尽量开大

class DisjointSet { 
    public:	
        int n;
        int root[MAXM];
        DisjointSet( ) { };
        DisjointSet( int _n ) : n(_n) { };
        void INI ( int n ) {
            this->n = n;
            for ( int i = 1; i <= n; ++i ) root[i] = i;
        }
        int GET ( int x ) {
            return root[x]==x ? x : root[x]=GET( root[x] );
        }
        bool MERGE ( int x, int y ) {
            int rx = GET( x ), ry = GET( y );
            if ( rx==ry ) return false;
            root[ry] = rx; return true;
        }
}ds;
struct EDGE {
	int u, v, w;
}e[MAXM];
bool hasEqual[MAXM], hasUsed[MAXM], hasDeleted[MAXM];
int n, m;

bool firstKruskal = true;
int kruskal ( ) {
	int sum = 0, cnt = 0;
	ds.INI( n );
	for ( int i = 0; i < m; ++i ) {
		if ( hasDeleted[i] ) continue; // 实现删边操作, 如果标记了这条边, 就跳过它, 来实现删除操作
		if ( ds.MERGE( e[i].u, e[i].v ) ) {
			if ( firstKruskal ) hasUsed[i] = 1; // 记录最小生成树所选择的边, 也就是第一次跑 kruskal 所选的边
			sum += e[i].w;
			cnt++;
			if ( cnt == n-1 )
				break;
		}
	}
	return sum;
}
bool cmp ( EDGE a, EDGE b ) {return a.w < b.w; }

int main ( ) {
	int T;

	cin >> T;
	while ( T-- ) {

		cin >> n >> m;

		for ( int i = 0; i < m; ++i ) 
            cin >> e[i].u >> e[i].v >> e[i].w;

        memset( hasEqual, false, sizeof(hasEqual) );
        memset( hasUsed, false, sizeof(hasUsed) );
        memset( hasDeleted, false, sizeof(hasDeleted) );

		sort ( e, e + m, cmp );
		for ( int i = 0; i < m; ++i ) // 排序完找是否有权值等于这条边的其他边, 优化查找第二个及以上的最小生成树的时间
			for ( int j = i+1; j < m; j++ ) // 还可以通过这个标记, 保证再次找树的时候, 不会生成不了树死循环, 或者找到一个更大的树
                if ( e[i].w!=e[j].w ) break;
                else hasEqual[i] = hasEqual[j] = true;

        firstKruskal = true; 
		int minSum = kruskal ( ); // 记录最小生成树的权值和
		firstKruskal = false;

        bool isUnique = true; 
		for ( int i = 0; i < m; ++i ) { // 开始寻找次小生成树
            if ( !hasUsed[i] || !hasEqual[i] ) continue; 
            hasDeleted[i] = true;
            if ( minSum==kruskal() ){ 
                isUnique = false;
                break;
            }
            hasDeleted[i] = false;
		}

		if ( isUnique )
			cout << minSum << endl;
		else
			cout << "Not Unique!" << endl;
	}


    
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44510468/article/details/102679592