『题解』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;
}

浙公网安备 33010602011771号