Problem 1608 - Calculation 【状压dp】

题目链接:

Problem 1608 - Calculation Time Limit: 500MS Memory Limit: 65536KB
Total Submit: 316 Accepted: 84 Special Judge: No DescriptionToday, 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.InputThe 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.
OutputFor each test case, output one line with one integer without any space.Sample Input2
5 5
1 2 3 4 5
5 5
1 2 3 8 8
Sample Output3
2

题意:将n个数分若干份,使得每份都可以通过加减得到s。问最多分多少份。

我们用 sum[s] 先记录状态s下的数之和,然后用 yes[s] 标记状态是否合法即 == s。对于s的判定, yes[s]=yes[j]|(sum[s]2sum[j]) j是s的子集。
dp[s] 表示该状态下的最大方案数。dp[s] = max(dp[j] + dp[s ^ j]) j 是s的所有子集。我们要求的就是 dp[(2n)1] 。不过时间卡的好严,姿态不对是过不了的。。。枚举子集 -> j = (j-1) & i。

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#define fi first
#define se second
#define ll o<<1
#define rr o<<1|1
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MOD = 1e9 + 7;
const int MAXN = 100;
const int INF = 1e9 + 10;
void add(LL &x, LL y) { x += y; x %= MOD; }
int dp[1<<15], sum[1<<15], yes[1<<15];
int a[15];
int main()
{
    int t; scanf("%d", &t);
    while(t--) {
        int n, s; scanf("%d%d", &n, &s);
        for(int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
        }
        for(int i = 0; i < (1<<n); i++) {
            dp[i] = sum[i] = yes[i] = 0;
            for(int j = 0; j < n; j++) {
                if(i & (1<<j)) {
                    sum[i] += a[j];
                }
            }
            if(sum[i] == s) {
                yes[i] = 1;
            }
        }
        for(int i = 0; i < (1<<n); i++) {
            if(yes[i]) continue;
            for(int j = i; j > 0; j = (j-1) & i) {
                if(yes[j] || sum[i] - 2 * sum[j] == s) {
                    yes[i] = 1; break;
                }
            }
        }
        for(int i = 0; i < (1<<n); i++) {
            for(int j = i; j > 0; j = (j-1) & i) {
                if(yes[j]) {
                    dp[i] = max(dp[i], dp[i^j] + 1);
                }
            }
        }
        printf("%d\n", dp[(1<<n)-1]);
    }
    return 0;
}

你可能感兴趣的:(Problem 1608 - Calculation 【状压dp】)