题目链接:
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 }