POJ 1679 The Unique MST(次小生成树)
http://poj.org/problem?id=1679
题意:
给你一个n个节点m条边的无向图,问你该图的最小生成树是否唯一?如果唯一输出,树的权值,否则输出'Not Unique!'.
分析:
其实本题就是要求该无向图的次小生成树的权值是否等于最小生成树的权值.刘汝佳的<<训练指南>>P344介绍了次小生成树以及它的求法.这里我们采取上面介绍的简单点的方法.
一个图的次小生成树(权值<=最小生成树的权值,可能等于最小生成树的权值)肯定至少有一条边与最小生成树的一条边不同.所以我们可以枚举最小生成树上的边,然后依次用m-1条边的无向图来重新生成最小生成树,求出新生成的最小生成树中的权值最小值即为次小生成树的权值.
注意:原题说原图是连通的,但是如果我们删除了一条边后,原图就很可能不连通了,我在这里考虑不全,WA了几次…
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=100+10;
const int maxm=100*100+10;
struct Edge
{
int u,v,dist;
int id;//原始编号
Edge(){}
Edge(int u,int v,int d,int id):u(u),v(v),dist(d),id(id){}
bool operator<(const Edge &rhs)const
{
return dist <rhs.dist;
}
};
struct Kruskal
{
int n,m;
Edge edges[maxm];
vector<int> E;//保存最小生成树上的边原始序号
int fa[maxn];//并查集相关
int findset(int x){ return fa[x]==-1? x: fa[x]=findset(fa[x]); }
void init(int n)
{
this->n=n;
m=0;
}
void AddEdge(int u,int v,int dist,int id)
{
edges[m++]=Edge(u,v,dist,id);
}
int kruskal(int ID)
{
E.clear();
memset(fa,-1,sizeof(fa));
int sum=0; //最小生成树权值
int cnt=0; //最小生成树边数目
sort(edges,edges+m);
for(int i=0;i<m;i++)
{
if(edges[i].id == ID) continue;//ID边被删除
int u=edges[i].u, v=edges[i].v;
if(findset(u) != findset(v))
{
E.push_back(edges[i].id);
fa[findset(u)] = findset(v);
sum +=edges[i].dist;
if(++cnt>=n-1) break;
}
}
if(cnt<n-1) return -1;
return sum;
}
}KK;
int main()
{
int T; scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
KK.init(n);
for(int i=0;i<m;i++)
{
int u,v,d;
scanf("%d%d%d",&u,&v,&d);
KK.AddEdge(u,v,d,i);
}
int ans1 = KK.kruskal(-1);
int ans2 = 1e9;
vector<int> E(KK.E); //保存原图最小生成树上的
for(int i=0;i<E.size();i++)
{
int tmp = KK.kruskal(E[i]);
if(tmp ==-1) continue; //此时图不连通,不存在最小生成树
ans2 = min(ans2,tmp);
if(ans2 == ans1) break;
}
if(ans1==ans2) printf("Not Unique!\n");
else printf("%d\n",ans1);
}
return 0;
}