zoukankan      html  css  js  c++  java
  • Calculation(dfs+状压dp)

    Problem 1608 - Calculation
    Time Limit: 500MS   Memory Limit: 65536KB    Total Submit: 311  Accepted: 82  Special Judge: No
    Description
    Today, Alice got her math homework again!
    She had n integers, and she needed to divide them into several piles or one pile. For each pile, if the teacher could get S, by + or – operator, then Alice got 1 small red flower. Alice wanted to get as many flowers as possible. Could you help her? Just tell her the maximum number of flowers she could get.
    Input
    The input consists of several test cases. The first line consists of one integer T (T <= 100), meaning the number of test cases. The first line of each test cases consists of two integer n (n<=14), meaning the number of the integer, and S (0<= S<= 100000000), meaning the result which teacher wanted. The next line consists of n integer a1, a2, …, an (0<= ai <= 10000000). You should know a few cases that n is larger than 12.
    Output
    For each test case, output one line with one integer without any space.
    Sample Input
    2
    5 5
    1 2 3 4 5
    5 5
    1 2 3 8 8
    Sample Output
    3 2
    题解:
    题目让求a集合的元素通过加减能组成S的最大组数,每个数字只能用一次;
    看到这个题目就想着用dfs搜索,再状压下用的位置;但是华丽丽的wa了;首先自己的处理不对,时间复杂度是4^n,也就是2^28,超时不说,还有就是自己的太暴力了,并不一定是最优解;最后问了学长,学长一眼看出了我的错误,然后我的思路就行不通了。。。学长说这个应该是状压dp;
    第一发状压dp,思想是dfs找解,如果找到dp状压的位置是1;然后for循环找到1~1<<n的所有的子集,dp[i]=max(子集的最优解+对i的补集的最优解,dp[i]);
    代码:
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int dp[1 << 14];
    int a[14];
    int n, S;
    void dfs(int i, int val, int cur){
        if(i == n){
            if(val == S){
                dp[cur] = 1;
            }
            return;
        }
        dfs(i + 1, val, cur);
        dfs(i + 1, val + a[i], cur | (1 << i));
        dfs(i + 1, val - a[i], cur | (1 << i));
    }
    int main(){
        int T;
        scanf("%d", &T);
        while(T--){
            scanf("%d%d", &n, &S);
            for(int i = 0; i < n; i++){
                scanf("%d", a + i);
            }
            fill(dp, dp + (1 << n), 0);
            dfs(0, 0, 0);
            for (int i = 1; i < (1 << n); i++){
                for (int j = i & (i - 1); j > 0; j = i & (j - 1)){
                    dp[i] = max(dp[i], dp[j] + dp[j ^ i]);
                    //j是i的子集,j ^ i 是i的补集; 
                }
            }
            printf("%d
    ", dp[(1 << n) - 1]);
        }
        return 0;
    }

     学长说还可以用中途对撞写,那样可以把dfs时间复杂度降到O(n*3^(n / 2)):

    学长的中途对撞:

    int d[1 << 14];
    int a[14];
    int n, s;
    
    void MakeSum(const vector<int> &x, vector< pair<int, int> > &res) {
      for (int i = 0; i < (1 << x.size()); ++i) {
        int cur = 0;
        for (int j = 0; j < x.size(); ++j) {
          if (i >> j & 1) cur -= x[j];
        }
        res.push_back(make_pair(cur, i));
        for (int j = i; j > 0; j = (j - 1) & i) {
          cur = 0;
          for (int k = 0; k < x.size(); ++k) {
            if (i >> k & 1) {
              if (j >> k & 1) cur += x[k];
              else cur -= x[k];
            }
          }
          res.push_back(make_pair(cur, i));
        }
      }
      sort(res.begin(), res.end());
    }
    
    void Init() {
      fill(d, d + (1 << n), 0);
      vector< pair<int, int> > left, right;
      int mid = n / 2;
      MakeSum(vector<int>(a, a + mid), left);
      MakeSum(vector<int>(a + mid, a + n), right);
      for (int i = 0; i < left.size(); ++i) {
        int lo = lower_bound(right.begin(), right.end(), make_pair(s - left[i].first, -1)) - right.begin();
        int hi = upper_bound(right.begin(), right.end(), make_pair(s - left[i].first, 1 << 30)) - right.begin();
        for (int j = lo; j < hi; ++j) {
          d[left[i].second | (right[j].second << mid)] = 1;
        }
      }
    }
    
    int main() {
      int T;
      scanf("%d", &T);
      while (T--) {
        scanf("%d%d", &n, &s);
        for (int i = 0; i < n; ++i) {
          scanf("%d", a + i);
        }
        Init();
        for (int i = 1; i < (1 << n); ++i) {
          for (int j = (i - 1) & i; j > 0; j = (j - 1) & i) {
            d[i] = max(d[i], d[j] + d[i ^ j]);
          }
        }
        printf("%d
    ", d[(1 << n) - 1]);
      }
      return 0;
    }
     
  • 相关阅读:
    Sql Server 日期推算处理,格式化处理
    自动升级功能(zt)
    绘制普通的可上下左右布局的RULER(zt)
    合并两个WORD文档,并且修改Word中标签的内容
    Sql Server 中汉字处理排序规则,全角半角
    用函数式编程技术编写优美的 JavaScript(zt)
    什么是序列化?
    日期概念理解中的一些测试
    DataSet,DataTable 不使用EXCEL组件直接保存为EXCEL
    接口测试自动化实践指南
  • 原文地址:https://www.cnblogs.com/handsomecui/p/5373526.html
Copyright © 2011-2022 走看看