【AcWing】蓝桥杯集训每日一题Day21|背包问题|完全背包|DP|1371.货币系统(C++)

news/2024/5/17 15:45:06/文章来源:https://blog.csdn.net/CoderZzz6310/article/details/137614263
1371.货币系统
1371. 货币系统 - AcWing题库
难度:简单
时/空限制:1s / 64MB
总通过数:7096
总尝试数:12581
来源:

usaco training 2.3
算法标签

DP背包问题

题目内容

给定 V 种货币(单位:元),每种货币使用的次数不限。
不同种类的货币,面值可能是相同的。
现在,要你用这 V 种货币凑出 N 元钱,请问共有多少种不同的凑法。

输入格式

第一行包含两个整数 V 和 N。
接下来的若干行,将一共输入 V 个整数,每个整数表示一种货币的面值。

输出格式

输出一个整数,表示所求总方案数。

数据范围

1≤V≤25,
1≤N≤10000
答案保证在long long范围内。

输入样例:
3 10
1 2 5
输出样例:
10
题目解析

V种货币,每种货币的使用次数是不限的
不同的货币,面值可能是相同的也可能是不同的
要用V种货币凑出N元钱,问有多少种不同的凑法

输入若干行,总共输入V个数,不一定每行一个数,也不一定只有一行

最多有25种货币
总钱数最大是10000

非常经典的完全背包问题

把总钱数N看成背包容量
把每种货币看成是一个物品
每种物品可以用无限个

如果想装满背包的话,一共有多少种不同的方案

时间复杂度是N*V,25万

如何分析完全背包问题

从集合角度来分析

  1. 状态表示
    背包问题可以用一维来写,分析的时候最好用二维来分析
    f[i][j]
    i表示物品,j表示当前的背包容量
  • 当前状态表现的是哪个集合
    表示只用前i种货币,且总钱数是j的所有方案的集合
  • 存的是集合的哪个属性
    问的是方案数,所以属性就是集合当中的元素数量

只要把每一个状态都求出来,答案就是f[V][N]

  1. 状态计算
    集合的划分
    求这个集合的元素数量的话,一般是先将这个集合分成若干个子集,分别求每一个子集的元素数,再相加,就可以了

划分的时候一般是找最后一个不同点,也就是第i种货币用的数量
用第i种货币用的数量将集合分成若干类

  • 第一类是第i种货币用0个
  • 第二类是用1个
  • 第三类是用2个
  • 以此类推
    这种划分方案是不重不漏的
    每一种方案可以依据第i种货币的数量一定属于且仅属于某一个子集
    因此要求整个方案的方案数的话,只要分别求每个子集的方案数,再相加就可以了

如何求每一个子集的方案数

  • 先看一下第一个子集的方案数,从具体含义来看怎么求
    从1到i中选,且总金额是j的所有方案的集合,并且第i种货币选0个,等价于从1~i-1中选,总价值是j
    根据状态表示,它恰好就是f[i-1][j]

  • 第二个子集的方案数
    从1到i中选,总钱数是j,并且第i种货币用了1张,的所有方案的集合
    前面随便选,最后一个都是只包含1个第i种货币
    变化的部分就是前面,方案数就取决于前面可以变化出多少种

前面可以变化出多少种,要看前面变化的含义是什么
用1~i-1,凑出来 j − v i j - v_{i} jvi,的所有方案的集合
每一个前面的集合里面的方案加上一张 v i v_{i} vi,就可以构成这个子集的一个方案

根据状态的表示,恰好就是f[i-1][j - v]

  • 同理下一个子集的方案数就是f[i-1][j - 2 * v]
  • 以此类推

最终,
f[i][j] = f[i-1][j] + f[i - 1][j - v] + f[i - 1][j - 2 * v]...

状态数量是货币数量乘总金额25万
每个货币最多会用1万次
整个的时间复杂度 2.5 ∗ 1 0 9 2.5*10^9 2.5109

需要优化
通过公式变形,给这个式子做化简

用同样的方式把f[i][j - v]展开,相当于换元法,用j-v换掉v
f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i − 1 ] [ j − v ] + f [ i − 1 ] [ j − 2 v ] … f [ i ] [ j − v ] = f [ i − 1 ] [ j − v ] + f [ i − 1 ] [ j − 2 v ] + f [ i − 1 ] [ j − 3 v ] … \begin{array}{} f[i][j] = f[i-1][j] + f[i - 1][j - v] + f[i - 1][j - 2v]\dots \\ f[i][j-v]=f[i-1][j-v]+f[i-1][j-2v]+f[i-1][j-3v]\dots \end{array} f[i][j]=f[i1][j]+f[i1][jv]+f[i1][j2v]f[i][jv]=f[i1][jv]+f[i1][j2v]+f[i1][j3v]
对比一下两个式子的后半部分,发现完全一样
所以第一个式子的后半部分其实就是
f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i ] [ j − v ] f[i][j] = f[i-1][j]+f[i][j-v] f[i][j]=f[i1][j]+f[i][jv]
这样计算每个状态的时候就不需要循环10000次了,时间复杂度变为25万,可以过了

