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;
    }
     
  • 相关阅读:
    IDEA启动报错 NoClassDefFound
    IDEA手动导入jar包到maven本地库
    springboot FilterRegistrationBean 拦截器的使用
    springboot多模块controller访问的问题
    @Slf4j -- lombok.extern.slf4j.Slf4j;
    @mapper注解
    lombok
    Lodop打印小票
    spring security登录认证流程解析
    使用selenium实现简单网络爬虫抓取MM图片
  • 原文地址:https://www.cnblogs.com/handsomecui/p/5373526.html
Copyright © 2011-2022 走看看