1. 程式人生 > 其它 >prim演算法求最小生成樹_1299 最小比例 (最小生成樹)

prim演算法求最小生成樹_1299 最小比例 (最小生成樹)

技術標籤:prim演算法求最小生成樹

題目描述

圖中共有N個點的完全圖,每條邊都有權值,每個點也有權值。要求選出M個點和M-1條邊,構成一棵樹,使得:

cac209f8cfa35d3bd3677c2e1b073c74.png

即所有邊的權值比所有點的權值之和的比率最小。

給定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;}