问题:Dollar Dayz POJ - 3181
FJ想知道,在一家商品价格为1至K (1 <= K <= 100)的店有多少方法能正好花完他的N(1 <= N <= 1000)元钱 。商品数量充足。
Input:N , K. Output:FJ花钱的方法数 Sample Input
5 3Sample Output
5
分析:
设dp[i][j]为用前i种商品凑j元钱的方法数
若i>j,则第i种商品不可能被选用,dp[i][j]=dp[i-1][j];
若i<=j,则dp[i][j]=dp[i-1][j]+dp[i][j-i](不选用第i种商品的情况+至少选用第i种商品一次的情况);
还需要注意尝试n=1000,k=100的情况,发现可能益处,换用double,发现输出可能有31位有效数字(当然用double只能得到15位有效数字),考虑用两个long long 来存储结果的高位和低位,以17位为分界,用INF=10^17来作为除数和模数,则可以修改递推式子为:
dpb[i][j]=dpb[i-1][j]%INF;
dpa[i][j]=dpa[i-1][j]+dpb[i-1][j]/INF
和
dpb[i][j]=(dpb[i-1][j]+dpb[i][j-i])%INF;
dpa[i][j]=dpa[i-1][j]+dpa[i][j-i]+(dpb[i-1][j]+dpb[i][j-i])/INF;
注意输出的时候,判断一下dpa是不是0,是0的话就不要输出了
代码:
#include<iostream> #include<cstring> using namespace std; typedef long long ll; #define INF 100000000000000000; ll dpa[110][1010],dpb[110][1010]; int main(){ memset(dpb,0,sizeof(dpb)); memset(dpa,0,sizeof(dpa)); int n,k; cin>>n>>k; for(int i=0;i<=k;i++) dpb[i][0]=1; for(int i=1;i<=k;i++){ for(int j=1;j<=n;j++){ if(j<i){ dpb[i][j]=dpb[i-1][j]%INF; dpa[i][j]=dpa[i-1][j]+dpb[i-1][j]/INF } else{ dpb[i][j]=(dpb[i-1][j]+dpb[i][j-i])%INF; dpa[i][j]=dpa[i-1][j]+dpa[i][j-i]+(dpb[i-1][j]+dpb[i][j-i])/INF; } } } if(dpa[k][n]!=0)cout<<dpa[k][n]; cout<<dpb[k][n]; return 0; }