Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 7668 | Accepted: 2863 |
Description
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$1Write 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
Output
Sample Input
5 3
Sample Output
5
Source
问题描述:
第一行给出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";
}