多重背包,重量与价值相同,恰好装满。
和这个题差不多。有n
种硬币,每种硬币都有一个数量和价值,问你拿这些硬币能恰好支付多少个价格(价格区间[1,m]
),支付每个价格都可用全部的硬币。
方法就是将重量和价值视为相同,然后再恰好装满,所以dp[j]
表示恰好拿取价值总和为j
的硬币所获得的(最大,233)价值。可以想到,这个值要么是非法值,要么是j
。
这个题给的硬币价值总和的上限太大了,肯定不能用作数组大小。而m
的上限可以接受,用m
当作数组大小就可以了,因为求解答案只会用到dp[1]~dp[m]
。
另外想到:这道题即使硬币价值总和(V
)比m
小也没关系,因为是恰好装满,dp[V+1]
及以后的值一定都是非法值。
这题本来没什么好说的,但是有个bug坑了一个小时,很烦。
用fill()
会超时(>1000ms)!!!换成朴素的循环赋值就AC了(200+ms)。
结论就是:以后不再用fill()
,其实根本没省几个字母。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std; // 多重背包,要求恰好装满
const int INF = 1e9;
int N, M;
int p[100];
int num[100];
int dp[100001];
int cnt;
void init()
{
for (int i = 1; i <= M; i++) // 不能用fill()!!!!!!!!!!!!!!!!!!用fill()就超时
dp[i] = -INF;
//fill(dp + 1, dp + M + 1, -INF); // 这他妈是什么鬼函数,用了直接超时(>1000ms),不用200+ms
dp[0] = 0;
cnt = 0;
}
int main()
{
for (; ~scanf("%d%d", &N, &M);)
{
if (N == 0 && M == 0) break;
for (int i = 0; i < N; i++)
scanf("%d", &p[i]);
for (int i = 0; i < N; i++)
scanf("%d", &num[i]);
init();
for (int i = 0; i < N; i++)
{
if (p[i] * num[i] >= M)
{
for (int j = p[i]; j <= M; j++)
dp[j] = max(dp[j], dp[j - p[i]] + p[i]);
}
else
{
int t;
for (int k = 1; k < num[i]; k <<= 1)
{
t = p[i] * k;
for (int j = M; j >= t; j--)
dp[j] = max(dp[j], dp[j - t] + t);
num[i] -= k;
}
t = p[i] * num[i];
for (int j = M; j >= t; j--)
dp[j] = max(dp[j], dp[j - t] + t);
}
}
for (int i = 1; i <= M; i++)
if (dp[i] > 0) cnt++; // 等价dp[i] == i
printf("%d\n", cnt);
}
return 0;
}