代码

二维空间写法

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;typedef long long LL;//N表示货币数量,M表示背包容量
const int N = 30, M = 10010;int n, m;
int v[N];   //v表示每一个货币的面值
LL f[N][M];  //f是状态数int main()
{scanf("%d%d", &n, &m);//先读入每一种货币的面额for (int i = 1; i <= n; i ++) scanf("%d", &v[i]);//定义初始,一张货币也没有,也是一种方案f[0][0] = 1;//枚举一下所有的状态for (int i = 1; i <= n; i ++)for (int j = 0; j <= m; j ++){//左边的状态一定存在f[i][j] = f[i - 1][j];//后面的状态不一定存在,当j小于v的时候不存在if (j >= v[i]) f[i][j] += f[i][j - v[i]];}printf("%lld\n", f[n][m]);return 0;
}

优化空间的写法

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;typedef long long LL;//N表示货币数量,M表示背包容量
const int N = 30, M = 10010;int n, m;
int v[N];   //v表示每一个货币的面值
LL f[M];  //f是状态数int main()
{scanf("%d%d", &n, &m);//先读入每一种货币的面额for (int i = 1; i <= n; i ++) scanf("%d", &v[i]);//定义初始,一张货币也没有,也是一种方案f[0] = 1;//枚举一下所有的状态for (int i = 1; i <= n; i ++)for (int j = 0; j <= m; j ++){//左边的状态一定存在//右边的j还没有更新过,就是上层循环的f[i-1][j]//左边的j是第i层循环算的j,就是f[i][j]f[j] = f[j];//后面的状态不一定存在,当j小于v的时候不存在if (j >= v[i]) //左边的j是当前算的j,就是f[i][j]//由于是从小到大枚举的j,在算j的时候,j-v[i]已经在当前第i层循环算过了,所以右边这样写的话,用的就是第i层循环的j-v[i]f[j] += f[j - v[i]];}printf("%lld\n", f[m]);return 0;
}

再优化

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;typedef long long LL;//N表示货币数量,M表示背包容量
const int N = 30, M = 10010;int n, m;
int v[N];   //v表示每一个货币的面值
LL f[M];  //f是状态数int main()
{scanf("%d%d", &n, &m);//先读入每一种货币的面额for (int i = 1; i <= n; i ++) scanf("%d", &v[i]);//定义初始,一张货币也没有,也是一种方案f[0] = 1;//枚举一下所有的状态for (int i = 1; i <= n; i ++)for (int j = 0; j <= m; j ++){f[j] = f[j]; //这句话是恒等式,可以删掉if (j >= v[i])  //当j小于v[i]的时候,循环会直接跳过,可以让j直接从v[i]开始枚举f[j] += f[j - v[i]];}printf("%lld\n", f[m]);return 0;
}

变成

	for (int i = 1; i <= n; i ++)for (int j = v[i]; j <= m; j ++)f[j] += f[j - v[i]];

完全背包问题,直接改完是等价的
01背包问题改完不等价,需要倒过来循环

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python画图Matplotlib和Seaborn

python画图Matplotlib和Season 一、Matplotlib1、介绍2、安装3、内容二、Seaborn1、介绍2、安装3、内容一、Matplotlib Matplotlib官网 1、介绍 Matplotlib 是一个 Python 的绘图库,用于创建高质量的二维图表和一些基本的三维图表。它广泛应用于科学计算、数据分析、工程学和…

泛微OA 自定义多选浏览框

1、建模引擎-》应用建模-》表单 2、建模引擎-》应用建模-》模块 3、建模引擎-》应用建模-》查询 4、把查询页面挂到前端页面。 效果展示&#xff1a; 5、建模引擎-》应用建模-》浏览框 6、流程表单中字段应用

IP-GUARD内置用户系统同步飞书组织架构使用说明

一、功能简介 实现将飞书的通讯录组织架构同步到内置用户系统。 二、功能配置 2.1 飞书创建自建应用 在浏览器上打开飞书开放平台 https://open.feishu.cn ,登录管理员账号后点击开发 者后台 在开发者后台点击创建企业自建应用,填写自建应用程序名称以及描述,设置图标,点…

SSRF靶场

SSRF概述 ​ 强制服务器发送一个攻击者的请求 ​ 互联网上的很多web应用提供了从其他服务器&#xff08;也可以是本地)获取数据的功能。使用用户指定的URL&#xff0c;web应用可以获取图片&#xff08;载入图片&#xff09;、文件资源&#xff08;下载或读取)。如下图所示&…

利用Leaflet + React:构建WEBGIS

React是 Facebook 开发的一个开源库&#xff0c;用于构建用户界面。就其本身而言&#xff0c;Leaflet是一个用于将地图发布到网络的JavaScript 库。这两个工具的组合很简单&#xff0c;允许您创建动态网络地图。在本文中&#xff0c;我们将看到这种组合的一些特征以及一些简单的…

【MySQL数据库 | 第二十五篇】深入探讨MVCC底层原理

