Python解题 - CSDN周赛第18期 - 又见背包

news2024/9/23 5:18:47

卧床一周,一觉醒来,恍如隔世,做什么事都提不起兴趣,也不知道这算不算后遗症。

本期的题目还是比较简单的,也有几道做过的题。最后一道照搬过来的背包题也是比较经典的01背包了,整体感觉没有什么值得说的,于是也就一直没更新。


第一题:单链表排序

单链表的节点定义如下(C++): class Node { public: int element; Node * next; } 实现如下函数:Node * sort(Node * head),排序以 head 为头结点的单链表,返回排序后列表的 head 节点;只能修改 next 指针,要求时间复杂度和空间复杂度尽可能优化。

分析

说实话,这题我忘得一干二净,完全想不起来考的是什么了。题面上给的是C++的例子,差不多是构建链表的意思,而题目里却没有要求要以链表的数据结构输出,而给的模板也没有构建链表,于是直接将输入的数组进行排序再输出就pass了,这就很搞笑了,完全不懂这样考有什么意义。

参考代码

n = int(input().strip())
arr = [int(item) for item in input().strip().split()]
print(*sorted(arr))

第二题:合并二叉树

已知两颗二叉树,将它们合并成一颗二叉树。合并规则是:都存在的结点,就将结点值加起来,否则空的位置就由另一个树的结点来代替。

例如:两颗二叉树是: Tree 1 1 / 3 2 / 5 Tree 2 2 / 1 3 4 7 合并后的树为 3 / 4 5 / 5 4 7

分析

和第一题一样,这里虽然说是在考二叉树,但却完全没有构建二叉树的数据结构,玩儿一样。而且题目解释得也有问题,比如例子里Tree1和Tree2的格式,Tree2里的空结点并没有用null来表示。不过问题不大,反正也不存在二叉树,在python看来,这只是两个列表的归并排序。

参考代码

n, m = map(int, input().strip().split())
arr1 = input().strip().split()
arr2 = input().strip().split()
result = []
i = j = 0
while i<n and j<m:
    if arr1[i]=="null":
        if arr2[j]=="null":
            result.append("null")
        else:
            result.append(int(arr2[j]))
    else:
        if arr2[j]=="null":
            result.append(int(arr1[i]))
        else:
            result.append(int(arr1[i])+int(arr2[j]))
    i += 1
    j += 1
result.extend(arr1[i:])
result.extend(arr2[j:])
print(*result)

第三题:n边形划分

已知存在 n 多边形,连接多边形所有对角线,能形成多少区域。

分析

这题也没啥好分析的,纯纯的数学题。现成的公式摆在那里,O(1) 的时间复杂度。 

\frac{(n-1)(n-2)(n^2-3n+12)}{24}

唯一要注意的是python的除法默认会把数据类型转成浮点型,所以要使用整除(//)得到整数型,最后的结果还要对1e9+7取模。

参考代码

n = int(input().strip())
result = (n-1)*(n-2)*(n**2-3*n+12)//24
print(result % 1000000007)

第四题:开心的金明

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 N 元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 N 元。于是,他把每件物品规定了一个重要度,分为5等:用整数1−5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第 j 件物品的价格为 v[j],重要度为 w[j],共选中了 k 件物品,编号依次为 j_{1},j_{2},...,j_{k},则所求的总和为:v[j_{1}]*w[j_{1}]+v[j_{2}]*w[j_{2}]+...+v[j_{k}]*w[j_{k}] 。

请你帮助金明设计一个满足要求的购物单。

第一行,为2个正整数,用一个空格隔开:n,m(其中 N(<30000)表示总钱数,m(<25)为希望购买物品的个数。)

从第2行到第m+1行,第 j 行给出了编号为j−1的物品的基本数据,每行有2个非负整数 v,p(其中 v 表示该物品的价格(v≤10000),p 表示该物品的重要度(1−5))

示例:

示例
输入1000 5
800 2
400 5
300 5
400 3
200 2
输出3900

分析

又是洛谷原题,估计人家也习惯了。官方题库也是吃紧,如果没有此题,本期都不能被称为竞赛。不过话又说回来,本题也是经典的01背包,虽然没什么难度,但还是有必要熟练掌握的。

所谓01背包,就是指背包里的每种东西只有1件,要么选择放,要么不放,其状态非0即1。本题的“金明的房间”就可以看成是一个“背包”,该“背包”的“容量”就是房间里允许放置的物品总价N,物品之间无重复,而要求的就是在该容量内,放置的物品所能构成的总最大价值是多少。

背包问题是动态规划的一种,有机会我们再单独写篇文章总结一下。简单点说,对新手而言(毕竟问哥也是新手摸索过来的,所以还是比较了解新手,尤其是外行新手的感受),背包模板解法的难度主要有二:1)背包的颗粒度;2)背包空间复杂度的优化。我们拿此题为例,一条条来看。

