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;
    }
     
  • 相关阅读:
    Hook钩子程序
    KMeans笔记 K值以及初始类簇中心点的选取
    自己用C#写的一个俄罗斯方块的小程序(附源代码)。
    那些帮助你成为优秀前端工程师的讲座——《JavaScript篇》
    Mac技巧合集第二期
    WCF增加UDP绑定(应用篇)
    第一个MVC4 Web应用程序
    jQuery的页面加载事件
    通过网页进行 iOS 应用内部分发
    sql count效率
  • 原文地址:https://www.cnblogs.com/handsomecui/p/5373526.html
Copyright © 2011-2022 走看看