前言&#xff1a; 在当今互联网时代&#xff0c;数据库扮演着数据存储和管理的关键角色。对于大型Web应用程序和企业级系统而言&#xff0c;高效地处理并发访问和事务管理是至关重要的。多版本并发控制&#xff08;MVCC&#xff09;是一种数据库事务处理的技术&#xff0c;旨…

三种常见webshell工具的流量特征分析

又来跟师傅们分享小技巧了&#xff0c;这次简单介绍一下三种常见的webshell流量分析&#xff0c;希望能对参加HW蓝队的师傅们有所帮助。 什么是webshell webshell就是以asp、php、jsp或者cgi等网页文件形式存在的一种代码执行环境&#xff0c;主要用于网站管理、服务器管理、…

机器学习中的激活函数

激活函数存在的意义&#xff1a; 激活函数决定了某个神经元是否被激活&#xff0c;当这个神经元接收到的信息是有用或无用的时候&#xff0c;激活函数决定了对这个神经元接收到的信息是留下还是抛弃。如果不加激活函数&#xff0c;神经元仅仅做线性变换&#xff0c;那么该神经网…

蓝桥杯——17

学习视频&#xff1a;18-深搜的剪枝策略练习_哔哩哔哩_bilibili Q&#xff1a;找数字 #include<iostream> #include<cstring> using namespace std; int n; bool ok; void dfs(int num, int cnt) {if (cnt > 19) {return;}if (ok) {return;}if (num % n 0) {…

MySQL-基本SQL语句编写:运算符练习

运算符练习 1.选择工资不在5000到12000的员工的姓名和工资 SELECT last_name,salary FROM employees #where salary not between 5000 and 12000; WHERE salary < 5000 OR salary > 12000;2.选择在20或50号部门工作的员工姓名和部门号 SELECT last_name,department_id…

基于springboot+vue+Mysql的职称评审管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

4月6号排序算法(2)

堆排序 讲堆排序之前我们需要了解几个定义 什么叫做最大堆&#xff0c;父亲节点&#xff0c;以及孩子节点 将根节点最大的堆叫做最大堆或大根堆&#xff0c;根节点最小的堆叫做最小堆或小根堆。 每个节点都是它的子树的根节点的父亲 。 反过来每个节点都是它父亲的孩子 。 …

Java从坚持到精通-SpringSecurity

1.安全框架是什么 安全框架的本质就是一堆过滤器的组成&#xff0c;目的在于保护系统资源&#xff0c;所以在到达资源之前会做一系列的验证工作&#xff0c;这些验证工作通过一系列的过滤器完成。安全框架通常的功能有认证、授权、防止常见的网络攻击&#xff0c;以此为核心拓…

IP地址中网络号的查看方法

IP地址是互联网中设备的标识&#xff0c;它由网络号和主机号两部分组成。网络号用于标识设备所连接的网络&#xff0c;而主机号则用于标识该网络中的具体设备。了解如何查看IP地址中的网络号对于网络管理员和需要进行网络配置的用户来说至关重要。虎观代理将介绍几种常见的查看…

linux之文件系统、inode和动静态库制作和发布

一、背景 1.没有被打开的文件都在磁盘上 --- 磁盘级文件 2.对磁盘级别的文件&#xff0c;我们的侧重点 单个文件角度 -- 这个文件在哪里&#xff0c;有多大&#xff0c;其他属性是什么&#xff1f; 站在系统角度 -- 一共有多少文件&#xff1f;各自属性在哪里&#xff1f…

Vue3---基础2(component)

主要讲解 component 的创建 以及vue插件的安装 Vue.js Devtools 为谷歌浏览器的Vue插件&#xff0c;可以在调试工具内查看组件的数据等 下载 有两种下载方式 1. 谷歌应用商店 打开Chrome应用商店去下载&#xff0c;这个方法需要魔法 2. 极简插件 极简插件官网_Chrome插件下载_…

react渲染列表信息(简单易学)

1.新建个文件夹&#xff0c;启动终端&#xff0c;使用create-react-app my-react命令创建项目&#xff0c;其中my-react是自定义项目名称。 2.删除根目录src文件夹下多余文件&#xff0c;保留index.js和index.css文件 3.安装scss需要的依赖&#xff0c;使用npm install --sav…

常见性能测试工具对比

在性能测试工作中&#xff0c;我们常常会遇到好几个工具&#xff0c;但是每一个工具都有自己的优势&#xff0c;一时间不知道怎么选择。 今天我们就将性能测试常用的工具进行对比&#xff0c;这样大家在选择工具的时候心里就有底啦&#xff01; 阿里云PTS 性能测试PTS&#xff…

【Linux】shell 脚本基础使用

在终端中输入命令可以完成一些常用的操作&#xff0c;但是我们都是一条一条输入命令&#xff0c;比较麻烦&#xff0c;为了解决这个问题&#xff0c;就会涉及到 shell 脚本&#xff0c;它可以将很多条命令放到一个文件里面&#xff0c;然后直接运行这个文件即可。 shell 脚本类…

【UnityRPG游戏制作】Unity_RPG项目之界面面板分离和搭建

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…