紫书 例题7-7 UVA 1354

【题意】给出了房间的宽度,天平长度为1,天平有左右两个空位,可以放重物或另一个天平,然后有n个重物放到天平上保持天平平衡的情况下问天平最宽延伸是多少。

【解题方法】 这道题十分的有意思,实在不知道怎么做,看了紫书的思路,仍然写不出代码,主要是不知道如何去枚举二叉树。查看了刘老师的标程,终于弄懂了、也学到了二叉树该如何去枚举。解决了这个问题就可以,每次枚举两个坠合并成一个坠,那么每次都会使得整个可选的坠的数目减一。

把每个坠都看成一个天平,因此有三个量需要维护:w,l,r(重量,左臂总长,右臂总长),同时在合并两个坠的时候要考虑特殊情况,左边的那个坠右臂特别长,或者右边的那个坠左臂特别长  。所以有这么一段:llx=max(a[i].l+l,a[j].l-r);  rrx=max(a[j].r+r,a[i].r-l); 为了便于来理解我画一个图:



【AC代码】


//
//Created by BLUEBUFF 2016/1/6
//Copyright (c) 2016 BLUEBUFF.All Rights Reserved
//

#pragma comment(linker,"/STACK:102400000,102400000")
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdio>
#include <time.h>
#include <cstdlib>
#include <cstring>
#include <sstream> //isstringstream
#include <iostream>
#include <algorithm>
using namespace std;
//using namespace __gnu_pbds;
typedef long long LL;
typedef pair<int, LL> pp;
#define REP1(i, a, b) for(int i = a; i < b; i++)
#define REP2(i, a, b) for(int i = a; i <= b; i++)
#define REP3(i, a, b) for(int i = a; i >= b; i--)
#define CLR(a, b)     memset(a, b, sizeof(a))
#define MP(x, y)      make_pair(x,y)
const int maxn = 6;
const int maxm = 2e5;
const int maxs = 10;
const int maxp = 1e3 + 10;
const int INF  = 1e9;
const int UNF  = -1e9;
const int mod  = 1e9 + 7;
int gcd(int x, int y) {return y == 0 ? x : gcd(y, x % y);}
//typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>order_set;
//head

struct node{
    double l, r;
    node(){}
    node(int l, int r) : l(l), r(r) {}
};

int n, vis[1<<maxn];
double rr, w[maxn], sum[1<<maxn];
vector <node> tree[1<<maxn];

void dfs(int s)
{
    if(vis[s]) return; //记忆化
    vis[s] = 1;
    bool leaf = 0;
    for(int l = s & (s - 1); l; l = (l - 1) & s){//枚举二叉树
        leaf = 1;
        int r = s ^ l;
        double d1 = sum[r] / sum[s];
        double d2 = sum[l] / sum[s];
        dfs(l);
        dfs(r);
        for(int i = 0; i < tree[l].size(); i++){
            for(int j = 0; j < tree[r].size(); j++){
                node tx;
                tx.l = max(tree[l][i].l + d1, tree[r][j].l - d2);
                tx.r = max(tree[r][j].r + d2, tree[l][i].r - d1);
                if((tx.l + tx.r) < rr) tree[s].push_back(tx);
            }
        }
    }
    if(!leaf) tree[s].push_back(node(0, 0));
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%lf%d", &rr, &n);
        REP1(i, 0, n) scanf("%lf", &w[i]);
        REP1(i, 0, (1 << n)){
            sum[i] = 0;
            tree[i].clear();
            REP1(j, 0, n){
                if(i & (1<<j)){
                    sum[i] += w[j];
                }
            }
        }
        memset(vis, false, sizeof(vis));
        int u = (1 << n) - 1;
        dfs(u);
        double ans = -1.0;
        REP1(i, 0, tree[u].size()){
            ans = max(ans, tree[u][i].l + tree[u][i].r);
        }
        printf("%.10f\n", ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值