P1268 樹的重量
題目描述
樹可以用來表示物種之間的進化關系。一棵“進化樹”是一個帶邊權的樹,其葉節點表示一個物種,兩個葉節點之間的距離表示兩個物種的差異。現在,一個重要的問題是,根據物種之間的距離,重構相應的“進化樹”。
令N={1..n},用一個N上的矩陣M來定義樹T。其中,矩陣M滿足:對於任意的i,j,k,有M[i,j] + M[j,k] >= M[i,k]。樹T滿足:
1.葉節點屬於集合N;
2.邊權均為非負整數;
3.dT(i,j)=M[i,j],其中dT(i,j)表示樹上i到j的最短路徑長度。
如下圖,矩陣M描述了一棵樹。
樹的重量是指樹上所有邊權之和。對於任意給出的合法矩陣M,它所能表示樹的重量是惟一確定的,不可能找到兩棵不同重量的樹,它們都符合矩陣M。你的任務就是,根據給出的矩陣M,計算M所表示樹的重量。下圖是上面給出的矩陣M所能表示的一棵樹,這棵樹的總重量為15。
輸入輸出格式
輸入格式:
輸入數據包含若幹組數據。每組數據的第一行是一個整數n(2<n<30)。其後n-1行,給出的是矩陣M的一個上三角(不包含對角線),矩陣中所有元素是不超過100的非負整數。輸入數據保證合法。
輸入數據以n=0結尾。
輸出格式:
對於每組輸入,輸出一行,一個整數,表示樹的重量。
輸入輸出樣例
輸入樣例#1:5
5 9 12 8
8 11 7
5 1
4
4
15 36 60
31 55
36
0
輸出樣例#1: 15
71
Solution:
本題很有意思的思維題(話說期末考崩了啊,本來想數學拉分的,結果數學也炸了,好崩心態啊!滾粗搞競賽!)。
題意等價於給定一棵樹的$n$個葉子節點之間的最短路徑,且$d[i][j]+d[j][k]\geq d[i][k]$,需要求出滿足條件的樹的邊權和(我們不必在乎點具體所在位置,只需考慮長度的貢獻)。
首先不難想到的是當只有兩個葉子節點時,顯然答案為$d[1][2]$。
而當$n==3$時,因為要滿足標號節點均為葉子節點,所以多出的$3$號葉子節點必定只能由$G[1][2]$之間多出一條分枝相連,如圖:
那麽多出的分枝長度顯然為$\frac{d[1][3]+d[2][3]-d[1][2]}{2}$。
當$n==4$時,還是得滿足均為葉子節點的限制,所以多出的節點顯然只能在$G[1][2]$或$G[1][3]$或$G[2][3]$這三條分枝上再接一條分枝與$4$相連,如圖:
容易發現,此時多出的長度為$min(\frac{d[1][4]+d[2][4]-d[1][2]}{2},\frac{d[1][4]+d[3][4]-d[1][3]}{2},\frac{d[2][3]+d[3][4]-d[2][4]}{2})$。
不難發現每次多出的$n$號節點,需要枚舉在前$n-1$個節點互相之間的路徑上多出分枝的情況,取最小值累加。
那麽實現時直接模擬就好了。
代碼:
1 #include<bits/stdc++.h> 2 #define il inline 3 #define ll long long 4 #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) 5 #define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--) 6 using namespace std; 7 int n,mp[31][31],ans,tp; 8 9 il int gi(){ 10 int a=0;char x=getchar(); 11 while(x<‘0‘||x>‘9‘)x=getchar(); 12 while(x>=‘0‘&&x<=‘9‘)a=(a<<3)+(a<<1)+x-48,x=getchar(); 13 return a; 14 } 15 16 int main(){ 17 while(scanf("%d",&n)!=EOF&&n){ 18 For(i,1,n-1) For(j,i+1,n) mp[i][j]=mp[j][i]=gi(); 19 ans=mp[1][2]; 20 For(i,3,n){ 21 tp=0x7fffffff; 22 For(j,1,i-1) For(k,1,j-1) 23 tp=min(tp,(mp[j][i]+mp[k][i]-mp[j][k])>>1); 24 ans+=tp; 25 } 26 printf("%d\n",ans); 27 } 28 return 0; 29 }
P1268 樹的重量