背包的硬币问题

转自

http://www.cnblogs.com/qiufeihai/archive/2012/09/11/2680840.html

首先说没限制的硬币问题吧:

先看这个问题:在一个国家仅有1分,2分,3分硬币,将钱N兑换成硬币有很多种兑法。请你编程序计算出共有多少种兑法。

我们用dp[n]表示用这些硬币组成n的方法总数。。。。

然后随着硬币种类的增加来更新dp[]的值,也就是最外面的一层循环for(i :1-->3)开始初始化的时候没有硬币,然后新来了面值为1的硬币,接着又来了面值为2的硬币。。。。

显然,每新增加一种面值的硬币,dp[]的值一定在增加。。。新的dp[] = 未新增前的dp[] + dp[1件新增硬币] + dp[2件新增硬币] + dp[3件新增硬币] +.......

dp[k件新增硬币]

由于for里用了完全背包里的顺序,for(j = c[i]; j <= n; j++),所以dp[j] += dp[j - c[i]];中的dp[j - c[i]]已经是有k件新增硬币的状态了。。。。。

即j不停地向右,开始的时候得到只有一个新增硬币而组成n的总数,然后由只有1个新增硬币的结果得到只有2个新增硬币而组成n的总数,慢慢地,。。。。得到由越来越多件新增硬币组成n的总数得到的dp[i]就是该容量下的总数了

HDU 1284 : http://acm.hdu.edu.cn/showproblem.php?pid=1284

代码:

复制代码
#include <iostream>
using namespace std;

const int M = 32768 + 10;

int dp[M];

int main()
{
int n;
while (~scanf("%d", &n))
{
memset(dp, 0, sizeof(dp));
dp[0] = 1;
for (int i = 1; i <= 3; i++)
{
for (int j = i; j <= n; j++)
{
dp[j] += dp[j - i];
}
}

printf("%d\n", dp[n]);
}
return 0;
}
复制代码

HDU 1028 http://acm.hdu.edu.cn/showproblem.php?pid=1028

代码:

复制代码
#include <iostream>
using namespace std;

const int M = 120 + 10;

int dp[M];

int main()
{
int n;
while (~scanf("%d", &n))
{

memset(dp, 0, sizeof(dp));
dp[0] = 1;

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

printf("%d\n", dp[n]);
}
return 0;
}
复制代码
HDU 1398 http://acm.hdu.edu.cn/showproblem.php?pid=1398

代码:

复制代码
#include <iostream>
#include <cmath>
using namespace std;

const int M = 300 + 10;

int dp[M];

int main()
{
int n;
while (~scanf("%d", &n), n)
{

memset(dp, 0, sizeof(dp));
dp[0] = 1;

int max = (int)sqrt(n * 1.0);
for (int i = 1; i <= max; i++)
{
for (int j = i * i; j <= n; j++)
{
dp[j] += dp[j - i * i];
}
}

printf("%d\n", dp[n]);
}
return 0;
}
复制代码

接着是有总数限制的硬币问题如HDU 2069 http://acm.hdu.edu.cn/showproblem.php?pid=2069

他要求硬币总数不超过100

这时候我们就要增加一个维度来限制数量,dp[i][j]表示用不超过i个硬币组成j的总数

最外一层枚举硬币总类和上面一样的道理,dp[num][j] += dp[num - 1][j - c[i]]这里是对于新来的每一个硬币,= 放这个硬币的总数+不放这个硬币的总数,

d[num-1][j-c[i]]是之前已经更新到有k-1个新硬币的了,而这个式子只针对地k个新硬币所以num-1是未放这个新硬币之前的答案,即dp[j - c[i]]放的都只是放一个硬币

(第k个新硬币)得到的总数

然后答案要累加不超过i:0-->100得到的总数,一定要从0开始累加,因为数据有0个硬币的总数

代码:

复制代码
#include <iostream>
#include <cmath>
using namespace std;

const int M = 300 + 10;

int dp[111][M];
int c[] = {0, 1, 5, 10, 25, 50};

int main()
{
//freopen("in.txt", "r", stdin);
int n;
while (~scanf("%d", &n))
{

memset(dp, 0, sizeof(dp));
dp[0][0] = 1;

for (int i = 1; i <= 5; i++)//枚举硬币总类
{
for (int num = 1; num <= 100; num++)//枚举硬币个数
{
for (int j = c[i]; j <= n; j++)//枚举容量
{

dp[num][j] += dp[num - 1][j - c[i]];
}
}
}

int ans = 0;
for (int i = 0; i <= 100; i++)//累加答案
{
ans += dp[i][n];
}

printf("%d\n", ans);
}
return 0;
}

时间: 2024-10-08 19:59:23

背包的硬币问题的相关文章

完全背包(硬币)

