折半搜索
https://ac.nowcoder.com/acm/contest/889/D(超大背包)
#include<bits/stdc++.h> using namespace std; //__ #define pb push_back typedef long long ll; typedef unsigned long long ull; struct node{ int num; ull sum; }a[1<<19],b[1<<19]; bool cmp(node p,node q){ return p.sum<q.sum; } int main(){ int n; ull s; scanf("%d%llu",&n,&s); int lena=1,lenb=1; int p=n/2; for(int i=0;i<p;i++){ ull x; scanf("%llu",&x); int m=lena; for(int j=0;j<m;j++){ a[lena].num=(a[j].num|(1<<i)); a[lena].sum=a[j].sum+x; lena++; } } for(int i=p;i<n;i++){ ull x; scanf("%llu",&x); int m=lenb; for(int j=0;j<m;j++){ b[lenb].num=(b[j].num|(1<<i)); b[lenb].sum=b[j].sum+x; lenb++; } } sort(a,a+lena,cmp); sort(b,b+lenb,cmp); int l=0,r=lenb-1; while(l<lena){ while(a[l].sum+b[r].sum>s&&r) r--; if(a[l].sum+b[r].sum==s) break; l++; } for(int i=0;i<(int)n/2;i++) printf("%d",(a[l].num>>i)&1); for(int i=int(n/2);i<n;i++) printf("%d",(b[r].num>>i)&1); return 0; }
https://www.luogu.org/problem/SP11469
题意:在n个数中去若干个数,再把这若干个数分成俩个集合,使这俩个集合的sum相同,问方案数
分析:n<=20,要是要求全部能取还可以直接用2进制枚举,但他要取若干个,所以考虑把20对半折,直接搜索
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define pb push_back const int M=2e6+5; vector<int>vis[M]; map<ll,int>cmp; ll yes[M]; int n,midd,cnt; ll a[M]; void dfs1(int i,int sum,int nowsta){ if(i>midd){ if(!cmp[sum]) cmp[sum]=++cnt; vis[cmp[sum]].pb(nowsta); return ; } dfs1(i+1,sum,nowsta);///不取这个数 dfs1(i+1,sum+a[i],nowsta|(1<<(i-1)));///把这个数取到第一个集合 dfs1(i+1,sum-a[i],nowsta|(1<<(i-1)));///把这个数取到另外的一个集合中 } void dfs2(int i,int sum,int nowsta){ if(i>n){ int x=cmp[sum]; for(int i=0;i<vis[x].size();i++){ yes[vis[x][i]|nowsta]=1; } return ; } dfs2(i+1,sum,nowsta); dfs2(i+1,sum+a[i],nowsta|(1<<(i-1))); dfs2(i+1,sum-a[i],nowsta|(1<<(i-1))); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); midd=n/2; dfs1(1,0,0); dfs2(midd+1,0,0); int ans=0; for(int i=1;i<(1<<n);i++) ans+=yes[i]; printf("%d\n",ans); return 0; }