Coins POJ - 1742 (背包变形)
题目链接
题目大意:给n个硬币,问凑1~m中的数,能凑出几个?
题目样例:
3(n个硬币) 10(m)
1 2 4 2 1 1 前n个是硬币的面额,后n个是每个硬币面额的个数
2 5
1 4 2 1
输出:8 4
第一反应是多重背包,然后就去学了下。附代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1000;
int value[maxn], amount[maxn], dp[100005];
int n, V; //n是个数, V是容量
//value价值 amount 数量
//应该是物品所占的体积 价值 数量 此题物品所占体积和价值相等
void Completepark(int vol, int val) { //完全背包
for(int i = vol; i <= V; i++)
dp[i] = max(dp[i], dp[i - vol] + vol);
}
void ZeroOnepark(int vol, int val) { //01背包
for(int i = V; i >= vol; i--)
dp[i] = max(dp[i], dp[i - vol] + vol);
}
void Multiplepark(int vol, int val, int amount) { //多重背包
if(amount * vol >= V) { //数量和体积的乘积大于总积 可直接看成完全背包来处理 相当于有无限个
Completepark(vol, val);
}
else {
int k = 1;
while(k < amount) {
ZeroOnepark(k * vol, k * val); //01背包
amount -= k;
k <<= 1;
}
if(amount) {
ZeroOnepark(amount * vol, amount * val);
}
}
}
int main()
{
while(~scanf("%d %d", &n, &V)) {
if(n == 0 && V == 0) break;
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; i++) {
scanf("%d", &value[i]);
}
for(int i = 0; i < n; i++) {
scanf("%d", &amount[i]);
}
for(int i = 0; i < n; i++) {
Multiplepark(value[i], value[i], amount[i]);
}
int num = 0;
for(int i = 1; i <= V; i++) {
if(dp[i] == i)
num++;
}
printf("%d\n", num);
}
}
然后就TLE了。。。看了poj的讨论区,发现这个题卡这种做法,然后就知道了还有一种更快的多重背包的做法,单调队列优化的,遂去学,然而没学会。。
然后看了挑战上面的做法,很棒的做法.
dp[i][j] 表示的是用前i种去凑j这些容量,i还剩多少种 num[i] if(dp[i][j] >= 0) dp[i][j] -1 (j < num[i] || dp[i + 1][j - num[i]] < 0) dp[i + 1][j - num[i]] - 1 else
然后就MLE了,把它换成1维的就ok
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 500 + 100;
int num[maxn], value[maxn], dp[100001];
int main()
{
int n, m;
int ans;
while(~scanf("%d %d", &n, &m)) {
if(n == 0 && m == 0) break;
for(int i = 1; i <= n; i++)
scanf("%d", &num[i]);
for(int i = 1; i <= n; i++)
scanf("%d", &value[i]);
memset(dp, -1, sizeof(dp));
dp[0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= m; j++) {
if(dp[j] >= 0) dp[j] = value[i];
else {
if(j < num[i] || dp[j - num[i]] < 0) dp[j] = -1;
else dp[j] = dp[j - num[i]] - 1;
}
// printf("%d %d %d\n", i, j, dp[i][j]);
}
}
ans = 0;
for(int i = 1; i <= m; i++) {
if(dp[i] >= 0) ans++;
}
printf("%d\n", ans);
}
}