题意:
给你n个硬币,每个硬币最多可以取两次,问你能否组成K这个值。
思路:
直接枚举,总的状态数为3^18,已经无法承受这么大复杂度了。
跟lightoj 1127一样,用到了折半枚举。关于折半枚举之前已经写过(可以点击前面的链接),这里就不讲了。
AC代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 20;
const double pi = acos(-1);
int a[maxn];
long long comb1[20005], comb2[20005];
int cot, cot2;
int n, k, stopindex;
void dfs(int index, long long val)
{
if(index == stopindex)
{
stopindex == n/2 ? comb1[cot++] = val : comb2[cot2++] = val;
return;
}
for(int i = 0;i <= 2; i++)
dfs(index+1, val+a[index]*i);
}
int main()
{
int t, cas = 0;
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &k);
for(int i = 0;i < n; i++)
scanf("%d", &a[i]);
//
//solve
cot = cot2 = 0;
stopindex = n/2;
dfs(0, 0);
stopindex = n;
dfs(n/2, 0);
sort(comb2, comb2+cot2);
int i;
for(i = 0;i < cot; i++)
{
long long rest = k-comb1[i];
int l = 0, r = cot2-1;
while(l <= r)
{
int mid = (l+r)/2;
if(comb2[mid] == rest) break;
if(comb2[mid] < rest)
l = mid+1;
else
r = mid-1;
}
if(l <= r) break;
}
//
if(i < cot)
printf("Case %d: Yes\n", ++cas);
else
printf("Case %d: No\n", ++cas);
}
return 0;
}