UVA 674 Coin Change(完全背包)

UVA 674 Coin Change(完全背包)

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=615

题意:

       有面值为1,5,10,25,50美分的5种硬币,每种硬币可以无限使用.现在给你任意一个钱数目x(x<=7489), 问你用上面的5种硬币有多少种方法能构成x美分?

分析:

       由于每种硬币的数目都是无限的, 所以本题可以看成是一道完全背包问题(虽然本题所求的目标有点不同).

       我们令val[i]表示第i种硬币的面值, 令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为求和

       最终我们所求为dp[n][x]的值.

       如何理解上述的状态转移方程呢?

       首先来看dp[i][j], 它表示用前i中硬币构成j美分的方法数目. 那么:

       1.    如果我们根本不用第i种硬币(只用前i-1种硬币即可),我们可以知道有dp[i-1][j]种方式可以(不用任何一个第i种硬币)构成j美分.

       2.    如果我们至少用1第i种硬币来构成j美分, 那么我们可以知道有dp[i][j-val[i]] 种方法能达成目的.

       综上所述, dp[i][j]==用前i种硬币构成j美分的方法总数== sum( dp[i-1][j] , dp[i][j-val[i]] ).

程序实现用的滚动数组, 所以dp只有[j]这维. 且注意j必须从小到大循环了.(想想为什么)

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=7489+5;

//5种硬币的面值
int val[]={1,5,10,25,50};
int dp[maxn];

int main()
{
    //初始化
    memset(dp,0,sizeof(dp));
    dp[0]=1;

    //递推过程
    for(int i=0;i<5;i++)
        for(int j=val[i];j<maxn;j++)
            dp[j]+=dp[j-val[i]];

    //输出每组数据
    int v;
    while(scanf("%d",&v)==1)
        printf("%d\n",dp[v]);
    return 0;
}

你可能感兴趣的:(Algorithm,算法,dp,ACM)