WOJ 1608 Calculation

来源:互联网 发布:在线抓阄软件 编辑:程序博客网 时间:2024/06/05 00:10

题目链接:http://acm.whu.edu.cn/land/problem/detail?problem_id=1608


题意:将n个数划分为若干个部分,然后每一部分里面的数进行+/-操作,如果一个部分里面的数可以凑成s,那么就计1,问最多可以凑成多少个s。
比如1 2 3 4 5 -> (1+4) (2+3) (5)  1 2 3 8 8 -> (8-3) (8-1-2)


思路:状态压缩Dp,用二进制表状态先预处理状态为s时的和,然后再求每个状态能否构成s,最后再计算每个状态最多能构成多少个s,后两个可以借助位运算来枚举子集。


#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <cstdlib>#include <iostream>#include <algorithm>#include <stack>#include <map>#include <set>#include <vector>#include <sstream>#include <queue>#include <utility>using namespace std;#define rep(i,j,k) for (int i=j;i<=k;i++)#define Rrep(i,j,k) for (int i=j;i>=k;i--)#define Clean(x,y) memset(x,y,sizeof(x))#define LL long long#define ULL unsigned long long#define inf 0x7fffffff#define mod %100000007int const maxn = 1<<14;int T;int n,s;int a[15];int sum[maxn];int ans[maxn];int f[maxn];int main(){    cin>>T;    while(T--)    {        Clean(f,0);        Clean(sum,0);        Clean(ans,0);        scanf("%d %d",&n,&s);        rep(i,1,n) scanf("%d",&a[i]);        int uplim = 1<<n;        for( int i = 1; i <= uplim - 1; i++ ) //先处理当前状态的和            for( int j = 0; j < n; j++ )            if ( i & 1<<(j) ) sum[i] += a[j+1];        for( int i = 1; i <= uplim-1; i++ )        {            ans[i] = ( sum[i] == s );            if ( !ans[i] )  //当前状态的数是否可以凑成s            {                for( int j = i & ( i - 1 ); j; j = i & ( j - 1 ) )                {                    ans[i] = ans[i] | ( ( sum[j] == s + sum[i & ~j] ) || ( sum[i & ~j] == s + sum[i] ) );                }            }            f[i] = ans[i]; //当前状态的这些数最多可以凑成几个s            for( int j = i & ( i - 1 ); j; j = i & ( j - 1 ) )            {                f[i] = max( f[i] , f[j] + f[i & ~j] );            }        }        printf("%d\n",f[uplim-1]);    }    return 0;}


0 0
原创粉丝点击