UVa1354 ——天平难题

考察回溯法的题目。

难点在于如何枚举天平结构的各种情况。

思路1:自底向上,用类似二叉树的结构储存(类似霍夫曼树,挂坠全部在叶节点),每次选择两个节点组成一个子树同时算出子树的左右臂长度,递归建树。但是这样会有较多重复的情况。

思路2:自顶向下,把集合分为左右子集(分别为左右子树所含的挂坠集合),在递归调用左右子集。枚举子集的思路用的是二进制枚举集合的思路,每个二进制数分别对应挂坠集合能组成的所有天平的左右臂长度,用vector<Node> node[MAXN]储存,[]内是二进制数。还用到了二进制&,^运算来处理集合间的关系。

思路2的代码。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<cassert>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cstring>
#include<functional>
using namespace std;
#define INF 0x3f3f3f3f
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N = 6;
const int MAXN = (1 << N);
int t, n, i, j, vis[MAXN];
double w[N], sumw[MAXN], r;
struct Node {
    double l, r;
    Node() {}
    Node(double ll, double rr) { l = ll; r = rr; }
};
vector<Node> node[MAXN];

int bitcount(int x) {  //计算二进制中1的个数
    if (x == 0) return 0;
    return bitcount(x / 2) + (x & 1);
}

void dfs(int s) {
    if (vis[s]) return;//添加了记忆数组,如果状态s已经被搜索过,直接返回
    vis[s] = 1;
    if (bitcount(s) == 1) {  //当只有一个1时,说明是叶子,天平的两臂都是0
        node[s].push_back(Node(0, 0));
        return;
    }
    for (int l = (s - 1)&s; l > 0; l = (l - 1)&s) { //枚举左右子集情况,(此处利用二进制枚举左右子集的方法值得学习)
        int r = s^l;
        dfs(l); dfs(r);
        for (int i = 0; i < node[l].size(); i++) {
            for (int j = 0; j < node[r].size(); j++) {
                double ll = min(-sumw[r] / (sumw[l] + sumw[r]) + node[l][i].l, sumw[l] / (sumw[l] + sumw[r]) + node[r][j].l);//比较 左臂+左子天平的左臂 与 右子天平的左臂-右臂  谁更小
                double rr = max(sumw[l] / (sumw[l] + sumw[r]) + node[r][j].r, -sumw[r] / (sumw[l] + sumw[r]) + node[l][i].r);//比较 右臂+右子天平的右臂 与 左子天平的右臂-左臂  谁更大
                node[s].push_back(Node(ll, rr));//将得到的该根节点的左右臂长度放入数组
            }
        }
    }
}

void solve() {
    double ans = -1;
    int s = (1 << n) - 1;
    dfs(s);
    for (int i = 0; i < node[s].size(); i++) {
        if (node[s][i].r - node[s][i].l < r) {//s结点是根结点,存有所有二叉树的左右臂的长度,选出差值<r的最大值即可
            if (node[s][i].r - node[s][i].l > ans)
                ans = node[s][i].r - node[s][i].l;
        }
    }
    if (ans == -1) printf("-1\n");
    else printf("%.10lf\n", ans);
}

int main() {
    scanf("%d", &t);
    while (t--) {
        memset(vis, 0, sizeof(vis));
        memset(node, 0, sizeof(node));
        scanf("%lf%d", &r, &n);
        for (i = 0; i < n; i++)
            scanf("%lf", &w[i]);
        for (i = 0; i < (1 << n); i++) {
            sumw[i] = 0;
            for (j = 0; j < n; j++) {
                if (i&(1 << j))
                    sumw[i] += w[j];
            }
        }
        solve();
    }
    return 0;
}

原文地址:https://www.cnblogs.com/loganlzj/p/9419875.html

时间: 2024-10-20 11:40:42

UVa1354 ——天平难题的相关文章

uva1354 天平难题 解题报告

uva1354 天平难题.主要有 回溯法,二叉树模拟. 当然,这道题也有很多剪枝,但是这个用二叉树性质模拟的数组应该过了,这样写,这道题,完全就足够了. 原题目链接:https://uva.onlinejudge.org/external/13/1354.pdf 题目大意: 就是首先给你一个房间的宽度r,之后有s个挂坠,第i个挂坠的重量是wi,设计一个尽量宽,但是不能宽过房间.的天平.当然会有好几组这样的数据. 这些天平棍的长度都为1,天平棍要么挂 挂坠,要么就继续挂木棍设挂的木棍必须要让天平平

