LightOJ 1235

题目链接:

  http://www.lightoj.com/volume_showproblem.php?problem=1235

题目描述:

  给出n个硬币,每种硬币最多使用两次,问能否组成K面值?

解题思路:

  因为K草鸡大,尽管n很小,但是2^n很大,无法用背包做到O(nK)的复杂度。如果暴力枚举复杂度立马飙升到O(2^(n+1))。···········

  所以引进一种新的算法,折半查找:把所要枚举的值,先把前部分的值所有情况枚举出来,再把后部分的值所有情况枚举出来并排序,结合二分进行查找。 这样可以把复杂度降到O(2^(n/2)*log2(n))。

  最近不做题,感觉自己萌萌哒。大家玩敲七,到我这里,我总是一脸懵逼的样子。希望一天一个dp,到老也能萌萌哒  (。・`ω´・)

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 
 8 #define maxn 20000
 9 int cnt1, cnt2, k, n, num;
10 int a1[maxn], a2[maxn], b[20];
11 
12 void dfs (int sum, int x)
13 {
14     if (x == num)
15     {
16         num == n/2 ? a1[cnt1++] = sum : a2[cnt2++] = sum;
17         return ;
18     }
19     for (int i=0; i<3; i++)
20         dfs (sum+b[x]*i, x+1);
21 }
22 
23 bool sreach (int x)
24 {
25     int low = 0, high = cnt2-1;
26 
27     while (low <= high)
28     {
29         int mid = (low + high) / 2;
30         if (a1[x] + a2[mid] == k)
31             return true;
32         else if (a1[x] + a2[mid] > k)
33             high = mid - 1;
34         else
35             low = mid + 1;
36     }
37 
38     return false;
39 }
40 
41 int main ()
42 {
43     int T, l = 1;
44     scanf ("%d", &T);
45 
46     while (T --)
47     {
48         scanf ("%d %d", &n, &k);
49         for (int i=0; i<n; i++)
50             scanf ("%d", &b[i]);
51 
52         cnt1 = cnt2 = 0;
53         num = n / 2;
54         dfs (0, 0);
55 
56         num = n;
57         dfs (0, n/2);
58         sort (a2, a2 + cnt2);
59 
60         int i;
61         for (i=0; i<cnt1; i++)
62         {
63             if (sreach (i))
64                 break;
65         }
66         if (i == cnt1) printf("Case %d: No
", l++);
67         else printf ("Case %d: Yes
", l++);
68     }
69     return 0;
70 }
原文地址:https://www.cnblogs.com/alihenaixiao/p/5405870.html