背包的颗粒度

所谓的“颗粒度”(可能只有问哥这么叫),指的是背包所能容纳的最小物品的体积(本题里指的是物品单价,下同)。理解这一点很关键的原因是,我们是直接利用了数组的下标来表示不同的容量,从而进行回溯和比较。因此,很多题目除非特别提示,默认的背包颗粒度都是1(数组下标的间隔默认都是1)。比如本题里背包的颗粒度也是1,也就是单价为1的物品。但是从示例来看,给出的总价N和每件物品的单价都是100的倍数,所以我们其实也可以尝试把背包的颗粒度变成100来试着解题,这样更省空间(原本需要开1000的数组,颗粒度变成100的话只要开10个)。但是由于题目只明确提到了“每件物品的价格都是整数元”,而并不是“整百”,所以我们不能假设所有的测试数据里都是100的倍数,因此这里我们就用默认的背包颗粒度来开数组即可。

为了求解,我们先按照题目给的数据来列出二维表格如下:

横坐标(每列)表示总价N范围内的每种价格(颗粒度为1,但是为了画图方便,这里简化为颗粒度100),纵坐标(每行)表示每种物品的单价和重要度。需要注意的是,我们在进行背包问题推演求解的过程中,每种物品放入的先后顺序是无关紧要的,这是因为每种物品的背包问题是互相独立的,这也是动态规划应用的基础。而表格中要填写的部分,就是表示在放入可能的物品后,所能构成的最大价值。很显然,我们可以开出一个a[m+1][N+1]的二维数组来表示该表格,而本例中我们要求最大价值N的时候m种物品所能组成的最大价值,就是要求出右下角方格数组元素a[m][N]的值。

之所以需要m+1的行与N+1的列空间,正如你所见,我们需要一个0行0列,来表示什么都不放的时候背包的最大价值,虽然听起来像废话。你也可以把它理解为初始值,而在问哥看来,这一行一列的作用就是为了方便后面的推演计算。所以总而言之,我们需要m+1行、N+1列的数组空间。

下面,我们就可以依次放入物品了(最终结果与放入物品顺序无关),而每放入一件物品,我们都要循环检查这一列,来计算在不同容量的情况下的最优解,而当该列计算完毕,我们放入下一件物品的时候就要以此列数据作为回溯的参考,来进行总价值的比较,从而决定要不要放入下一件物品。

举例来说,当我们放入物品1的时候,由于物品1的单价是800,所以如果背包的容量小于800,我们什么都放不下(这时还无需考虑其他物品),所以只有当容量大于等于800的时候,可以组成的最高总价为1600(800*2),表格更新为下图(我们省略了801、802这样的颗粒度的数据)

(实际上,这一行的数据也是和第0行的数据比较而得来的,每一列和上一行的数据相比,更优的数据放入该行该列。)

接下来,我们放入第2件物品。由于第2件物品的单价更低,总价值更高(400*5=2000),所以和上一行相比,同样容量的情况下,我们优先选择放入第2件物品,而同时我们的总价又无法同时买入两者,所以在800到1000的容量时,我们还是优先放入第2件物品,于是得到更新的表格如下:

