题意:给定大小为n(<=36)的集合a,整数s,求a的一个和为s的子集(有且只有一个)
直接搜索要\(2^{36}\)次,时间过多,考虑一次搜索前半集合,一次搜索后半集合,得到两个\(2^{16}\)的答案数组,就变成了双数组匹配问题
#include <algorithm> #include <iostream> #include <cstdio> using namespace std; typedef long long ll; const ll maxn=1e6+5; struct node{ ll vis,v;//用二进制数vis表示元素的选择情况 node(ll a=0,ll b=0):vis(a),v(b){} bool operator<(node b){ return v<b.v; } }N[maxn]; ll a[40]; ll n,half,cnt; ll s; void dfs(ll cur,ll sum,ll vis){ if(sum>s)return; if(cur==half){ N[++cnt].vis=vis; N[cnt].v=sum; return; } if(a[cur]+sum<=s) dfs(cur+1,sum+a[cur],vis|1<<(cur-1)); dfs(cur+1,sum,vis); } void dfs_(ll cur,ll sum,ll vis){ if(sum>s) return; if(cur==n+1){ ll temp=s-sum; ll p=lower_bound(N+1,N+1+cnt,node(1,temp) )-N; if(N[p].v==temp){ ll t=N[p].vis; for(int i=1;i<half;i++){ if(t&1) printf("1"); else printf("0"); t=t>>1; } t=vis; for(int i=half;i<=n;i++){ if(t&1) printf("1"); else printf("0"); t=t>>1; } printf("\n"); exit(0); } return; } if(sum+a[cur]<=s) dfs_(cur+1,sum+a[cur],vis|1<<(cur-half)); dfs_(cur+1,sum,vis); } int main(){ cin>>n>>s; half=n/2; for(ll i=1;i<=n;i++) scanf("%lld",&a[i]); dfs(1,0,0); sort(N+1,N+1+cnt); dfs_(half,0,0); }