UVa 1354 天平难题

首先呈现刘汝佳的高级代码 // UVa1354 Mobile Computing // Rujia Liu #include<cstdio> #include<cstring> #include<vector> using namespace std; struct Tree { double L, R; // distance from the root to the leftmost/rightmost point Tree():L(0),R(0) {} }; co

UVa 1354 天平难题 (枚举二叉树)

题意: 分析: 其实刚看到这题的时候觉得很难, 以至于结束了第七章然后去做了一遍第六章树的部分.现在再做这题觉得思路并不是太难,因为总共就只有六个结点,那么只要枚举二叉树然后算出天平然后再从叶子往上推就能得出这棵树的宽度.这题我觉得主要难点是如何去枚举二叉树,其实这就是回溯法的核心.先去dfs选这个作为结点的, 然后还原, 再做不选的dfs, 这样就能没有遗漏(但会有重复)地枚举二叉树了. 这题还有个细节是一个天平中,左子树的右长度可能会超过天平右臂 + 右子树的长度, 如下图 那么就不能单纯地

UVA 1354 Mobile Computing(天平难题,枚举子集,递归,好题*)

1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 5 /** 6 思路:在每一个根节点枚举左右子树 7 8 学习: 9 (1)枚举子集的方法 例如 枚举 s = 100101 的子集 10 for(int l = (s-1)&s , l > 0 ; l = (l-1) & s){ 11 int r = s ^ l; 12 (2)思考:如何表示 左边距离 右边距离 . 该点的

Mobile Computing-天平难题-Uva1354(回溯枚举二叉树)

原题:https://uva.onlinejudge.org/external/13/1354.pdf 有s块石头,每块都被一根绳子吊着,如果有两个及以上的石头,需要平衡的天平把所有的石头挂起来. 房间的宽度为r,问小于房间宽度r的天平的最大宽度. 分析: 是个回溯枚举的问题,枚举中途如果发现当前宽度已经大于r,回溯. 难点: 也可以说是亮点,就是枚举所有的二叉树,一个天平可以看成是一个二叉树. 具体点说递归建立二叉树的过程就是每次从包含所有节点的集合中选择两个节点,合二为一 所以我们建树的过程

1225 八数码难题

1225 八数码难题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 查看运行结果 题目描述 Description Yours和zero在研究A*启发式算法.拿到一道经典的A*问题,但是他们不会做,请你帮他们.问题描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765

“黑五”的本土化难题,如何定位、立足?

黑五,或者黑色星期五,对大部分中国人来说都会觉得很陌生,但这个在欧美如同双十一的购物节日已经开始被一部分中国消费者所接受并且积极的进行消费购物.虽然在国内黑五还无法与双十一相提并论,但随着跨境电商的兴起,黑五的本土化氛围正越来越浓. "黑五"关注度持续升温 从特定人群向多元化.年轻化转变 从市场层面的变化来看,这两年跨境电商的持续发展为黑五提供了基本的市场促销环境,虽然国内的黑五发展程度尚不足以与双十一相提并论,但对跨境电商企业而言,这几年已经逐渐向市场传达到了"黑五&quo

hdu 1251 统计难题(字典树)

Problem Description Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀). Input 输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串. 注意:本题只有一组测试数据,处理到文件结束. Output 对于每个提

计算机科学只存在两个难题:缓存失效和命名

计算机科学只存在两个难题:缓存失效和命名. 命名的艺术 “计算机科学只存在两个难题:缓存失效和命名.” ——Phil KarIton 前言 命名一直是我编程过程中很头痛的事,有时为了一个恰当的名称是想了又想,还忍不住Google一下.命名真是一门艺术,好的命名那叫一个高大上.今天总结一些前端命名的规范,梳理一份自己的命名想法,从以下4种代码风格的命名规范部分获取灵感: 1. Google JavaScript代码风格指南 2. Crockford代码规范 3. Dojo Javascript 编