而放入第3件物品的时候,我们就需要考虑得多一点了,因为我们的背包容量是可以同时放入物品2和物品3的,但是在只能二选一的情况时,比如背包容量大于等于400,小于700,优先放入物品2可以获得的总价值更高(和上一行的2000做比较),所以我们在背包容量为400到700之间时,优先放入物品2,而在可以同时放入二者时,背包容量大于等于700,我们可以得到的总价值为二者之和(300*5+400*5=3500):

现在我们来看第4件物品,这件物品比较特殊,它的单价与第二件物品相同(都是400),但是重要度却更低(只有3),所以,我们在放入的时候,要参考上一行我们已经得到的结果来决定:当背包容量大于等于400时,显然我们放入物品2可获得的总价值更高;而当容量大于等于700时,我们放入物品2和物品3价值更高;显然如果背包容量大于等于1100,我们可以三者都放,但本例背包最大容量只有1000,所以我们这一行的数据和上一行相同,换句话说,除非背包容量大于1100,第4件物品我们不应该选择。更新表格如下:

最后,我们放入第5件物品,由于该物品的单价最低,所以我们有了更多可以决定的空间:当背包容量为600时,我们之前只能放入物品2/3/4,但这时我们还可以放入物品5,所以这时背包的总价值可以达到2400,而不是之前的2000。(当背包容量为500时,我们也可以同时放入物品3和物品5,但两者的总价值为1900,要小于单独放物品2的总价值2000,所以此处并没有更新。)当背包容量大于等于900时,我们之前只能放入物品2和物品3,此时我们还可以放入物品5,所以总价值可以达到3900,也就是我们最终的答案。

由上面推演的过程,我们可以总结如下规律:

  1. 每一行的计算都只需要参考上一行的数据,而与更早的数据无关,因此,物品放入的顺序并不重要。
  2. 每一格 dp[i][j] 的计算都是在比较两种情况:①不放入当前物品(上一行的数据 dp[i-1][j]);②放入当前物品后,剩下的空间是否还能放其他物品(v*p+dp[i-1][j-v],v*p表示放入当前物品的价值,dp[i-1][j-v]表示放入当前物品后,在上一行里查询剩下的空间所能得到的最大价值);然后再比较这两种情况所能得到的最大值,放入表格。 

上面的第二点又可以写成动态规划的状态转移方程:

dp[i][j]=max(dp[i-1][j], v*p+dp[i-1][j-v])

可以看到,这里的 v 既表示了物品的单价,又参与了下标的计算(j - v),这里就显示出颗粒度的重要性,直接使用容量(单价)作为数组下标进行回溯查询,相信这里是部分编程新手的难点之一。

于是,我们把上述推演过程放入循环,用代码表示出来,就可以得到如下解题代码。

参考代码1

n, m = map(int, input().split())
dp = [[0]*(n+1) for _ in range(m+1)] # 创建上图描述的二维表格
for i in range(1, m+1):
    v, p = map(int, input().split()) # 循环m次,得到每件物品的单价和重要度
    for j in range(1, n+1): # 循环计算每一列
        if j < v: continue # 如果单价大于当前背包容量,无法回溯,跳过
        dp[i][j] = max(dp[i-1][j], v*p+dp[i-1][j-v]) # 状态转移方程
print(dp[m][n])

背包空间复杂度的优化

上面的代码代表了01背包问题的基本解题思路,但是,你看到的模板可能会长得不太一样,也许还有的教程会说不同背包循环的方向不同等等。这是因为,在循环的过程中,我们发现二维数组的空间太浪费了。比如我们发现,除了上一行(dp[i-1][j])的数据以外,之前的数据就完全都不需要了,因此我们可以省去dp[i-2]之外的行数据,只留下dp[i]和dp[i-1]两行数据;而在此基础上更进一步,我们是否真的需要两行数据呢?然后我们发现,如果我们倒序计算的话,只需要一行数据即可,从而将空间复杂度由二维空间压缩到一维。

另外,就此题而言,因为只需要右下角的数据,因此倒序计算的话,最后一件物品的第一个计算结果就是我们要的答案,所以最后一行的数据甚至都不需要完全计算。改进的代码如下:

参考代码2

