prim演算法求最小生成樹_1299 最小比例 (最小生成樹)
阿新 • • 發佈:2021-01-05
技術標籤:prim演算法求最小生成樹
題目描述
圖中共有N個點的完全圖,每條邊都有權值,每個點也有權值。要求選出M個點和M-1條邊,構成一棵樹,使得:
即所有邊的權值比所有點的權值之和的比率最小。
給定N和M,以及N個點的權值,和所有的邊權,要求M個點的最小比率生成樹。
輸入
第一行包含兩個整數N和M(2<=N<=15,2<=M<=N),表示點數和生成樹的點數。
接下來一行N個整數,表示N個點的邊權。
最後N行,每行N列,表示完全圖中的邊權。所有點權和邊權都在[1,100]之間。
輸出
輸出最小比率生成樹的M個點。當答案出現多種時,要求輸出的第一個點的編號儘量小,第一個相同,則第二個點的編號儘量小,依次類推,中間用空格分開。編號從1開始。
樣例輸入
3 2
30 20 10
0 6 2
6 0 3
2 3 0
樣例輸出
1 3
思路
題目要求(邊權和/點權和)最小,當m個點確定時,點權和是固定的,只能邊權和最小,即用最小生成樹的邊構成邊權和。
n和m的資料規模都很小,可以暴力dfs產生C[N][M]的組合方案,然後再用prim演算法求最小生成樹,即可求出每組方案的答案。
最後用陣列記錄最優答案,兩個除法比較時,可以用交叉相乘替代除法(交叉相乘時注意資料範圍,有時要用long long),避免精度誤差。
#includeusing namespace std;int n,m,v[25],g[25][25],ans1,ans2,t[25],a[25];void prim(){ int s1=0,s2=0,mark[25],d[25]; //s1表示點權和,s2表示邊權和 for(int i=1; i<=n; i++) mark[i]=1,d[i]=1e9; for(int i=1; i<=m; i++) s1+=v[t[i]]; for(int i=2; i<=m; i++) d[t[i]]=g[t[1]][t[i]],mark[t[i]]=0; for(int i=2; i<=m; i++){ //prim演算法 int x=0,y=1e9; for(int i=1; i<=n; i++) if(mark[i]==0 && d[i] x=i; y=d[i]; } mark[x]=1; s2+=y; for(int i=1; i<=n; i++) if(mark[i]==0 && g[x][i] } if(s2*ans1 //if(s2/s1 ans1=s1; ans2=s2; for(int i=1; i<=m; i++) a[i]=t[i]; }}void dfs(int i,int num){ //產生C[n][m]的全排列 if(num==m){ prim(); return; } if(i>n) return; t[num+1]=i; dfs(i+1,num+1); dfs(i+1,num);}int main(){ scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) scanf("%d",&v[i]); for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) scanf("%d",&g[i][j]); ans1=1, ans2=1e4; dfs(1,0); for(int i=1; i<=m; i++) printf("%d ",a[i]); return 0;}