POJ 3181 Dollar Dayz (动态规划之计数问题+高精度运算)

问题:Dollar Dayz POJ - 3181

FJ想知道,在一家商品价格为1至K (1 <= K <= 100)的店有多少方法能正好花完他的N(1 <= N <= 1000)元钱 。商品数量充足。

Input:N , K. Output:FJ花钱的方法数 Sample Input
5 3
Sample 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;
}

猜你喜欢

转载自blog.csdn.net/qq_41333528/article/details/80159987
今日推荐