题目大意】
给出一个带权无向图,问其最小生成树是否唯一,若唯一输出其权值和,否则输出"Not Unique!".
1.其实,在判断最小生成树是否是唯一的时候,用到了次小生成树的思想,求一棵次小生成树,最次小生成树的总权值等于最小生成树的权值,则不唯一;
这里涉及到求次小生成树的问题,首先用prim 算法求一个最小生成树, 再做一次添边和删边的操作,添加一条最小生成树外的边,此时必在最小生成树上形成环,在删去环上权值最大的边,求出添删操作中差额最小值,若最小值为0,则不唯一,否则唯一;(这里我是这样做的,在pime算法求最小生成树的过程中,有一个增加生成树节点的操作,每增加一个节点,则求出该节点到生成树上每一个点的路径上的最大边)
2.第二种判断方法,在pime算法过程中,若某个点i的D[i]被确定时,D[i]能够被2个或以上的点更新得到,那么MST显然不唯一。
判断方法一:
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int g[110][110],n,m,ans,d[110],pre[110];
int max1[110][110],stack[110];
bool flag[110];
//max1[i][j]表示i 到j路径上最大边的值
void prim()
{
memset(flag,false,sizeof(flag));
flag[1]=0;
for(int i=2;i<=n;i++)
{
pre[i]=1;
d[i]=g[1][i];
}
d[1]=0;
ans=0;
int top=0;
stack[top++]=1;//保存最小生成树上的节点
for(int i=1;i<n;i++)
{
int tmp=INT_MAX,t=1;
for(int j=2;j<=n;j++)
{
if(!flag[j]&&d[j]!=-1&&d[j]<tmp)
t=j,tmp=d[j];
}
flag[t]=true;
ans+=tmp;
for(int i=0;i<top;i++)//求出当前找到的节点到最小生长树上每一个点的路径上的最大边
max1[stack[i]][t]=max1[t][stack[i]]=max(max1[pre[t]][stack[i]],tmp);
stack[top++]=t;
for(int j=1;j<=n;j++)
{
if(!flag[j]&&g[t][j]!=-1&&(g[t][j]<d[j]||d[j]==-1))
{
d[j]=g[t][j];
pre[j]=t;//记录直接前驱
}
}
}
}
int main()
{
int cas;
int a,b,c;
scanf("%d",&cas);
while(cas--)
{
memset(g,-1,sizeof(g));
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d %d %d",&a,&b,&c);
g[a][b]=g[b][a]=c;
}
memset(max1,-1,sizeof(max1));
prim();
int min1=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]&&g[i][j]>0)
min1=min(g[i][j]-max1[i][j],min1);
if(min1) cout<<ans<<endl;
else cout<<"Not Unique!"<<endl;
}
return 0;
}
判断方法二:
#include<cstdio>
#include<cstdlib>
#define N 110
const int inf=10000000;
int n,m,a[N][N],d[N],v[N],s,change[N];
void init(){
scanf("%d %d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
a[i][j]=inf-1;
for (int i=0;i<m;i++){
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
a[x][y]=a[y][x]=z;
}
for (int i=1;i<=n;i++)
v[i]=0,d[i]=inf,change[i]=0;
return;
}
bool solve(){
s=0;
d[1]=0;
for (int t=0;t<n;t++){
int min=inf,k=0;
for (int i=1;i<=n;i++)
if (!v[i]&&d[i]<min){
min=d[i];
k=i;
}
if (!k) break;
v[k]=1;
if (change[k]>=2) return 0;
s+=min;
for (int i=1;i<=n;i++)
if (!v[i])
if (a[k][i]<d[i])d[i]=a[k][i],change[i]=1;
else if (a[k][i]==d[i])
change[i]++;
}
return 1;
}
int main(){
int tests;
scanf("%d",&tests);
for (int i=0;i<tests;i++){
init();
if (solve()) printf("%d\n",s);
else printf("Not Unique!\n");
}
}