Farmer John 想知道,在一家商品价格为1至K (1 <= K <= 100)的店有多少方法能正好花完他的N(1 <= N <= 1000)元钱 。商品数量充足。
Input
N 、 K.
FJ花钱的方法数
5 3
Sample Output
5
1*3+1*2
1*3+2*1
1*2+3*1
2*2+1*1
5*1
这道题要注意对大数的处理!!
当N为0时,K不论为多少,dp[0]=1;
在货币种类K相同的情况下,随着钱数总和N的增加,我们会发现有这样的状态方程:dp[N]=dp[N]+dp[N-K];
因为之前的dp[N]是在循环下累加的结果,我们在逐个增加K后,只需要将dp[N](K=K-1)之前的状态下再增加个N=K+…… (……总和=N-K)这样的排列序列(例如上述N=4,K=2;N=5,K=3),即增加总钱数为K,货币有1-K-1的总排列数即可。因此得到状态方程。
#include <cstdio>
#define INF 100000000000000000
using namespace std;
long long dp[1001][2]; //0是高位,1是低位
int K,N;
int main()
{
scanf("%d %d",&N,&K);
dp[0][1]=1;//总价0元的用任何类型的钱去组合结果都是什么都不买->一种情况
for(int i=1;i<=K;i++)
{
for(int j=i;j<=N;j++)
{
dp[j][1] += dp[j-i][1];//低位十八位
//处理进位
dp[j][0] += dp[j-i][0] + dp[j][1] / INF; //高位进位
dp[j][1] %= INF; //低位只取前十八位
}
}
//两数合并成一个大数的输出
if(dp[N][0]) printf("%lld",dp[N][0]);
printf("%lld\n",dp[N][1]);
return 0;
}
一维数组表示是现代码:
#include<iostream>
#include<cstring>
using namespace std;
long long int inf=1000000000000000000; //大数
//用j种价格配出金额i的方案数
long long int a[1005]; //低位
long long int b[1005]; //高位
//2^63~=9*10^18,所以用a数组表示前18位数,用b数组表示后18位数
int main()
{
int i,j,n,k;
cin>>n>>k;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
a[0]=1; //用任何种配0都是1 -> 什么都不用
for(j=1;j<=k;j++)
{
for(i=j;i<=n;i++)
{
//if(i<j) 另一种方法,i初始值为1时
// continue;
b[i]=(b[i]+b[i-j])+(a[i]+a[i-j])/inf;
a[i]=(a[i]+a[i-j])%inf;
}
}
if(b[n])
cout<<b[n];
cout<<a[n]<<endl;
return 0;
}