纸币问题(动态规划)

news/2024/10/20 9:36:37

前言

本蒟蒻今天在洛谷上练动态规划,遂写此篇

在这里插入图片描述

一、纸币问题 1

P2842 纸币问题 1

题目描述

某国有 \(n\) 种纸币,每种纸币面额为 \(a_i\) 并且有无限张,现在要凑出 \(w\) 的金额,试问最少用多少张纸币可以凑出来?

输入格式

第一行两个整数 \(n,w\),分别表示纸币的种数和要凑出的金额。
第二行一行 \(n\) 个以空格隔开的整数 \(a_1, a_2, a_3, \dots a_n\) 依次表示这 \(n\) 种纸币的面额。

输出格式

一行一个整数,表示最少使用的纸币张数。

提示

对于 \(40\%\) 的数据,满足 \(n\le 10\)\(w\le 100\)
对于 \(100\%\) 的数据,满足 \(1\le n\le 10^3\)\(1\le a_i \leq w\le 10^4\)

题解

此题是简单的动态规划,求能构成给定数值的纸币数量的最小值。
\(dp[j]\) 表示前 \(i\) 种面额的纸币构成数值为 \(j\) 的金额所需要的最少纸币数量,初状态赋值为\(-1\),表示数值为 \(j\) 的金额无法得到。
对于当前循环的 \(i,j\),如果 \(dp[j-a[i]]!=0\) ,那么则要考虑由数值为 \(j-a[i]\) 的金额得到数值为 \(j\) 的金额能否更新其最小值。
整个过程只需要注意判定 \(dp[j]\) 是否为 \(-1\) 即可。

代码如下

#include<bits/stdc++.h>
using namespace std;
int n,w,a[1001],dp[10001];
int main()
{memset(dp,-1,sizeof(dp));cin>>n>>w;for(int i=1;i<=n;i++) cin>>a[i];dp[0]=0;for(int i=1;i<=n;i++)for(int j=a[i];j<=w;j++)if(dp[j-a[i]]!=0)if(dp[j]==-1) dp[j]=dp[j-a[i]]+1;else dp[j]=min(dp[j],dp[j-a[i]]+1);cout<<dp[w]<<endl;return 0;
}

二、纸币问题 2

P2840 纸币问题 2

题目背景

你是一个非常有钱的小朋友。

题目描述

你有 \(n\) 种面额互不相同的纸币,第 \(i\) 种纸币的面额为 \(a_i\) 并且有无限张,现在你需要支付 \(w\) 的金额,求问有多少种方式可以支付面额 \(w\),答案对 \(10^9+7\) 取模。
注意在这里,同样的纸币组合如果支付顺序不同,会被视作不同的方式。例如支付 \(3\) 元,使用一张面值 \(1\) 的纸币和一张面值 \(2\) 的纸币会产生两种方式(\(1+2\)\(2+1\))。

输入格式

第一行两个正整数 \(n,w\),分别表示纸币的种数和要凑出的金额。
第二行一行 \(n\) 个以空格隔开的正整数 \(a_1, a_2, \dots a_n\) 依次表示这 \(n\) 种纸币的面额。

输出格式

一行一个整数,表示支付方式的数量。

提示

对于 \(40\%\) 的数据,满足 \(n\le 10\)\(w\le 100\)
对于 \(100\%\) 的数据,满足 \(1\le n\le 10^3\)\(1\le a_i \le w\le 10^4\)

其实小朋友并不有钱。

题解

这题困扰了我很久,关键在于支付顺序调换会导致不同支付方式的产生,那么如何 \(dp\) 呢?
先给出状态转移方程

\[dp[i]=\sum_{j=1}^{n}{dp[i-a[j]]}(i>=a[j]) \]

外层循环 \(i=1\dots w\),内层循环 \(j=1\dots n\)
这样是如何将顺序调换考虑在内的呢?
我们可以 \(a[j]\) 理解为“最后一个支付的纸币” ,下面来模拟看看:

