原题
题目分析
完全背包题,不过需要状压和处理大数,首先定义dp[i][j]为用前i种货币(从1-k),凑出j价格的方案数,dp初始化为0,然后更新有两个方向,第一种显然dp[i][j]+=dp[i-1][j],第二种dp[i][j]+=dp[i-1][j-i]+dp[i-1][j-2i]+dp[i-1][j-3i]....直到j-ki<0为止,第二种更新方向想一下可以优化成dp[i][j]+=dp[i][j-i],这里因为i只有(i,i-1),所以可以状压,由dp更新方向知可直接压成第二为维,j的更新方向从小到大即可.这里的大数不大,可以把两个longlong构成一个大数,也就是一个longlong放后17位数,另一个放前17位数,具体操作可以参考代码.
代码
1 #include <iostream> 2 #include <algorithm> 3 #include <utility> 4 #include <cstdio> 5 #include <cmath> 6 #include <cstring> 7 #include <string> 8 #include <vector> 9 #include <stack> 10 #include <queue> 11 #include <map> 12 #include <set> 13 14 using namespace std; 15 typedef long long LL; 16 const int INF_INT=0x3f3f3f3f; 17 const LL INF_LL=0x3f3f3f3f3f3f3f3f; 18 19 const LL limit=1e17; 20 LL dp[2000][2]; 21 22 int main() 23 { 24 // freopen("black.in","r",stdin); 25 // freopen("black.out","w",stdout); 26 int n,k; 27 cin>>n>>k; 28 dp[0][0]=1; 29 for(int i=1;i<=k;i++) 30 { 31 for(int j=0;j<=n;j++) 32 if(j-i>=0) 33 { 34 dp[j][0]+=dp[j-i][0]; 35 dp[j][1]+=dp[j-i][1]; 36 dp[j][1]+=dp[j][0]/limit; 37 dp[j][0]=dp[j][0]%limit; 38 } 39 /* for(int j=0;j<=n;j++) 40 { 41 printf("dp[%d][%d]=",i,j); 42 if(dp[j][1]) printf("%lld",dp[j][1]); 43 printf("%lld\n",dp[j][0]); 44 }*/ 45 } 46 if(dp[n][1]) printf("%lld",dp[n][1]); 47 printf("%lld\n",dp[n][0]); 48 return 0; 49 }