POJ 3181 -- Dollar Dayz(动态规划)

Dollar Dayz
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 7668 Accepted: 2863

Description

Farmer John goes to Dollar Days at The Cow Store and discovers an unlimited number of tools on sale. During his first visit, the tools are selling variously for $1, $2, and $3. Farmer John has exactly $5 to spend. He can buy 5 tools at $1 each or 1 tool at $3 and an additional 1 tool at $2. Of course, there are other combinations for a total of 5 different ways FJ can spend all his money on tools. Here they are: 

        1 @ US$3 + 1 @ US$2

        1 @ US$3 + 2 @ US$1

        1 @ US$2 + 3 @ US$1

        2 @ US$2 + 1 @ US$1

        5 @ US$1
Write a program than will compute the number of ways FJ can spend N dollars (1 <= N <= 1000) at The Cow Store for tools on sale with a cost of $1..$K (1 <= K <= 100).

Input

A single line with two space-separated integers: N and K.

Output

A single line with a single integer that is the number of unique ways FJ can spend his money.

Sample Input

5 3

Sample Output

5

Source

USACO 2006 January Silver


问题描述:

第一行给出2个数 N K

你拥有K种硬币,面值为1...K 每种的数量有无限个

求:

用这K种硬币能凑出几种方案 使得总金额为N


dp[i][j] := 用i种价格配出金额j的方案数。

这样考虑:

dp[i][0] = 1,使用任何价格配出金额0的方案个数都是1(什么都不用)。

而从1开始

dp[i][j] : 代表新使用一种硬币(面额为j)之后的金额:

dp[i][j] = dp[i-1][j] + dp[i-1][j-i] + dp[i-1][j-2*i] + ...... dp[i-1][0]

也就是说:新加入一种硬币 那么这种硬币我使用1个的方案为dp[i-1][j-i]

使用2个的方案为dp[i-1][j-2*i](使用前面的i-1种硬币凑到j-2*i,再加入2枚金额为i的硬币,恰好凑到j)


考虑优化这个解法:
用j-i去代换j

dp[i][j-i] = dp[i-1][j-i] + dp[i-1][j-2*i] + ..... dp[i-1][0] 恰好为原式中的第二项及以后的项

那么原式为:

dp[i][j] = dp[i-1][j] + dp[i][j-1] 

(j>=i)

这样就得到了优化的方案

除此之外

本题的数据很大,以至于使用unsigned long long 都会溢出

由于 unsigned long long 最大范围到 1844674407370955161 ,所以取一个比这个值小一个数量级同时又能被10整除的数作为limit

(参考自网络)

也就是100000000000000000


那么我们使用的

unsigned long long dp[105][1005][2];

其中dp[i][j][0]保留unsigned long long不能保存的前i位数

dp[i][j][1]则保留unsigned long long能够保存的数


代码:


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
                  1844674407370955161
#define LIMIT_ULL 100000000000000000
using namespace std;

unsigned long long dp[105][1005][2];
int main()
{
	int n,k;
	
	scanf("%d %d",&n,&k);
	
	for(int i = 1;i<=k;i++)
	{
		dp[i][0][1] = 1;
		for(int j = 1;j<=n;j++)
		{
			if (j < i)
			{
				dp[i][j][0] = dp[i - 1][j][0];
				dp[i][j][1] = dp[i - 1][j][1];
			}
			else
			{
				dp[i][j][0] = dp[i - 1][j][0] + dp[i][j - i][0];
				dp[i][j][1] = dp[i - 1][j][1] + dp[i][j - i][1];
				// 高位进位
				dp[i][j][0] += dp[i][j][1] / LIMIT_ULL;
				// 低位限制
				dp[i][j][1] = dp[i][j][1] % LIMIT_ULL;
			}
		}
	}
	if(dp[k][n][0])
	cout << dp[k][n][0];
	
	cout << dp[k][n][1] << "\n";
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值