题意:求面值n用k个金币组成的方法数。
思路:一开始直接想到设状态dp(i,j)为面值i用j个硬币表示的方法数,但最后发现没办法转移,看到有人说用无限背包,可能是我理解不够深入,交了几次都WA了,最后查了写资料,发现需要用到一个结论,即面值i用j个金币组成的方法数等于面值i用不超过j的面值的金币组成的方法数。关于证明可以百度,用到了ferrers图像的性质,这样一来状态dp(i,j)表示面值i用面值不超过j的金币组成的方法数,那么对于dp(i,j),我们可以选择用j和不用j,那么状态转移就是dp(i,j)=dp(i-j,j)+dp(i,j-1)。
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
long long dp[310][310]={0};
char t[50];
int main()
{
dp[0][0]=1;
for(int i=0;i<=300;i++)
for(int j=1;j<=300;j++){
if(i>=j) dp[i][j]+=dp[i-j][j];
dp[i][j]+=dp[i][j-1];
}
while(cin.getline(t,50)){
int l=-1,h=-1,n;
sscanf(t,"%d%d%d",&n,&l,&h);
l=min(l,300);h=min(h,300);
if(l==-1) cout<<dp[n][n]<<endl;
else if(h==-1) cout<<dp[n][l]<<endl;
else if(l) cout<<dp[n][h]-dp[n][l-1]<<endl;
else cout<<dp[n][h]<<endl;
}
return 0;
}