1. 程式人生 > >poj2054 Color a Tree

poj2054 Color a Tree

題解 tex 可謂 int std aps get max 錯誤

神題。這題是巨毒瘤...

自己寫真可謂是:

排空馭氣奔如電,上天入地求之遍

上窮碧落下黃泉,兩處茫茫皆不見

由於我們知道:不是樹形時,不停選值最大的節點可以得到最小代價。

那麽我們就能想出一個錯誤的貪心:每次從能選的中選出最大的。

下面我們來構造反例:


root

↙ ↘

1 2

100


貪心:2 + 2 + 300 = 304

實際:1 + 200 + 6 = 207

那麽我們該如何處理呢?

完全搞不倒啊!看題解看題解

得出如下結論:最大的節點一定是緊隨著父節點染色的。

下一步:沒有下一步....?????

我們要把這兩個節點合並!

等效權值為平均數!

很遺恨的是,我只會證明兩個點時權值是平均數。更多的還有待考證...

不過做題就是要靠想象!這就是正確的!

所以我們在代碼實現的難題上繼續敗北......

我的想法是每個節點記錄所有子節點以及所有合並過來的節點的順序。用兩個vector來實現。

然後那個堆是最困擾我的地方,實現起來困難重重。

看一看標程:cnm!直接n2暴力!

1000 * 1000 ......我覺得還行

然後我還發現了一件事:它沒有記錄次序!合並的同時更新ans!

驚為天人!

代碼要簡潔高效。這一點在寒假時我就有體會,現在又有這種感覺。

技術分享圖片
 1 /**
 2 poj 2054
 3 */
 4 #include <cstdio>
 5
using namespace std; 6 typedef long long LL; 7 const int N = 1010; 8 const double eps = 1e-10; 9 10 struct Node { 11 int tot, n, fa; 12 bool del; 13 double val; 14 }node[N]; 15 16 int n, R; 17 18 inline int find() { 19 double maxval = -1; 20 int ans = 0; 21 for(int i = 1; i <= n; i++) {
22 if(!node[i].del && i != R && node[i].val - maxval > eps) { 23 maxval = node[i].val; 24 ans = i; 25 } 26 } 27 return ans; 28 } 29 30 inline int getfa(int k) { 31 while(node[k].del) { 32 k = node[k].fa; 33 } 34 return k; 35 } 36 37 int main() { 38 scanf("%d%d", &n, &R); 39 while(n || R) { 40 LL ans = 0; 41 node[R].fa = 0; 42 for(int i = 1; i <= n; i++) { 43 scanf("%d", &node[i].tot); 44 node[i].val = (double)node[i].tot; 45 node[i].n = 1; 46 node[i].del = 0; 47 ans += node[i].tot; 48 } 49 for(int i = 1; i < n; i++) { 50 int x, y; 51 scanf("%d%d", &x, &y); 52 node[y].fa = x; 53 } 54 //printf("\n"); 55 for(int i = 1; i < n; i++) { 56 int s = find(); 57 int f = getfa(node[s].fa); 58 //printf("%d %d\n", s, f); 59 ans += node[f].n * node[s].tot; 60 node[f].n += node[s].n; 61 node[f].tot += node[s].tot; 62 node[f].val = (double)(node[f].tot) / node[f].n; 63 //printf("%f\n", node[f].val); 64 node[s].del = 1; 65 } 66 printf("%I64d\n", ans); 67 scanf("%d%d", &n, &R); 68 } 69 return 0; 70 }
AC代碼

poj2054 Color a Tree