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) {}
    };
    const int maxn = 6;
    int n, vis[1<<maxn];
    double r, w[maxn], sum[1<<maxn];
    vector<Tree> tree[1<<maxn];
    void dfs(int subset) {
      if(vis[subset]) return;
      vis[subset] = true;
      bool have_children = false;
      for(int left = (subset-1)&subset; left; left = (left-1)&subset) {
        have_children = true;
        int right = subset^left;
        double d1 = sum[right] / sum[subset];
        double d2 = 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 + d1, tree[right][j].L - d2);
            t.R = max(tree[right][j].R + d2, tree[left][i].R - d1);
            if(t.L + t.R < r) tree[subset].push_back(t);
          }
      }
      if(!have_children) tree[subset].push_back(Tree());
    }
    int main() {
      int T;
      scanf("%d", &T);
      while(T--) {
        scanf("%lf%d", &r, &n);
        for(int i = 0; i < n; i++) scanf("%lf", &w[i]);
        for(int i = 0; i < (1<<n); i++) {
          sum[i] = 0;
          tree[i].clear();
          for(int j = 0; j < n; j++)
            if(i & (1<<j)) sum[i] += w[j];
        }
        int root = (1<<n)-1;
        memset(vis, 0, sizeof(vis));
        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;
    }

这就是他的自顶向下构造。

接着提一下高效枚举

S表示一个01状态集,那么它的所有非空子集x可以通过以下代码枚举。

for (int x = S; x; x = (x-1)&S)

x = (x-1)&S实际上是把S中的0全部忽略,并不断减1的结果,比如S=1011,则x分别为:1011, 1010, 1001, 1000, 0011, 0010, 0001。忽略S中第二位的0其实就是111, 110, 101, 100, 011, 010, 001。

称S中的1所在位为有效位,0所在位为无效位,则x中的无效位必为0,有效位为0或1,比如S=1011,x=1001(有效位加下划线)。-1就是加上-1补码1111…,可以想成把无效位的1先加上去,比如x=1001变成1101,再加有效位的1。由于无效位加完肯定是1,会把有效位的进位“传递”下去,然后再位与S使得无效位变成0,实际就相当于有效位加上1111…,也就是有效位-1。

最后分析几个细节

  1. 54行两个循环算出了每个子集重量。
  2. 20、21行避免了重复枚举
  3. 27行用异或算出右子集
  4. 28、29行算出了天平杠杆距离
  5. 33-39行循环算出天平宽度集
  6. 63、64行算总宽度洁简了特判代码
总而言之,太巧妙了。

原文地址:https://www.cnblogs.com/autoint/p/9520690.html

时间: 2024-11-01 23:55:07

UVa 1354 天平难题的相关文章

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

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

uva1354 天平难题 解题报告

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

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)思考:如何表示 左边距离 右边距离 . 该点的

UVa 1354 Mobile Computing | GOJ 1320 不加修饰的天平问题

传送门1(UVa): https://uva.onlinejudge.org/external/13/1354.pdf 传送门2(GOJ): http://acm.gdufe.edu.cn/Problem/read/id/1320 题意: 长度限制 r (1 < r < 10), 给 n (1 <= n <= 6) 个砝码,组成平衡(考虑重量和力臂)的天平,求天平最长能多长. 2015个人选拔赛#6 1004 比赛的时候完全不知道怎么做,比赛完两天重新看一遍有点思路就是敲不出来(弱

UVa 1354 Mobile Computing[暴力枚举]

**1354 Mobile Computing** There is a mysterious planet called Yaen, whose space is 2-dimensional. There are many beautiful stones on the planet, and the Yaen people love to collect them. They bring the stones back home and make nice mobile arts of th

UVa 839 天平

原题链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=780 先建立二叉树,之后遍历. 1 #include<iostream> 2 using namespace std; 3 4 struct Node 5 { 6 int W1, D1, W2, D2; 7 Node *left; 8 Node *right; 9 };

uva 1354 Mobile Computing ——yhx

1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 struct node 5 { 6 int fat,lson,rson; 7 double wei; 8 }tree[500]; 9 double w[10],lim,ans; 10 int n; 11 double max(double x,double y) 12 { 13 return x>y?x:y; 14 } 15 void calc_s

Uva 1354 Mobile Computing

题目链接 题意: 在一个宽为r 的房间里, 有s个砝码, 每个天平的一端要么挂砝码, 要么挂另一个天平, 并且每个天平要保持平衡. 求使得所有砝码都放在天平上, 且总宽度不超过房间宽度的最大值. 思路: 每个节点只能有两个子节点, 这是一棵二叉树的形式. 通过枚举二叉树的形态, 再枚举每一个叶子节点所放砝码, 最后再计算每个方案的宽度并计算答案. 每增加一个天平, 那么可以放砝码数 + 1. note: 坑在0的输出了, 用primtf("%.9lf\n", 0)输出来的是0  用0.

Uva 839天平(二叉树dfs, 递归建树)

题意: 给定一个天平长度 输入格式为 wl dl wr dr 分别代表天平左边长度,左边重量, 右边长度, 右边重量. 如果重量为0, 说明下面还有一个天平, 递归给出. 样例输入:10 2 0 40 3 0 11 1 1 12 4 4 21 6 3 2 如果天平是左右重量相等的 输出YES 否则输出NO. 分析: 既然以递归的定义输入, 那么我们程序最好写成递归建树,如果有重量是0, 那么就递归建左子树或者右子树 , 如果是叶子节点的父节点(这个天平下面没天平), 那么就判断左右是否相等返回即