题意:n个砝码,给你若干个重物,问你是否能称出来。
题解:
n只有20所,直接枚举有三种状态,放左边,放右边,不放。共有3^20次方情况。
如果枚举一半,找另一半,找另一半共有2*(3^10)次方复杂度,方法可行。
还有一种折半方法,把20个物品重量取相反数变为40个物品,然后对于这40个物品的取放便包含了所有情况。直接枚举复杂度2^40,这题的重量很小,重复量很多,其实暴力枚举可以水过去。而另一种方法当然也是折半枚举,复杂度2*(2^20)。
最好理解的当然是01背包啦,这题物品重量不超过2000,超过2000肯定无解。我们直接对20个物品做个01背包,然后反过来对他们的相反数做个01背包就行了。注意加法背包和减法背包第二层循环方向是不同的。下面给出01背包代码。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int dp[4005];
int a[30];
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)cin>>a[i];
dp[0]=1;
for(int i=0;i<n;i++)
for(int j=2000;j>=a[i];j--)
dp[j]|=dp[j-a[i]];
for(int i=0;i<n;i++)
for(int j=0;j<=2000;j++)
dp[j]|=dp[j+a[i]];
int m;cin>>m;
while(m--)
{
int x;cin>>x;
if(x>2000)printf("NO\n");
else if(dp[x]) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}