UVA 1354 困难的秤 位运算枚举子集

题意:给你一个房间的宽度和n个挂坠的重量,在满足力矩平衡的条件下,问挂完这些挂坠不超过房间宽度的最长木棍长度之和是多少。注:每根木棍长度为一,且两端只能悬挂挂坠或者另一个木棍,这里的木棍不能水平拼接!只能单独使用!

思路:因题目数据小,所以 - 〉枚举二叉树,由小树一步步合并为大树,从顶部向下回溯,有点区间dp的感觉

收获技巧:

1.位运算如何枚举二叉树?

我们用二进制对应i位上表示这个子树挂不挂第i个挂坠,然后枚举这个二进制的所有子集为左子树,那么他的对称差集就是右子树,然后构成这棵子树的不同树形,每一种合格的树形都存在tree[这个二进制集合]里,那么同理,最后tree[全集]存的就是挂坠全挂上的时候的所有树形,这时候只要枚举每一个树形的lift+right长度,取最大的即可!

2.对于每一棵子树怎么求他的长度?

tree[father].L = l2+ l1(而l是与一侧挂坠与总重的比例)同理右侧

tree[father].L = l3 - r1

所以要综合判断: t.L = max(tree[lefted][i].L+lf,tree[righted][j].L-lr);

                          t.R = max(tree[righted][j].R+lr,tree[lefted][i].R-lf);

3.如何枚举子集?

for(int lefted = (father-1)&father;lefted;lefted = (lefted-1)&father)

int righted = father^lefted;构造差集

代码:

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
double r;
int n;
struct Tree
{
    double L,R;
    Tree(double a = 0,double b = 0):L(a),R(b) {}
};
double w[6];
double sum[150];
bool vis[150];
vector<Tree> tree[150];
void dfs(int father){
    if(vis[father])return;
    vis[father] = 1;//剪枝,已经判断了的直接结束
    bool judge = false;//是否有子集
    for(int lefted = (father-1)&father;lefted;lefted = (lefted-1)&father)//枚举子集
    {
        judge = true;
        int righted = father^lefted;//取差集
        double lf = sum[righted]/sum[father];//左子集在father杆的左侧长度
        double lr = sum[lefted]/sum[father];//右子集在father杆的右侧长度
        dfs(righted);
        dfs(lefted);//下面构造father树下所有可能的由左右子集构成的树形
        for(int i = 0;i<tree[lefted].size();i++){//左子树的树形
            for(int j = 0;j<tree[righted].size();j++){//右字树所有可能的树形
                Tree t;//构造father其中一个树形
                t.L = max(tree[lefted][i].L+lf,tree[righted][j].L-lr);//判断长度,取最大
                t.R = max(tree[righted][j].R+lr,tree[lefted][i].R-lf);
                if(t.L+t.R<r)tree[father].push_back(t);
            }
        }
    }
    if(!judge)
        tree[father].push_back(Tree(0,0));
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%lf%d",&r,&n);
        memset(vis,0,sizeof(vis));
        memset(sum,0,sizeof(sum));
        for(int i = 0;i<n;i++){
            scanf("%d",&w[i]);
        }
        int root = (1<<n) - 1;
        for(int i = 0;i<=root;i++){
                tree[i].clear();
            for(int j = 0;j<n;j++){
                if(i&(1<<j))sum[i]+=w[j];//所有子集总重量
            }
        }
        dfs(root);
        double ans = -1;
        for(int i = 0;i<tree[root].size();i++){
            ans = max(ans,tree[root][i].L+tree[root][i].R);
        }
        printf("%.10lf\n",ans);
    }
    return 0;
}



猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/80354622
今日推荐