HDU 2844 Coins

传送门

多重背包,重量与价值相同,恰好装满。
这个题差不多。有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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值