『题解』UVa online judge UVA12455 Bars

题目传送门

题目大意

给出 \(p\) 个数 \(a_1,a_2,\dots,a_p\),问能不能选择一些数组成 \(n\)

思路

不难发现这题是 01 背包弱化版。

状态定义:设 \(f_{i,j}\) 表示使用前 \(i\) 个数,能否组成 \(j\)

状态转移方程:\(f_{i,j}=f_{i-1,j} \lor f_{i-1,j-a_i}\)

对于第 \(i\) 个数,可以不选,也可以选。

如果不选,\(f_{i,j}=f_{i-1,j}\)

选,那么 \(f_{i,j}=f_{i-1,j-a_i}\)

在选与不选中,若有一种方案可以用凑够 \(j\),那么当前的 \(f_{i,j}\) 就为 \(1\)

由于 \(f_{i,j}\) 只由 \(f_{i-1,j}\) 转移而来,所以考虑滚动数组。

状态转移方程:\(f_{j}=f_{j} \lor f_{j-a_i}\)

注意:枚举 \(j\) 时,需要从大向小枚举。

例如,正在更新 \(f_7\),而更新 \(f_7\) 时需要用到原来的 \(f_4\),不优化的情况下是 \(f_{i-1,4}\),但之前已经更新过 \(f_4\) 了,变成了 \(f_{i,4}\)。从大到小枚举 \(j\) 就能解决这个问题。

\(j\) 最小要枚举到 \(a_i\),因为在 \(j \le a_i\) 时,\(j-a_i\) 就是负数,数组越界。

代码

#include <iostream>
using namespace std;
template<typename T=int>
inline T read(){
    T X=0; bool flag=1; char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-') flag=0; ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<1)+(X<<3)+ch-'0',ch=getchar();
    if(flag) return X;
    return ~(X-1);
}

const int N=1e3+5;
int t,p,n;
int a[N],f[N];

int main(){
    t=read();
    while(t--){
        n=read(),p=read();
        for(int i=1; i<=p; i++){
            a[i]=read();
        }
        f[0]=1; // 用 0 个数凑成 0 是可以的
        for(int i=1; i<=p; i++){
            for(int j=n; j>=a[i]; j--){
                f[j]=f[j] || f[j-a[i]];
            }
        }
        puts(f[n] ? "YES" : "NO");
        for(int i=0; i<N; i++){
            f[i]=0;
        }
    }
    return 0;
}
posted @ 2022-04-22 14:37  仙山有茗  阅读(90)  评论(0)    收藏  举报