题目链接:http://poj.org/problem?id=3181
题目大意:fj有N$,要买工具,工具的价格为1$~K$,问fj有哪些种购买方式。
例如: 5$ 工具价格最高为3$(即有1$, 2$, 3$三种)。
fj可以买5个1$, 1个2$及3个1$, 2个2$及1个1$, 1个3$及2个1$, 1个3$及1个2$
所以对应输出为5。
动态规划策略:设dp[i][j]为i划分不超过j时的种类数。
这个题目的类型其实是整数划分。这里只做了一个小小的限制,更改了划分的上限。
考虑到当i<j
时其实划分的种数其实和dp[i][i]是一样的。
当i>=j
时,可以递归的处理为dp[i-j][j],从i中拿出一个j再继续划分,划分的最大值不变,也可以处理为dp[i][j-1],即不划分,降低划分上限。
动态规划方程:所以最终的动态规划方程如下:
if( i < j ):
dp[i][j] = dp[i][i];
else:
dp[i][j] = dp[i-j][j] + dp[i][j-1];
动态规划的初值:对于dp的初值只需要考虑对于dp[0][0] = 1;
其实到这里题目本来就应该已经做出来了,但是题目中有个比较麻烦的地方,那就是整数溢出。
这个溢出还比较严重,严重到用long long存不下,用double会有精度误差。
因此这里必须引入高精度加法。
所以这里把dp[i][j]处理成了一个int[10]的数组,int[0] 是有效数字的长度,
int[1]~int[int[0]]中存的是有效数字, 每个有效数字位存放了一个9位数,如果有进位就向前一位进位。
下面是已ac的代码:
#include <iostream>
#include <cstdio>
using namespace std;
#define m 1000000000
int dp[1010][110][10];
void add( int* r, int* a, int* b ) {
r[0] = max(a[0], b[0]);
for ( int i = 1 ; i <= r[0] ; ++ i ) {
r[i] = a[i] + b[i];
}
for ( int i = 1 ; i <= r[0] ; ++ i ) {
if ( r[i] >= m ) {
r[i+1] += r[i]/m;
r[i] %= m;
}
}
while ( r[r[0]+1] != 0 ) r[0] ++;
}
void print( int* r ) {
for ( int i = r[0] ; i >= 1 ; -- i ) {
printf( "%d", r[i] );
}
}
int main()
{
int N, K;
scanf( "%d%d", &N, &K );
dp[0][0][0] = dp[0][0][1] = 1;
for ( int i = 0 ; i <= N ; ++ i ) {
for ( int j = 1 ; j <= K ; ++ j ) {
if ( i < j ) {
for ( int k = 0 ; k <= dp[i][i][0] ; ++ k ) {
dp[i][j][k] = dp[i][i][k];
}
} else {
add( dp[i][j], dp[i-j][j], dp[i][j-1] );
}
}
}
print( dp[N][K] );
puts("");
return 0;
}