1. 程式人生 > >UVa 1354 天平難題 (枚舉二叉樹)

UVa 1354 天平難題 (枚舉二叉樹)

分析 d+ ring dep ret ems ges img 2-2

題意:

技術分享

分析:

其實剛看到這題的時候覺得很難, 以至於結束了第七章然後去做了一遍第六章樹的部分。現在再做這題覺得思路並不是太難,因為總共就只有六個結點,那麽只要枚舉二叉樹然後算出天平然後再從葉子往上推就能得出這棵樹的寬度。這題我覺得主要難點是如何去枚舉二叉樹,其實這就是回溯法的核心。先去dfs選這個作為結點的, 然後還原, 再做不選的dfs, 這樣就能沒有遺漏(但會有重復)地枚舉二叉樹了。

這題還有個細節是一個天平中,左子樹的右長度可能會超過天平右臂 + 右子樹的長度, 如下圖

技術分享

那麽就不能單純地看右臂+右子樹的長度了, 要取一個最大值作為這個天平的最右, 反過來的左邊也是一樣的。

 1 #include <cstdio>
 2
#include <iostream> 3 #include <cstring> 4 using namespace std; 5 struct Tree{ 6 double L, R; 7 Tree(int _L = 0, int _R = 0):L(_L), R(_R){} 8 }; 9 Tree tree[1<<6]; 10 int w[1<<6], vis[1<<6]; 11 12 int n, ind; 13 double room, maxw; 14 15 int dfs(int dep){
16 if(dep == n - 1){// 遞歸邊界 17 if(tree[ind].L + tree[ind].R <= room) 18 maxw = max(maxw, tree[ind].L + tree[ind].R); 19 } 20 for(int i = 1; i <= ind; i++){ 21 if(!vis[i]){ 22 for(int j = 1; j <= ind; j++){ 23 if(i != j && !vis[j]){
24 25 vis[i] = vis[j] = 1;//建樹 26 w[++ind] = w[i] + w[j]; 27 28 int wl = w[i], wr = w[j]; 29 // a | b 30 // ---------- 31 // | | 32 // wl wr 33 34 double a = (double)wr/(wl + wr); 35 double b = 1.0 - a; 36 37 tree[ind].R = max(b + tree[j].R, tree[i].R - a);//比較著取最大值, 38 tree[ind].L = max(a + tree[i].L, tree[j].L - b); 39 40 dfs(dep + 1); 41 42 vis[i] = vis[j] = vis[ind] = 0;//還原 43 tree[ind].R = tree[ind].L = 0; 44 --ind; 45 } 46 } 47 } 48 } 49 } 50 int main(){ 51 int T; 52 scanf("%d", &T); 53 while(T--){ 54 memset(tree,0,sizeof(tree)); 55 ind = 0;//秤砣數組下標 56 scanf("%lf", &room); 57 scanf("%d", &n); 58 for(int i = 1; i <= n;i++){ 59 scanf("%d", &w[i]); 60 ind++; 61 } 62 maxw = -1; 63 memset(vis,0,sizeof(vis)); 64 dfs(0); 65 if(maxw == -1) 66 printf("-1\n"); 67 else 68 printf("%.16f\n", maxw); 69 } 70 return 0; 71 }

UVa 1354 天平難題 (枚舉二叉樹)