首页 > 代码库 > POJ 3181 Dollar Dayz(完全背包+简单高精度加法)

POJ 3181 Dollar Dayz(完全背包+简单高精度加法)

POJ 3181 Dollar Dayz(完全背包+简单高精度加法)

http://poj.org/problem?id=3181

题意:

       给你K种硬币,每种硬币分别是1美元,2美元…K美元且可以无限使用,问你用上面K种硬币构成n美元的话有多少种方法?

分析:

       本题是一道明显的完全背包问题, 不过本题还可以换一种方法来看: 整数n由前K个自然数构造, 一共有多少种方法? (虽然本题要用到高精度加法,但是很简单, 不要被吓到哦)

 

首先是DP部分:

       令dp[i][j]==x 表示由前i种硬币构成j美元一共有x种方法.

       初始化dp全0 且 dp[0][0]=1

       状态转移: dp[i][j] = sum( dp[i-1][j] , dp[i][j-val[i]] )    //sum为求和,val[i]是第i种硬币的面值.

前者表示第i种硬币一个都不选, 后者表示至少选一个第i种硬币来用.

       最终所求: dp[k][n]的值. 程序实现用的滚动数组, 所以dp只有[j]一维.

 

其次是高精度部分:

       如果输入1000 100时,输出将为:

         15658181104580771094597751280645这个值超过了long long的范围. 所以这里我们需要用大整数来表示dp[i][j]的值. 我的大整数实现是用high和low 的组合来表示一个大整数的. 其中low表示大整数的十进制表示时的低18位数. high表示大整数的十进制表示时的高18位数.

       总体来说实现比较简单.

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const long long BASE = 1e18;
const int maxn=1000+5;

int n,k;

//大整数类
class BigNum
{
public:
    BigNum(){}
    BigNum(long long high,long long low):high(high),low(low){}
    long long high; //高18位
    long long low;  //低18位

    //相加运算
    BigNum operator+(BigNum &B)
    {
        long long high_tmp = (low+B.low)/BASE+high+B.high;
        long long low_tmp = (low+B.low)%BASE;
        return BigNum(high_tmp, low_tmp);
    }

    //输出值
    void print()
    {
        if(!high)//高位为0
            printf("%I64d\n",low);
        else     //高位非0
        {
            printf("%I64d",high);
            printf("%018I64d",low);
        }
    }
}dp[maxn];

int main()
{
    while(scanf("%d%d",&n,&k)==2)
    {
        //初始化
        memset(dp,0,sizeof(dp));
        dp[0].low=1;//等效于令dp[0]=0;

        //递推
        for(int i=1;i<=k;i++)
        {
            for(int j=i;j<=n;j++)
                dp[j] = dp[j]+dp[j-i];
        }

        //输出
        dp[n].print();
    }

    return 0;
}

POJ 3181 Dollar Dayz(完全背包+简单高精度加法)