http://acdream.info/problem?pid=1726
官方题解:http://acdream.info/topic?tid=4246
求n个数里面能不能选一些数出来让它们的和等于k。
因为k很大,不能用背包,但是n很小,最大为40,所以拆成了2部分,之后最大为2^20次方<1050000;每次枚举前一半的和,然后用数组存储,然后得到一个总和减去后一半的差用二分查找。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int n,h,n1,n2,a[50],b[50],c[1050000],cnt; 7 bool flag=0; 8 9 bool check(int x) 10 { 11 int l=0,r=cnt-1; 12 while(l<=r) 13 { 14 int mid=(l+r)>>1; 15 if(c[mid]==x) return 1; 16 else if(c[mid]>x) r=mid-1; 17 else l=mid+1; 18 } 19 return 0; 20 } 21 void dfs(int sum,int deep,int m,int on) 22 { 23 if(flag) return; //剪枝 24 if(deep==m) //到最后一个数 25 { 26 if(on) c[cnt++]=sum; //第一次记录 和 27 else flag=check(h-sum); //第二次查找 28 return; 29 } 30 for(int i=0;i<2;i++) 31 { 32 sum+=b[deep]*i; //累加和 33 if(sum>h) return; //剪枝 34 dfs(sum,deep+1,m,on); 35 } 36 } 37 int main() 38 { 39 //freopen("a.txt","r",stdin); 40 while(~scanf("%d%d",&n,&h)) 41 { 42 cnt=0; 43 flag=0; 44 for(int i=0;i<n;i++) 45 scanf("%d",&a[i]); 46 n1=n>>1;n2=n-n1; //分成两半 47 for(int i=n1;i<n;i++) 48 b[i-n1]=a[i]; //后一半数用b存储 49 dfs(0,0,n2,1); //枚举所有可能的和 50 sort(c,c+cnt); //对得到的数组进行排序 便于二分查找 51 for(int i=0;i<n1;i++) 52 b[i]=a[i]; //得到前一半的和 53 dfs(0,0,n1,0); //枚举所有的和 54 if(flag) puts("Yes"); 55 else puts("No"); 56 } 57 return 0; 58 }
时间: 2024-10-07 21:00:40