whu1608 Calculation

链接:http://acm.whu.edu.cn/land/problem/detail?problem_id=1608&contest_id=16

题意:给定n个数,将这n个数随意划分为若干个集合,求一次划分后最大的满足条件的集合个数,条件为:将集合内所有的元素进行+-两种运算后结果为s。

分析:看到题面和数据规模都很容易看出是状压DP。我们枚举集合是2^n,然后还有快速判断一个集合是否能满足条件,而且集合的dp要靠子集来传递。很容易想到是二进制集合枚举子集那种3^n的方法(不熟的可以参考《算法竞赛入门经典训练指南》P69例题29)。判断一个集合是否满足条件也只要枚举子集来当做要减去的数的集合即可。详见代码。O(t*3^n)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int MAX=151;
const int mod=100000000;
const int MOD1=100000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000009;
const ll INF=10000000010;
typedef double db;
typedef unsigned long long ull;
int a[20],e[20],f[17000],g[17000],bo[17000];
int main()
{
    int i,j,n,s,t,w,ans;
    e[0]=1;
    for (i=1;i<20;i++) e[i]=e[i-1]*2;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &s);
        for (i=e[n]-1;i>=0;i--) f[i]=g[i]=bo[i]=0;
        for (i=0;i<n;i++) {
            scanf("%d", &a[i]);
            for (j=e[n]-1;j;j--)
            if (j&e[i]) g[j]+=a[i];
        }
        for (i=e[n]-1;i;i--) {
            if (g[i]==s) { bo[i]=1;continue ; }
            for (j=i;j;j=(j-1)&i)
            if (g[i]-g[j]-g[j]==s) { bo[i]=1;break ; }
        }
        ans=0;
        for (i=1;i<e[n];i++) {
            if (bo[i]) f[i]=1;
            for (j=i;j;j=(j-1)&i)
            if (bo[j]) f[i]=max(f[i],f[i^j]+1);
            ans=max(ans,f[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}


你可能感兴趣的:(whu1608 Calculation)