n, m = map(int, input().split())
dp = [0]*(n+1) # 一维数组空间足矣
for i in range(m-1): # 留下最后一个物品
    v, p = map(int, input().split())
    for j in range(n, v-1, -1): # 倒序计算,计算到背包容量等于物品单价为止
        dp[j] = max(dp[j], v*p+dp[j-v])
v, p = map(int, input().split())
print(max(dp[n], v*p+dp[n-v])) # 最后一个物品只需计算一次即得到答案

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

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

相关文章

linux常用命令(四)- 文件备份解压缩

查看压缩文件信息 - zipinfo zipinfo命令用于列出压缩文件信息。 语法 zipinfo [-12hsvz][压缩文件]-1 只列出文件名称。-2 此参数的效果和指定"-1"参数类似&#xff0c;但可搭配"-h",“-t"和”-z"参数使用。-h 只列出压缩文件的文件名称。-s…

c++11 标准模板(STL)(std::deque)(四)

定义于头文件 <deque> std::deque 元素访问 访问指定的元素&#xff0c;同时进行越界检查 std::deque<T,Allocator>::at reference at( size_type pos ); const_reference at( size_type pos ) const; 返回位于指定位置 pos 的元素的引用&#xff0c;有边…

如何在PVE(Proxmox)中安装OpenWrt软路由?

出处&#xff1a; https://www.928wang.cn/archives/1763.html https://blog.itwk.cc/post/pve_install_openwrt.html 工具准备 WinSCP或者XFTPOpenWrt镜像(自行寻找)安装好PVE的主机一台 安装教程 镜像上传 将下载好的OpenWrt img镜像上传到 PVE主机中(这里使用XFTP工具) 选…

MySQL中的DDL、DML、DCL、DQL

SQL分类 DDL(Data Definition Language)数据定义语言 用来定义数据库对象&#xff1a;数据库&#xff0c;表&#xff0c;列等。关键字&#xff1a;create, drop,alter 等 DML(Data Manipulation Language)数据操作语言 用来对数据库中表的数据进行增删改。关键字&#xff1a;i…

vue-element-表格 Excel 【导入】功能 (2023元旦快乐~~~)

一、页面表格导入功能 我们借鉴vue-element-admin文件来学习表格导入功能,如果你有vue-element-admin的完整文件&#xff0c;可以去这里找 or 用我这里的代码 1. 整体复制到你要用到的页面 <template><div class"app-container"><upload-excel-com…

unreal engine 纹理动态运动的实现

先用ps涉及一张图,发光的地方为白色 下图实际上边缘是相连的白色 split_line.jpgue新建材质 基础色vector3 随便选择一个偏灰的颜色 自发光 TextureCoordirate ->Panner->图片rgb->*发光常量 * 20自发光 预览效果 通过修改纹理协调器的V垂直平铺控制条纹数量 image.pn…

mybatis插件

Configuration组成 Mapper映射器 3个部分组成&#xff1a; MappedStatement 保存一个节点(select | insert | update | delete) &#xff0c;包括我们配置的sql&#xff0c;&#xff0c;sql的id&#xff0c;&#xff0c;缓存信息&#xff0c;&#xff0c;resultMap,parameterT…

Redis高并发锁(三)分布式锁

在很多情况下&#xff0c;你的数据库不支持事务&#xff0c;分布式部署也使得你无法去使用JVM锁&#xff0c;那么这种时候&#xff0c;你可以考虑用分布式锁 文章目录分布式锁1. 实现方式2. 特征3. 操作4. 代码改造5. 测试优化1. 递归改成循环2. 防止死锁3. 防误删4. LUA脚本 保…

Arduino code for RS-365PW 16120

Pictures These pictures are from Baidu Search. Picture 1: Installment Picture 2: Appearance Picture 3: Encoder of Motor Picture 4: Pins location and number Physical Specification Brand: Mabuchi Motor (万宝至电机)Type: RS-365PW 16120 Body length&#xff1…

学生抢课接口(高并发入门)

