判斷唯一最小生成樹(次小生成樹)
阿新 • • 發佈:2019-01-31
先說一下最小生成樹的思路 將邊按長度從小到大排序 依次判斷 如果會出現環就跳過這條邊(用到並查集) 否則新增 直到新增到n-1條邊 就是最小生成樹
至於次小生成樹 先提供第一種思路
先建成最小生成樹 顯然繼續再新增任意一條邊都會形成環 倘若新添的這條邊與形成的環中除新添的邊以外的最大的邊長度相等 說明最小生成樹不唯一 列舉一下就好(求這條邊減環中除它以外的最大值就可以求次小生成樹)重點就是怎麼求最小生成樹中任意兩點間的邊的最大值
上程式碼:
提供判斷唯一最小生成樹的另一種思路 最小生成樹一開始只有一個點 每次尋找最小生成樹以外的離最小生成樹最近的點(即到離最小生成樹內最近的點的距離 取為最小值)倘若此刻這個最近的點到最小生成樹內所有的點的距離中出現了兩次或兩次以上的最小值 那麼說明最小生成樹不唯一 倘若此次列舉最小值出現了x次 那麼最小生成樹至少有x個 倘若每次枚舉出現的最小值的次數為x1,x2,x3,……xn-1那麼最小生成樹共有x1*x2*x3……*xn-1個。#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,m; struct node{ int x,y,w; }; node s[10100];//讀入的陣列 int fa[110],ss[110][110];//fa是並查集 ss是最小生成樹中任意兩點之間經過的路徑的邊的最大值 bool p[10100];//p[i]判斷第i條邊是否在最小生成樹內 bool cmp(node x,node y){ return x.w<=y.w; } int dfs(int h){ if(fa[h]==h)return h;//並查集 return fa[h]=dfs(fa[h]);//路徑壓縮 } int main(){ int i,j,k,t,r; scanf("%d",&t); for(k=1;k<=t;k++){ scanf("%d%d",&n,&m); int ans=0; memset(p,0,sizeof(p)); for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ ss[i][j]=999999999; } ss[i][i]=0; }//一開始將任意兩點之間的邊的最大距離設為無限大 自己與自己的最大距離為0 for(i=1;i<=m;i++){ scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].w); } sort(s+1,s+m+1,cmp); for(i=1;i<=n;i++)fa[i]=i;//未連邊之前 每個點都是一個獨立的集合 int f=1; for(i=0;i<n-1;f++){//一共有n個點 所以需要連線n-1條邊 迴圈n-1次 f指現在搜尋到第f條邊了 if(dfs(s[f].x)!=dfs(s[f].y)){ fa[dfs(s[f].x)]=s[f].y; ans+=s[f].w; ss[s[f].x][s[f].y]=s[f].w;//更新第f條邊連線的兩個點之間的距離 ss[s[f].y][s[f].x]=s[f].w; p[f]=1; i++; } }//之前基本就是KRUSCAL for(r=1;r<=n;r++){//思想類似與floyd 不會的看我部落格 for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ if(ss[i][j]==999999999 && ss[i][r]<999999999 && ss[j][r]<999999999 && i!=j){//一定要判斷ss[i][j] 不然會被多餘的替換 第一次替換的肯定是正確值 之後還會繼續替換 越來越大 一開始就wa在這裡了 ss[i][j]=max(ss[i][r],ss[j][r]); } } } }/這裡用的爆搜是O(n^3) 貌似可以用LCA優化到O(n)今天困了沒寫 想學的看我部落格 for(i=1;i<=m;i++){ if(!p[i] && ss[s[i].x][s[i].y]==s[i].w){ printf("Not Unique!\n"); ans=-1; break; } } if(ans==-1)continue; printf("%d\n",ans); } return 0; }