POJ 1742 Coins(多重背包) DP

 参考:http://www.hankcs.com/program/cpp/poj-1742-coins.html

题意:给你n种面值的硬币,面值为a1...an,数量分别为c1...cn,求问,在这些硬币的组合下,能够多少种面值,该面值不超过m

思路:设d[i][j]——前i种硬币,凑成总值j时,第i种硬币所剩余的个数。

   默认d[i][j] = -1,代表无法凑成总值j

   转移方程为,若d[i-1][j]≥0,代表前i-1种已能够凑成j,那么就不必花费第i种硬币,所以d[i][j] = c[i]

   否则就看d[i][j-a[i]]的值,显然如果j < a[i],那么d[i][j] = -1,否则d[i][j-a[i]] ≤ 0,代表此刻第i种硬币已使用完了,所以自然d[i][j] = -1;

   否则,d[i][j] = d[i][j-a[i]]-1;

   可以看到d[i][]的值只与d[i-1][]和d[i][]有关,所以我们可以采用一维数组的形式,从而能够节省内存空间。

 AC代码:

#include <cstdio>
#include <cstring>
using namespace std;

const int M = 100005;
const int N = 105;
int d[M];
int n,a[N],c[N],m;

void solve()
{
	memset(d, -1, sizeof(d));
	d[0] = 0;
	
	for(int i = 0; i < n; i++)
	{
		for(int j = 0; j <= m; j++)
		{
			if(d[j] >= 0) d[j] = c[i];
			else if(j < a[i] || d[j-a[i]]<= 0)
				d[j] = -1;
			else d[j] = d[j-a[i]]-1;
		}
	}
	
	int ans = 0;
	for(int i = 1; i <= m; i++)
		if(d[i]>=0) ans++;
	printf("%d
", ans);
}

int main()
{
	//freopen("in.txt", "r", stdin);
	while(~scanf("%d %d", &n, &m) &&(n||m))
	{
		for(int i = 0; i < n; i++) scanf("%d", a+i);
		for(int i = 0; i < n; i++) scanf("%d", c+i);
		solve();
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/sevenun/p/5442279.html