版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题意
给一个图, 求有几个最小生成树, 有一个最小生成树则输出权值和, 多个输出一个字符串
题解
先吐槽一下 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;
}