第五届华中区程序设计邀请赛 网络赛Calculation E 【状压dp】

来源:互联网 发布:下载模组的软件 编辑:程序博客网 时间:2024/06/04 18:00

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

题意:把n个数分成若干堆(一个数只能在一堆),若一堆经过+or-操作等于m,ans+1,问最大的ans。

分析:n只有14,我们考虑状压,可以知道哪些数在一个集合(s)得到和sum[s],然后我们可以枚举s的子集(s0),当sum[s]或sum[s]-sum[s0]-sum[s0]等于m是标记为1,代表当前集合可以等于m。

然后我们再次枚举子集开始dp,当一个子集是1时,dp[s]=max(dp[s],dp[s^s0]+1),表示s可以由s^s0集合与s0组成且在s^s0集合的基础上的多了一个等于m的子集。

代码:

#include <algorithm>#include <iostream>#include <iostream>#include <cstring>#include <cstdio>#include <string>#include <vector>#include <queue>#include <cmath>#include <stack>#include <set>#include <map>#include <ctime>#define INF 0x3f3f3f3f#define Mn 1<<14#define Mm 15#define mod 1000000007#define CLR(a,b) memset((a),(b),sizeof((a)))#define CPY(a,b) memcpy ((a), (b), sizeof((a)))#pragma comment(linker, "/STACK:102400000,102400000")#define ul (u<<1)#define ur (u<<1)|1using namespace std;typedef long long ll;ll sum[Mn];int dp[Mn];int flag[Mn];int a[Mm];int main(){    int n,m;int t;cin>>t;while(t--) {scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) {scanf("%d",&a[i]);}int all=(1<<n)-1;CLR(sum,0);CLR(flag,0);CLR(dp,0);for(int s=1;s<=all;s++) {for(int i=0;i<n;i++) {if((s>>i)&1) {sum[s]+=a[i+1];}}if(sum[s]==m) flag[s]=1;else for(int s0=s;s0;s0=(s0-1)&s) {if((sum[s]-sum[s0]-sum[s0])==m) {flag[s]=1;break;}}}for(int s=1;s<=all;s++) {for(int s0=s;s0;s0=(s0-1)&s) {if(flag[s0]) dp[s]=max(dp[s],dp[s^s0]+1);}}cout<<dp[all]<<endl;}return 0;}


0 0