1. 程式人生 > >[枚舉]Luogu P1268 樹的重量

[枚舉]Luogu P1268 樹的重量

load 輸出格式 對角線 最短路徑 技術 pos -o 定義 我們

題目描述

樹可以用來表示物種之間的進化關系。一棵“進化樹”是一個帶邊權的樹,其葉節點表示一個物種,兩個葉節點之間的距離表示兩個物種的差異。現在,一個重要的問題是,根據物種之間的距離,重構相應的“進化樹”。

令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-l行,給出的是矩陣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

題解

  • 盜用一張圖先技術分享圖片

  • 這張圖的樹的重量為(1,2)+藍色部分
  • 那麽藍色部分怎麽求?
  • 發現,(1,3)和(2,3)都是經過藍色部分的
  • 把它們相加發現:(1,2)+2*藍色部分
  • 而且已知(1,2),藍色部分就用容易求
  • 用字母表示就是:((1,i)+(j,i)-(1,j))/2
  • 解決這個問題後,考慮怎麽構造樹
  • 這棵樹要滿足:
  • ①符合矩陣 ②每條邊都要加一次,而且最多一次
  • 假如我們前i-1個都構造完了,那麽怎麽構造i個
  • 題目要求重量要盡量小,顯然就枚舉加入哪條邊中,取最小值
  • Ojbk!!

代碼

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace
std; 4 const int inf=(1<<30); 5 int d[60][60],n; 6 int main() 7 { 8 while(scanf("%d",&n)==1&&n) 9 { 10 for(int i=1;i<=n;i++) 11 for(int j=i+1;j<=n;j++) 12 scanf("%d",&d[i][j]),d[j][i]=d[i][j]; 13 int ans=d[1][2]; 14 for(int i=3;i<=n;i++) 15 { 16 int t=inf; 17 for(int j=2;j<i;j++) t=min(t,(d[1][i]+d[j][i]-d[1][j])/2); 18 ans+=t; 19 } 20 printf("%d\n",ans); 21 } 22 return 0; 23 }

[枚舉]Luogu P1268 樹的重量