前置知识:

背包

解题思路

题目意思其实就是一个存在性判定问题。问某一个数是否可以通过给定的数进行加减得到(每个数仅使用一次的前提下)

由于给定的石头数量很少,而且给定的石头的重量小,所以考虑对于每次询问前预处理出哪一些重量是可以被表示出来的。

不难想到这其实就是一个经典的问题:01背包,但是这个问题不能单纯用01背包来解,因为每一个数支持加上这个数或者减去这个数。

这时候我们不妨考虑做两遍 01背包,一次限定只能加上石头,一次限定只能减去石头。注意一下枚举的顺序,加石头的时候反过来从 枚举到 ,减去石头的时候就是从 枚举到

有人会问,两次 会不会出现有一个石头又被加上了又被减去了的情况?对于这个问题的回答我本来写了很多,但是后来我转念一想:你减去这个石头,又加上这个石头,这个重量是不会变化的啊,用 的语言解释:

你第一次枚举完是只考虑加的情况,第二次枚举的时候,假设某一个重量是通过加上这个石头得到的,那么显然这个状态原本就已经是 才能使得某一个重量通过加上这个石头达到。所以无论如何都是没有关系的。

Code

#include <bits/stdc++.h>
using namespace std;
bool dp[10005] = {1};
int A[105],n,sum = 0;
int in() {int x; scanf("%d",&x) ; return x;}
int main() {
    while(cin >> n) {
        memset(dp,0,sizeof(dp)); sum = 0;
        dp[0] = 1;
        for(int i = 1 ; i <= n ; i ++) A[i] = in() , sum += A[i];
        for(int j = 1 ; j <= n ; j ++) 
            for(int i = sum ; i >= 1 ; i --)
            if(i - A[j] >= 0) dp[i] |= dp[i - A[j]];//只考虑加上这个石头
        for(int j = 1 ; j <= n ; j ++) 
            for(int i = 1 ; i <= sum ; i ++)
            if(i + A[j] <= sum) dp[i] |= dp[i + A[j]];//只考虑减去这个石头
        int Q;
        cin >> Q;
        while(Q --) {
            int x = in();
            if(x > sum) {cout << "NO\n" ; continue;}
            if(dp[x] == 1) {cout << "YES\n" ; continue;}
            cout << "NO\n";
        }
    }
    return 0;
}