http://acm.hdu.edu.cn/showproblem.php?pid=1114 Piggy-Bank Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 43171    Accepted Submission(s): 21299 Problem Description Before ACM can do anything, a

线性动态规划

准确来说,动态规划是一种思想,而不是一种算法.算导里将它归结为——高级程序设计技巧. 在线性结构上进行状态转移DP,统称线性DP. 线性DP最常见的有: 子集和问题,LIS问题,LCS问题. 拓展之后有:子段和问题,杂类问题. 1. 子集和问题和硬币计数问题 子集和问题的一个实例: 〈S,t〉.其中,S={ 1 x , 2 x ,…, n x }是一个正整数的集合,c是一个正整数.子集和问题判定是否存在S的一个子集S1,使得s1中的各元素之和等于c. 这其实就是0/1背包. 硬币计数问题的一个实

hdu2844 Coins

hdu 二进制优化多重背包 将硬币的价值看做费用,使用的硬币个数看做价值,将第\(i\)种硬币看成\(c_i\)个价值为\(a_i\)的硬币跑01背包的话时间是\(O(m\sum c)\)的,显然不大行 注意到可以对\(c_i\)直接进行二进制拆分,把它拆成\(log\)个物品(\(2^0,2^1,\cdots,2^k,c_i-2^k(2^{k+1}>c_i)\)),再跑01背包时间就是\(O(m\sum log\ c_i)\)的,可以接受 #include<iostream> #inc

dp背包问题/01背包,完全背包,多重背包,/coin change算法求花硬币的种类数

一步一步循序渐进. Coin Change 具体思想:给你 N元,然后你有几种零钱S={S1,S2...,Sm} (每种零钱数量不限). 问:凑成N有多少种组合方式  即N=x1 * S1+x2*S2+...+xk*Sk (xk>=0,k=1,2..m) 设有f(x)中组合方式 有两种解答(自底向上回溯): 1.不用第m种货币   f(N,m-1) 2.用第m种货币 f(N-Sm,m) 总的组合方式为f(N,m)=f(N,m-1)+f(N-Sm,m) anything is nonsense,s

[BZOJ 1042][HAOI 2008]硬币购物(背包+容斥原理)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1042 刚开始搞容斥原理,还很有点吃力,我太弱了... 首先用被类似于背包的DP进行预处理,假设每种硬币个数无限制,求出f[i]=凑出面值i的方案总数. 但是实际上题目中每种硬币个数是有限制的,设四种硬币分别是a.b.c.d,则凑出面值S的方案中超出限制的方案数=a超出限制的方案数+b超出限制的方案数+c超出限制的方案数+d超出限制的方案数-a和b都超出限制的方案数-a和c都超出限

BZOJ 1042 硬币购物(完全背包+DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1042 题意:给出四种面值的硬币c1,c2,c3,c4.n个询问.每次询问用d1.d2.d3.d4个相应的硬币能够拼出多少种总和为s? 思路:(1)首先,用完全背包求出f[i]表示四种硬币的数量无限制拼出i的方案数. (2)接着我们来理解 x=f[s]-f[s-(d1+1)*c1]的含义:x表示c1硬币的数量不超过d1个而其他三种硬币的数量不限制拼成s的方案数.我们举着例子来说明, 假设

【bzoj1042】[HAOI2008]硬币购物 背包dp+容斥原理

题目描述 硬币购物一共有4种硬币.面值分别为c1,c2,c3,c4.某人去商店买东西,去了tot次.每次带di枚ci硬币,买si的价值的东西.请问每次有多少种付款方法. 输入 第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000 输出 每次的方法数 样例输入 1 2 5 10 2 3 2 3 1 10 1000 2 2 2 900 样例输出 4 27 题解 背包dp+容斥原理 考虑没有硬币个数限制,那么本题显然是完全

UVA 674 Coin Change 硬币转换(完全背包,常规)

题意:有5种硬币,个数无限的,组成n元的不同方案有多少种? 思路:常规完全背包.重点在dp[0]=1,dp[j]中记录的是组成 j 元的方案数.状态转移方程dp[j+coin[i]]+=dp[j]. 1 #include <bits/stdc++.h> 2 using namespace std; 3 int coin[]={1, 5, 10, 25, 50}; 4 int dp[10000], n; 5 6 int cal() 7 { 8 if(!n) return 1; 9 memset(

bzoj1708:[Usaco2007 Oct]Money奶牛的硬币(完全背包

1708: [Usaco2007 Oct]Money奶牛的硬币 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 797  Solved: 540[Submit][Status][Discuss] Description 在创立了她们自己的政权之后,奶牛们决定推广新的货币系统.在强烈的叛逆心理的驱使下,她们准备使用奇怪的面值.在传统的货币系统中,硬币的面值通常是1,5,10,20或25,50,以及100单位的货币,有时为了更方便地交易,会发行面值为2单位