输入如下:

\(n\) \(w\) \(a[0]\) \(a[1]\) \(a[2]\) \(a[3]\) \(a[4]\) \(a[5]\) \(a[6]\)
\(6\) \(15\) \(0\) \(1\) \(5\) \(10\) \(20\) \(50\) \(100\)

模拟如下:

$$i$$ $$0$$ $$1$$ $$2$$ $$3$$ $$4$$ $$5$$ $$6$$
$$dp[i]$$ \(1\) \(1\) \(1\) \(1\) \(1\) \(2\) \(3\)
\(i\) 方式1 方式2 方式3
0 \(0\) -- --
1 \(1\)(最后是1) -- --
2 \(1+1\)(最后是1) -- --
3 \(1+1+1\)(最后是1) -- --
4 \(1+1+1+1\)(最后是1) -- --
5 \(1+1+1+1+1\)(最后是1) \(5\) (最后是5) --
6 \(1+1+1+1+1+1\)(最后是1) \(5+1\)(最后是1) \(1+5\)(最后是5)
\(\dots\) \(\dots\) \(\dots\) \(\dots\)

说明:\(dp[0]=1\) 表示支付 \(0\) 元仅一种方式,即不付钱

\(dp[0\dots i-1]\) 已经求得所有情况时,仅需考虑求 \(dp[i]\) 的最后一张纸币的面额 \(a[j]\) 即可,因为在计算每一个 \(dp[i]\) 时,已经将所有纸币面额 \(a[j]\) 考虑在内;确定最后一张的面额并进行情况数加和的过程中,已经包含了计算排列的操作。换句话说,\(dp[i-a[j]]\) 已经将所有排列考虑好了,只需要把最后的 \(a[j]\) 加上去,就得到了 \(dp[i]\) 的一种新情况,并且最后不会有漏算的或重复的(如表中 \(i=6\) 时,可以清晰地看到三种情况)。这很好地体现了“动态规划”的意义。
更形象地来说,考虑将 \(6\) 拆成 \(1+5\) 或者 \(5+1\),当最后一个加的是 \(5\)时,前面的包括 \(1\) 在内的所有数已经排列过了,也就是说 \(1\) 已经出现在了除了最后一个位置之外的所有可能位置,那么只需要再加上 \(1\) 在最后位置的情况,\(1\) 的排列就没有遗漏了。

另外注意:好事多%

代码如下

#include<bits/stdc++.h>
using namespace std;
int n,w,a[1001],dp[10001];
const int mod=1e9+7;
int main()
{cin>>n>>w;for(int i=1;i<=n;i++) cin>>a[i];dp[0]=1;for(int i=1;i<=w;i++)for(int j=1;j<=n;j++){if(i-a[j]>=0)dp[i]=(dp[i]+dp[i-a[j]])%mod;}cout<<(dp[w]%mod)<<endl;return 0;
}

三、纸币问题 3

P2834 纸币问题 3

题目背景

你是一个非常有钱的小朋友。

注意:本题和《进阶篇》的对应题目,输入格式略有差异。

题目描述

你有 \(n\) 种面额互不相同的纸币,第 \(i\) 种纸币的面额为 \(a_i\) 并且有无限张,现在你需要支付 \(w\) 的金额,请问有多少种纸币组合能恰好支付金额 \(w\),答案对 \(10^9+7\) 取模。

输入格式

第一行两个正整数 \(n,w\),分别表示纸币的种数和要凑出的金额。
第二行一行 \(n\) 个以空格隔开的正整数 \(a_1, a_2, \dots a_n\) 依次表示这 \(n\) 种纸币的面额。

输出格式

一行一个整数,表示能恰好凑齐面额 \(w\) 的纸币组合数量。

提示

对于 \(40\%\) 的数据,满足 \(n\le 10\)\(w\le 100\)
对于 \(100\%\) 的数据,满足 \(1\le n\le 10^3\)\(1\le a_i \le w\le 10^4\)

