题目链接:
[POJ 3181]Dollar Dayz[DP]
题意分析:
给出无穷多个1~K的价值的货币,问:能有多少种方式组合成N。
解题思路:
完全背包。dp[i] = dp[i] + dp[i - j]。j为货币价值。第i个价值可以有第i - j的价值加上价值为j的货币组成。
由于数量很大,需要高精度。这里我们可以使用ldp和hdp分别代表数字的低位和高位,整个数字长度就可以表示到1e36了,这里我们设低位上限为1e18。
记得输出结果的时候补上低位前导零!!!
个人感受:
想着楼梯那种dp,结果发现这题的组合数用楼梯的话会出现重复,例如1 2 2 1和 2 1 1 2是一样的,按楼梯那种写法会重复。结果完全背包从小到大放,完全不会出现重复的情况,我服。然后是像汇编一样使用数组,感觉棒棒哒。以后高精度就不用那么愁了,2333。
具体代码如下:
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<string>
#define ll long long
using namespace std;
const int INF = 0x7f7f7f7f;
const int MAXN = 1e3 + 111;
const ll up = 1e18;
ll hdp[MAXN], ldp[MAXN];
int main()
{
int n, k; cin >> n >> k;
for (int i = 1; i <= k; ++i) {
ldp[0] = 1;
for (int j = i; j <= n; ++j) {
ldp[j] += ldp[j - i];
hdp[j] += hdp[j - i];
if (ldp[j] >= up) {
ldp[j] -= up;
++hdp[j];
}
}
}
if (hdp[n]) {
cout << hdp[n];
ll x = ldp[n];
int cnt = 0;
while (x) {
++cnt;
x /= 10;
}
for (int i = 0; i < 18 - cnt; ++i) {
cout << 0;
}
cout << ldp[n] << '\n';
}
else
cout << ldp[n] << '\n';
return 0;
}