题意描述:https://ac.nowcoder.com/acm/problem/52168
子集和问题----问数组里面选哪几个数可以使其和为给定数m 数组最长长度为36
思路:
错误思路:
数组里面的每个数只有两个选择---选或者不选 总的情况最多为2^36
然后把这些情况用map数组存 把值为m的情况输出
这种思路肯定不可能 超亿运算 一定超时
正确思路:
思路一:
采用折半搜索 分开搜索前一半的所有情况x 和后一半的所有情况y
然后map一下 判断mp[tag-x]是否存在
降低时间复杂度 2^18 *2
#include<bits/stdc++.h> using namespace std; #define ll long long ll a[40]; int main() { ll n,m; scanf("%lld %lld",&n,&m); for(int i=0;i<n;i++) { scanf("%lld",&a[i]); } map<ll,string> mp1; map<ll,string> mp2; ll n1=n/2; ll n2=n-n1; for(int i=0;i<(1<<n1);i++) //遍历前半部分的所有情况 (1<<n1)即2^n1 { string str; ll sum=0; for(int j=0;j<n1;j++) { if(i&(1<<j)) //判断将i转成二进制之后,从右往左数第j+1位是不是1 { sum+=a[j]; str+='1'; } else str+='0'; } mp1[sum]=str; } for(int i=0;i<(1<<n2);i++) //遍历后半部分的所有情况 { string str; ll sum=0; for(int j=0;j<n2;j++) { if(i&(1<<j)) { sum+=a[j+n1]; str+='1'; } else str+='0'; } mp2[sum]=str; } map<ll,string>::iterator it; for(it=mp1.begin();it!=mp1.end();it++) { ll d=m-(*it).first; if(mp2.count(d)) { cout<<mp1[(*it).first]<<mp2[d]<<endl; break; } } }
收获:
i&(1<<j):将1向左移动j位(等价于1*2^j),然后按位与i,判断结果是否为0
搜索n个数每个数存在的情况2^n 可以用二进制
for(int i=0;i<(1<<n1);i++) //遍历前半部分的所有情况 (1<<n1)即2^n1 { string str; ll sum=0; for(int j=0;j<n1;j++) { if(i&(1<<j)) //判断将i转成二进制之后,从右往左数第j+1位是不是1 { sum+=a[j]; str+='1'; } else str+='0'; } mp1[sum]=str; }