題意:求面值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;
}