链接:戳这里
题意:给你n个数ai(1<=i<=n),任意去组合这些数,分成k个集合,集合里面的ai可以做+-操作,问你最多可以组成多少个集合使得集合的sum=m
思路:第一次写状压,大概就是把这n个数弄成2进制,每个数取或不取分别代表二进制上是1或者0,然后一开始先给每个集合加数进去,对应的二进制上第i位为1的就加上a[i]
然后要处理+-操作的问题,因为一开始我们是加上a[i]这个数啊,所以如果这个数是减的话我们直接减去两倍的a[i]就是了
对于当前的集合S,对于它的子集S0 减去两倍可以等于m的话说明这个集合S是满足操作和为m的 标记这个集合为1
接下来还是枚举集合S的子集S0,如果当前的集合S0是可以使得和为m的话,则dp[S]的权值为对应的S-S0这个子集的权值+1 即dp[S]=max(dp[S],dp[S-S0]+1)
然后直接输出dp[(1<<n)-1]这个总集合内满足条件的个数
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<vector> #include <ctime> #include<queue> #include<set> #include<map> #include<stack> #include<iomanip> #include<cmath> #define mst(ss,b) memset((ss),(b),sizeof(ss)) #define maxn 0x3f3f3f3f #define MAX 1000100 ///#pragma comment(linker, "/STACK:102400000,102400000") typedef long long ll; typedef unsigned long long ull; #define INF (1ll<<60)-1 using namespace std; int T,n; ll m,a[55],s[32768]; int f[32768],dp[32768]; int main(){ scanf("%d",&T); while(T--){ scanf("%d%lld",&n,&m); for(int i=0;i<n;i++) scanf("%d",&a[i]); for(int S=0;S<(1<<n);S++){ s[S]=0; for(int i=0;i<n;i++){ if(S & (1<<i) ) s[S]+=a[i]; } } for(int S=1;S<(1<<n);S++){ f[S]=0; for(int S0=S;S0;S0=S&(S0-1)){ ll t=s[S]-2*s[S0]; if(t==m || s[S]==m) { f[S]=1; break; } } } int ans=0; for(int S=1;S<(1<<n);S++){ dp[S]=0; for(int S0=S;S0;S0=S&(S0-1)){ if(f[S0]==1) dp[S]=max(dp[S],dp[S^S0]+1); } } cout<<dp[(1<<n)-1]<<endl; } return 0; }