WOJ 1608 Calculation

题目链接: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 %100000007
int 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值