ACdream 1726 A Math game (dfs+二分)

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 }

 

posted @ 2015-05-10 16:34  NowAndForever  阅读(193)  评论(0编辑  收藏  举报