• UVa 1354 天平难题 枚举二叉树


    题意:给出房间宽度 r 和 s 个挂坠的重量 wi,设计一个尽量宽的天平,挂着所有挂坠。天平由一些长度为 1 的木棍组成,木棍的每一端要么挂一个挂坠,要么挂另外一个木棍。

    这题卡了很久,看了很多大神的代码,终于把细节都搞懂了。

    将挂坠所有可能的集合的重量算出来,再用二进制的方式枚举子集(左子树)和补集(右子树)构成二叉树,算出左右的宽度。

    代码:

    #include <iostream>
    #include <cstring>
    #include <vector>
    using namespace std;
    
    const int MAX = 6;
    
    struct Tree{
        double L, R;    //树的左边和右边的长度
        Tree():L(0), R(0){}
    };
    
    double width, w[MAX], sum[1<<MAX];
    int vis[1<<MAX];
    vector<Tree> tree[1<<MAX];        //枚举出来的结点可以组合成多个树
    
    void dfs(int subset);        //枚举二叉树
    
    int main(){
        freopen("input.txt", "r", stdin);
        int T;
        cin >> T;
        
        while(T--){
            int s;
            scanf("%lf%d", &width, &s);
            for(int i=0; i<s; i++){
                scanf("%lf", &w[i]);
            }
            
            //初始化 
            memset(sum, 0, sizeof(sum));
            for(int i=1; i<(1<<s); i++){
                tree[i].clear();    //清空树
                int k = 1;
                for(int j=0; j<s; j++){    //算出当前集合所有结点的重量和
                    if(k & i){
                        sum[i] += w[j];
                    }
                    k = (k << 1);
                } 
            }
            
            //进行枚举
            int root = (1<<s) - 1;
            memset(vis, 0, sizeof(vis));
            dfs(root);
            
            //得到最大的答案
            double ans = -1;
            for(int i=0; i<tree[root].size(); i++){
                ans = max(tree[root][i].L + tree[root][i].R, ans);
            } 
            printf("%.10lf
    ", ans);
            
        }
        
    } 
    
    void dfs(int subset){
        if(vis[subset])
            return;
        vis[subset] = 1;
        
        bool hasChild = false;
        
        for(int left = ((subset-1) & subset); left > 0; left = ((left-1) & subset)){    //枚举集合 
            hasChild = true;
            
            int right = subset ^ left;        //left 的补集
            
            double l = sum[right] / sum[subset];    //左臂长度
            double r = sum[left] / sum[subset];        //右臂长度
            
            dfs(left);    dfs(right);        //把左右树的子树也枚举出来
            for(int i=0; i<tree[left].size(); i++){            //将枚举出的左右子树组合起来 
                for(int j=0; j<tree[right].size(); j++){
                    Tree t;
                    t.L = max(tree[left][i].L + l, tree[right][j].L - r);
                    t.R = max(tree[right][j].R + r, tree[left][i].R - l);
                    if(t.L + t.R < width){                    //如果满足条件,枚举结果加一 
                        tree[subset].push_back(t); 
                    }
                }
            }
        }
        
        if(!hasChild){        //叶子结点的左右臂为零 
            tree[subset].push_back(Tree());
        }
        
    }
  • 相关阅读:
    Hdu1711 Number Sequence--Kmp模板题
    Trie入门--Poj3630 Phone List,查单词,HDU1251 统计前缀,PKU2503 Babelfish
    高次幂的组合数表示形式
    BZOJ1697 [Usaco2007 Feb] Cow Sorting牛排序
    1025 [SCOI2009]游戏(置换群,DP)
    Poj1721 Cards
    [Poi2003]Shuffle
    poj 3128 Leonardo's Notebook(置换的幂)
    POJ3734 Block母函数入门
    重心拉格朗日插值法
  • 原文地址:https://www.cnblogs.com/lighter-blog/p/7216349.html
Copyright © 2020-2023  润新知