码迷,mamicode.com
首页 > 其他好文 > 详细

UVa 1354 天平难题

时间:2018-08-22 22:52:27      阅读:344      评论:0      收藏:0      [点我收藏+]

标签:通过   for   代码   tin   point   cst   distance   size   ble   

首先呈现刘汝佳的高级代码

    // 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行算总宽度洁简了特判代码

总而言之,太巧妙了。

UVa 1354 天平难题

标签:通过   for   代码   tin   point   cst   distance   size   ble   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!