其实小朋友并不有钱。

题解

本题是完全背包问题,可以直接用模板,\(dp[j]\) 记录前 \(i\) 种面额的纸币组成数值为 \(j\) 的钱的方案数,每次循环增加一种纸币面额,依次更新 \(dp[1\dots w]\) 即可。(注意取模)

代码如下

#include<bits/stdc++.h>
using namespace std;
int n,w,a[1001],dp[10001];
const int mod=1e9+7;
int main()
{cin>>n>>w;for(int i=1;i<=n;i++) cin>>a[i];dp[0]=1;for(int i=1;i<=n;i++)for(int j=a[i];j<=w;j++){dp[j]+=dp[j-a[i]]%mod;dp[j]%=mod;	}cout<<(dp[w]%mod)<<endl;return 0;
}

四、问题2与问题3的区别

纸币问题2实际上是方案数排列问题,纸币问题3仅仅是方案数组合问题。因此在动态规划上内外层循环略有不同,二者在递推过程中都符合题目要求完成了计算。

( ^ _ ^ ) /

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/73728.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Spring原理MVC

Spring原理 MVC 1 WEB 1.1 RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter 俩是一对,分别用来处理 @RequestMapping 映射 调用控制器方法、并处理方法参数与方法返回值演示1 - DispatcherServl…

树状数组——原理详解

前言 这两天在网上学树状数组,但是发现网上关于树状数组的解释大都对初学者不太友善,原理讲解部分并不是很容易理解,所以写了一篇树状数组,顺便帮自己巩固一下。一、什么是树状数组 1.概念: 简单来说,这是一种数据结构。顾名思义,它通过树的结构来对数组进行高效操作,一…

机器学习中空间和时间自相关的分析:从理论基础到实践应用

空间和时间自相关是数据分析中的两个基本概念,它们揭示了现象在空间和时间维度上的相互依赖关系。这些概念在各个领域都有广泛应用,从环境科学到城市规划,从流行病学到经济学。本文将探讨这些概念的理论基础,并通过一个实际的野火风险预测案例来展示它们的应用。图1: 空间自相关…

manim边做边学--直角平面

直角平面NumberPlane是Manim库中用于创建二维坐标平面的对象,它可以帮助用户在场景中可视化坐标轴以及网格线。 通过坐标轴、网格线以及刻度,它能够动态地展示函数曲线、几何图形以及它们的变换过程,使得复杂的数学概念变得直观易懂。 NumberPlane提供了x轴和y轴,通常是中心…

一文彻底弄懂MySQL的MVCC多版本控制器

InnoDB 的 MVCC(Multi-Version Concurrency Control,多版本并发控制) 是 MySQL 实现高并发事务处理的一种机制。通过 MVCC,InnoDB 可以在高并发环境下支持 事务隔离,并提供 非阻塞的读操作,从而避免锁定所有读操作带来的性能瓶颈。MVCC 允许事务在不加锁的情况下读取数据…

sicp每日一题[2.50]

Exercise 2.50Define the transformation flip-horiz, which flips painters horizontally, and transformations that rotate painters counterclockwise by 180 degrees and 270 degrees.这道题挺有意思的,搞明白这道题就明白了 frame 的3个点的位置。如上图所示,为了更好区…

stiReport中的DataBand的DataSource要设置,不然打印时哪怕数据有两行也只显示一行

stiReport中的DataBand的DataSource要设置,不然打印时哪怕数据有两行也只显示一行。哪怕report的数据源是通过程序动态设置的,这个属性也要在设计时有值。

读数据工程之道:设计和构建健壮的数据系统14源系统

源系统1. 源系统中的数据生成 1.1. 数据工程师的工作是从源系统获取数据,对其进行处理,使其有助于为下游用例提供服务 1.2. 数据工程师的角色将在很大程度上转向理解数据源和目的地之间的相互作用 1.3. 数据工程的最基本的数据管道任务——将数据从A移动到B 2. 数据源 2.1. 数…