Prim輸出無向圖中所有的最小生成樹
阿新 • • 發佈:2019-01-08
思路:
給出n個頂點m條邊。一棵最小生成樹中有n-1條邊,所以在m條邊中選n-1條邊判斷能否構成最小生成樹,如果能則直接輸出。
判斷是否能構成最小生成樹的條件是 當前的n-1條邊的權值的和是否是最小生成樹的權值的和(程式碼裡的ans)。
步驟:
先進行一次prim,只是為了求出MST的權值ans。
再對m條邊進行深搜,當選中n-1條邊時判斷是否能構成最小生成樹。
程式碼:
#include<stdio.h>
#include<string.h>
#include<string>
#include<algorithm>
#include<map>
#include<vector>
#define INF 0x3f3f3f3f
#define Max 1010
using namespace std;
int n,m,tt,ans;//頂點個數 邊的個數 輸出計數 MST的權值
struct edge
{
int u,v,w;
} Q[Max];
int mp[Max][Max],dis[Max],book[Max];
int used[Max][Max],pre[Max];
int out[Max],in[Max];
void print(int n) // 輸出最小生成樹函式;
{
printf("第%d棵最小生成樹:\n",tt++);
for(int j=0;j<n;j++)
for(int i=0;i<=j;i++)
{
if(i==j)
printf("%d - %d:%d\n",out[i],in[i],mp[out[i]][in[i]]);
else printf("%d - %d:%d,",out[i],in[i],mp[out[i]][in[i]]);
}
}
int prim(int u,int s)//s表示狀態
{
memset(book,0,sizeof(book));
int sum=0,nn=0;
for(int i=1;i<=n;i++)
{
//初始為1到能到的頂點的距離
if(used[u][i]==s) dis[i]=mp[u][i];//在n-1條邊中起點為1的邊
else dis[i]=INF;//其餘的邊
pre[i]=u;
}
book[u]=1;
for(int i=1;i<=n-1;i++)
{
int minn=INF;
for(int j=1;j<=n;j++)
{
if(dis[j]<minn&&book[j]==0)
{
minn=dis[j];
u=j;
}
}
if(minn==INF) return -1;
book[u]=1;
sum+=dis[u];
out[nn]=pre[u];
in[nn++]=u;
for(int k=1;k<=n;k++)
{
if(mp[u][k]<dis[k]&&book[k]==0&&used[u][k]==s)
{
dis[k]=mp[u][k];
pre[k]=u;
}
}
}
//if(nn==n-1)
// prf(nn);
return sum;
}
void init() // 初始化
{
memset(used,0,sizeof(used));
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
if(i==j) mp[i][j]=0;
else mp[i][j]=mp[j][i]=INF;
}
}
}
void dfs(int top,int x,int sum)//當前選中的邊,當前樹中有幾條邊和權值
{
if(sum>ans||x>=n) return;
if(top==m)
{
if(x==n-1&&sum==ans)
{
int cnt=prim(1,1);
if(cnt==ans) print(n-1);//如果權值相同則輸出n-1條邊
}
return;
}
edge tt=Q[top];
used[tt.u][tt.v]=used[tt.v][tt.u]=1;//選中這條邊
dfs(top+1,x+1,sum+tt.w);
used[tt.u][tt.v]=used[tt.v][tt.u]=0;//取消標記
dfs(top+1,x,sum);//不選這條邊
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
int u,v,w;
for(int i=0; i<m; i++)
{
scanf("%d%d%d",&u,&v,&w);
if(w<mp[u][v]) mp[u][v]=mp[v][u]=w;
Q[i].u=u;
Q[i].v=v;
Q[i].w=w;
}
tt=1;
ans=prim(1,0);
if(ans==-1) printf("沒有最小生成樹,請重新輸入:\n");
else dfs(0,0,0);
}
return 0;
}
/*
思路:
第一次prim只是為了求最小生成樹的權值用來剪枝(期間沒有更新used)
深搜:在m條邊中選n-1條邊判斷是否能構成最小生成樹
*/