最小生成樹和次小生成樹定義:
生成樹:圖G的生成樹包含原圖中的全部頂點n,和n-1條邊.
最小生成樹就是所有生成樹中邊權和最小的.
2 那么如何求最小生成樹呢?
介紹兩種基於貪心的算法:
1.prim算法
設圖G =(V,E),其生成樹的頂點集合為U。
①、把v0放入U。
②、在所有u∈U,v∈V-U的邊(u,v)∈E中找一條最小權值的邊,加入生成樹。
③、把②找到的邊的v加入U集合。如果U集合已有n個元素,則結束,否則繼續執行②。
2.kruskal算法
首先將所有邊按邊權排序,然后按照邊權從小到大依次處理.這里要用到並查集的思想,假設已有點集U,現在正在處理邊i->j,如果i,j已在
U中則處理下一條邊,否則將i,j加入並查集中.繼續處理下一條邊,直到有n-1條邊為止.
3 次小生成樹
次小生成樹可由最小生成樹換一條邊得到
算法:
1)先用prim求出最小生成樹T,在prim的同時,用一個矩陣max[u][v]記錄在樹中連接u-v的路徑中權值最大的邊.
2)枚舉所有不在T中的邊u-v,加入邊u-v,刪除權值為max[u][v]的邊,不斷枚舉找到次小生成樹.
次小生成樹的模板:
#include<iostream>
using namespace std;
const int INF=0x3f3f3f3f;
int g[110][110],dist[110],mmax[110][110];
int pre[110];
bool mark[110];
bool connect[110][110];
int mst,mint;
int n,m;
int prim()
{
int res=0,fa,p,min,i,j;
memset(mmax,0,sizeof(mmax));
for(i=1;i<=n;i++)
{
dist[i]=g[1][i];
pre[i]=1;
mark[i]=false;
}
dist[1]=0;
mark[1]=true;
for(i=1;i<n;i++)
{
p=-1;min=INF;
for(j=1;j<=n;j++)
{
if(!mark[j]&&dist[j]<min)
{
p=j;
min=dist[j];
}
}
if(p==-1) return res;
mark[p]=true;
res+=dist[p];
fa=pre[p];
connect[fa][p]=false;
connect[p][fa]=false;
mmax[fa][p]=min;
for(j=1;j<=n;j++)
mmax[j][p]=(mmax[fa][p]>mmax[j][fa])?mmax[fa][p]:mmax[j][fa];
for(j=1;j<=n;j++)
{
if(!mark[j]&&dist[j]>g[p][j])
{
dist[j]=g[p][j];
pre[j]=p;
}
}
}
return res;
}
int main()
{
int tc;
//freopen("1.txt","r",stdin);
scanf("%d",&tc);
while(tc--)
{
scanf("%d %d",&n,&m);
memset(g,INF,sizeof(g));
memset(connect,false,sizeof(connect));
while(m--)
{
int u,v,c;
scanf("%d %d %d",&u,&v,&c);
g[u][v]=c;
g[v][u]=c;
connect[u][v]=true;
connect[v][u]=true;
}
mst=prim();
int i,j;
bool flag=false;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
if(connect[i][j]==false||g[i][j]==INF)
continue;
if(g[i][j]==mmax[i][j])
{
flag=true;
break;
}
}
if(flag)
printf("Not Unique!\n");
else
printf("%d\n",mst);
}
return 0;
}
