题目链接:点击打开链接
题意:给出一些种类的硬币,每类硬币均有一定面值和数量,问用这些硬币能组成位于[1,m]范围内的多少种面值?
思路:背包中的可达性问题,按照经验,如果只求最值,dp数组为常规的即可,初始化为全0;如果既求最值又判断可达性,dp数组最好初始化为-1作为不可达的判断,且dp[0]初始化为0;此题只判断可达性,其实连dp数组都用不到,直接开数组标记判断结果即可。
网上不少人采用的方法为既求最值又判断可达性,最后判断dp[i] == i得出可达不可达,可参见点击打开链接
// HDU 2844 Coins.cpp 运行/限制:296ms/1000ms
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
//如果既判断可达性又判断最值,dp数组不变,初始化为-1
//此题只判断可达性,其实连dp数组都用不到,直接开数组标记判断结果即可
int n, m;
int v[105], num[105], judge[100005];
void completePack(int weight) {
for (int i = weight; i <= m; i++) {
judge[i] |= judge[i - weight];
}
}
void zeroOnePack(int weight) {
for (int i = m; i >= weight; i--) {
judge[i] |= judge[i - weight];
}
}
void multipPack(int weight, int num) {
if (m <= weight * num) {
completePack(weight);
return;
}
else {
int k = 1;
while (num > k) {
num -= k;
zeroOnePack(weight * k);
k *= 2;
}
zeroOnePack(weight * num);
}
}
int main(){
while (scanf("%d%d", &n, &m) != EOF && n && m) {
for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
for (int i = 1; i <= n; i++) scanf("%d", &num[i]);
memset(judge, 0, sizeof(judge));
judge[0] = 1;
for (int i = 1; i <= n; i++) {
multipPack(v[i], num[i]);
}
int ans = 0;
for (int i = 1; i <= m; i++) {
ans += judge[i];
}
printf("%d\n", ans);
}
return 0;
}