poj3181 Dollar Dayz(动态规划)

题目链接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;
}

猜你喜欢

转载自blog.csdn.net/hopygreat/article/details/79667050
今日推荐