2018.8.21 2018暑假集训之货币系统


试题描述
给你一个n种面值的货币系统,求组成面值为m的货币有多少种方案。样例:设n=3,m=10,要求输入和输出的格式如下:
输入
第一行包含两个数,分别是n(n<=15)和m(m<=3000) 以下n行每行包含一个数,表示每种货币的面值
输出
输出总的方案数
输入示例
3  10                                //3种面值组成面值为10的方案
1                                      //面值1
2                                      //面值2
5                                      //面值5
输出示例
10

多么古老的一道题有木有?
然而这个题向我们说明了古人也是很坑的
先看数据范围搜索可能会TLE
考虑dp/递推
其实思路很好想
和以前一样 考虑不同的变量 不难发现状态之间可以用一张不同面值的货币转移
比如我们用dp[i]表示取到面值为i有多少种取法
那么dp[i]=dp[i]+dp[i-money[j]]
多么简单有没有???
先上一波代码
#include<iostream>
using namespace std;
int dp[10050],money[10050],n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&money[i]);
    dp[0]=1;
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(money[j]>i)continue;
            dp[i]+=dp[i-money[j]];
        }
    }
    printf("%d",dp[m]);
    return 0;
}

然而这样明显是有问题的
经过调试我们可以发现从dp[3]的取值开始就出现错误

dp[3]应该等于2而不是等于3

不难发现是因为dp[3]将1 2和2 1算作了两种

那么问题来了

怎么办??

还记得01背包和floyd的循环吗

之所以外面要先循环物品和节点

就是为了避免出现序列上的重复

比如说对于这个题来讲

如果最外层控制物品就可以让每个重量先装物品1、再装物品2

比如dp[3]在转移到dp[3-1]时dp[2]=1

因为此时装成2的方法只有1 1!!

这就是为什么可以避免出现上面的情况

上代码

#include<iostream>
using namespace std;
int dp[10050],money[10050],n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&money[i]);
    dp[0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=money[i];j<=m;j++)
        {
            dp[j]+=dp[j-money[i]];
        }
    }
    printf("%d",dp[m]);
    return 0;
}

有点太坑人了吧qwq

/*====年轻人,瞎搞是出不了省一的,这就是现实====*/
原文地址:https://www.cnblogs.com/qxds/p/9509249.html