链接: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)|1 using 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; }