目录 使用Mysql 常规测试 张三测试 流程总结 redis优化 修改代码 测试 使用分布式锁 总结 使用Mysql 常规测试 原始代码: Override Transactional public ResponseResult selectCourse(SelectParmas selectParmas) {if (Objects.isNull(selectParmas)){return new …

【python游戏】新的一年快来变身兔兔战士打败獾守护兔兔吧~

前言 大家早好、午好、晚好吖 ❤ ~ 一只快乐兔&#xff0c; 来到快乐山&#xff0c;喝了快乐泉&#xff0c; 又到快乐殿&#xff0c;吃了快乐莲&#xff0c;遇到快乐仙&#xff0c; 听了快乐言&#xff1a;快乐很简单&#xff0c;快乐在身边&#xff0c;快乐无极限&#xff…

C++中STL的vector扩容机制

目录前言发生扩容扩容机制size()和capacity()reserve()和resize()前言 前阵子面试的时候&#xff0c;被问到往vector中插入一个数据可能会发生什么&#xff1f; 我答:可能会扩容; 为啥vector支持变长&#xff1f; 我答:它实在堆上动态申请内存&#xff0c;因此有自己的一套扩容…

Redis集群系列十 —— 集群伸缩之收缩

集群收缩原理 集群收缩就是让其中一些节点安全下线。 所谓的安全下线指的是让一个节点下线之前&#xff0c;把其负责的所有 slots 迁移到别的节点上&#xff0c;否则该节点下线后其负责的 slots 就没法继续提供服务了。 收缩流程如下&#xff1a; 需求 前面扩容完成后&…

字符串大小写转化,有序数组二分查找个人心得等若干内容

tips 1. 在电脑里面&#xff0c;任何一切字符&#xff0c;当一看到的时候&#xff0c;脑子里面就要把它转化成ACSII值。如while(0)&#xff0c;可以实现死循环。 2. 统计整形数组的长度不能用strlen()&#xff0c;别一天到晚用到底&#xff0c;strlen统计的是字符数组的长度 …

在wsl下开发T113的主线linux(1)-准备wsl开发环境

首先在win10或win11下安装wsl&#xff0c;选择wsl1或者wsl2都可以&#xff0c;wsl2的性能更高一些&#xff0c;wsl1的跨系统文件操作速度更快一些&#xff0c;我这里因为有一些工程在win文件系统下&#xff0c;所以选择了wsl1&#xff0c;发行版使用最新的Ubuntu 22.04.01 LTS。…

MySQL隐式转换

隐式转换概念 When an operator is used with operands of different types, type conversion occurs to make the operands compatible. Some conversions occur implicitly. 当运算符与不同类型的操作数一起使用时&#xff0c;将进行类型转换以使操作数兼容。某些转换是隐式发…

2022年年终总结---新方向,新期待

2022年行将结束&#xff0c;回首年初立下的flag&#xff1a; (1)完成OpenCoord版本升级&#xff0c;增加ITRF框架及历元转换、EGM2008查询功能&#xff1b; (2)完成多波束开源项目的数据读取和显示操作。 任务(1)已经完成了&#xff0c;任务&#xff08;2&#xff09;没有完成。…

力扣(LeetCode)2351. 第一个出现两次的字母(C++)

哈希集合 开哈希集合&#xff0c;遍历字符串&#xff0c;未出现的字母加入哈希集合&#xff0c;如果字母出现过&#xff0c;返回这个字母即可。 class Solution { public:char repeatedCharacter(string s) {unordered_set<char> S;for(auto &c:s)if(!S.count(c)) …

第二证券|72家公司接待机构过千,迈瑞医疗热度“断层领先”

2022年最终一个交易日&#xff0c;沪指以红盘收官&#xff0c;但年内A股商场震荡起伏&#xff0c;三大指数均收跌&#xff0c;其间&#xff0c;沪指全年下跌15%&#xff0c;创业板指跌近三成。 调研活动是出资者挖掘上市公司信息的重要来源&#xff0c;是洞悉商场主力资金意向的…

记录NCNN Yolov5部署华为鸿蒙系统踩过的坑

目录 踩坑一&#xff1a;Android Studio连接鸿蒙系统踩过的坑 踩坑二&#xff1a;配置Android studio环境 踩坑三&#xff1a;打开文件夹的位置 踩坑四&#xff1a;No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-androideabi 总结 踩…