Python解题 - CSDN周赛第32期 - 运输石油(三维背包)

news2024/11/25 2:34:28

上期周赛因为最后一题出现bug,再加上都是经典的模板题,问哥就懒得写题解了。

本期也是有两道考过的题目,不过最后一题因为考到了背包问题的特殊类型,还是值得拿出来记个笔记。


第一题:传奇霸业

传奇霸业,是兄弟就来干。小春(HP == a)遇到了一只黄金哥布林(HP == x)。小春每次能对哥布林造成b点伤害,哥布林每次能对小春造成y点伤害。作为玩家的小春怎么可能随便让哥布林打死呢!他有治疗神药,每次能恢复c点HP。HP无上限。小春需要操作多少次才能打死哥布林?(治疗+攻击)

输入描述:第一行输入a,b,c。(1<=a,b,c<=1000)。第二行输入x,y。(1<=x<=1000,0<=y<c)

输出描述:输出最小操作次数。

示例:

示例
输入11 6 100
12 5
输出2

分析

模拟。但本题出得不是特别好,因为对游戏的流程没有说清楚。不管有没有玩过游戏,读完此题都会有一定程度的迷惑:是先攻击怪物(哥布林)还是先受攻击?是回合制还是即时制(在被攻击前可以无限吃药)?问哥一开始以为是即时制,所以最初的思路是一旦自己的HP小于怪物的攻击力,就不断吃药,直到大于怪物攻击力。。。然后无法通过,才发现原来是回合制。修正思路为:如果自己的HP大于怪物的攻击力,或怪物的HP小于自己的攻击力(一击毙命,没必要浪费回合回HP),则发动一次攻击;否则就吃一次药恢复HP。然后轮到怪物发动一次攻击,自己的HP减去怪物的攻击力,然后循环直到怪物HP小于等于0。最后循环的次数就是答案。

参考代码

a, b, c = map(int, input().strip().split())
x, y = map(int, input().strip().split())
res = 0
while x > 0: # 循环直到怪物体力小于等于0
    if a > y or b >= x: # 如果自己的HP大于怪物的攻击力,或者怪物的HP小于自己的攻击力
        x -= b # 选择攻击
    else:
        a += c # 选择吃药回复HP
    res += 1 # 回合加一
    a -= y # 自己受到怪物的攻击,HP减少
print(res)

第二题:严查枪火

X国最近开始严管枪火。像是“ak”,“m4a1”,“skr”。都是明令禁止的。现在小Q查获了一批违禁物品其中部分是枪支。小Q想知道自己需要按照私藏枪火来关押多少人。(只有以上三种枪被视为违法)

输入描述:第一行输入整数n.(1<=n<=10000)表示携带违禁物品的人数。以下n行表示违禁物品的名称。

输出描述:输出需要按照私藏枪火来关押的人。

示例:

示例
输入3
Dsd
ak
232asd
输出

1

分析

第6期考过的老题,以前也写过题解。

没什么好分析的,就是依次检查列表中的字符串是否等于这三种枪火所代表的字符串,最后输出相等的个数即可。

参考代码

n = int(input().strip())
res = 0
for _ in range(n):
    temp = input().strip()
    if temp in {"ak", "m4a1", "skr"}:
        res += 1
print(res)

第三题:蚂蚁家族

小蚂蚁群是一个庞大的群体,在这个蚂蚁群中有n只小蚂蚁 ,为了保证所有蚂蚁在消息传送的时候都能接收到消息,需要在他们之间建立通信关系。就是要求小蚂蚁都可以通过多只或者直接联系到其他人。已知几条小蚂蚁之间有通信关系,请问还需要再新建至少多少条关系?

输入描述:第一行输入整数n,m;n为小蚂蚁总数;m为关系数。(1<=n,m<=1000)。以下m行每行m对整数x,y。(代表x与y有联系)

输出描述:输出最少需要新建关系数。

示例:

示例
输入4 3
1 2 
2 3
3 4
输出

0

分析

第12期考过(好像并不遥远),可以参考以前写的题解。

官方很喜欢考并查集,问哥当初解这道题的时候,是靠着测试用例的缺陷骗分过了。后来又自己琢磨了使用集合的方法,不过这次再次遇到,就可以用已经熟悉的并查集模板来解了。

算法原理还是一样,先定义并查集的查集函数find(),再把每次输入边的顶点合并在一起,都指向同一个“祖先”,最后统计祖先的个数,就表示了现在已存在的连通子图的个数,要把它们连在一起,只需要个数减一条边就可以了。

参考代码

n, m = map(int, input().strip().split())
ants = list(range(n+1))
def find(i):
    if ants[i] == i: return i
    ants[i] = find(ants[i])
    return ants[i]
for _ in range(m):
    a, b = map(int, input().strip().split())
    ants[find(a)] = find(b)
result = set()
for i in ants[1:]:
    j = find(i)
    result.add(j)
print(len(result)-1)

不过比赛中本题的数据有坑,有一个测试数据的节点编号超过了 n,于是在查集的时候报了下标越界的错误, 所以上面的代码要稍微修改一下。只要增加一个节点,最后再判断该节点有没有边即可。


第四题:运输石油

某石油公司需要向A、B两地运输石油。两地的需求量不同,而一辆车只能装载一定量的石油。经过计算A地需要a辆车,B地需要b辆车运输才能满足需求。现在一共有n辆车分布在各地,每辆车前往A、B两地运输石油均可以获得一定不等的利润。现在请你安排a辆车前往A地,b辆车前往B地运输石油,使得在满足A、B两地石油需求的前提下,获得最大的利润。每辆车只能前往一地运输石油。

输入描述:输入第一行包含三个整数n,a,b,分别表示公司的车辆数量和A,B两地车辆所需数量,保证a+b<=n。(1<=n<=1000)。接下来有n行,每行两个正整数x,y,分别表示该车完成A地任务的利润和B地任务的利润。

输出描述:输出仅包含一个正整数,表示最大获得的利润和。

示例:

示例
输入5 2 2
4 2
3 3
5 4
5 3
1 5
输出18

分析

题目本身不太难,难的是对测试数据的优化。

从题干来看,还是背包问题的一个扩展子类:三维背包问题。也就是,存在两个背包(A地和B地,两地需要的车的数量类似于普通背包的容积),怎样分配物品(n辆车,类似于体积)才能得到最优解(最大价值)。

既然是背包问题,就存在状态转移。我们可以用 dp[i][j][k] 的三维数组来表示 i 辆车中有 j 辆去往A地,k 辆去往B地的最大价值。那我们如果增加了一辆车,也就是检查第 i+1 辆车的时候,dp[i+1][j][k] 的值怎样得到呢?这时,我们有三个选择:

  1. 不派这辆车,那么 dp[i+1][j][k] = dp[i][j][k]
  2. 派这辆车去A地,那么 dp[i+1][j][k] = dp[i][j-1][k]+profit_{a} (因为这辆车去了A地,那么前面 i 辆车里就少派一辆去A地的车)
  3. 派这辆车去B地,那么 dp[i+1][j][k] = dp[i][j][k-1]+profit_{b} (同上,因为这辆车去了B地,那么前面 i 辆车里就少派一辆去B地的车)

很显然,最终 dp[i+1][j][k] 的值就等于这三项最大值。于是,可以得到状态转移方程如下:

dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j-1][k]+profit_{a},dp[i-1][j][k-1]+profit_{b})

如果熟练掌握了基础背包问题,这里很容易就可以看出:因为在状态转移时只用到了 dp[i-1] ,所以可以类似01背包,使用循环数组降低空间复杂度,减去一维。但是同样地,状态转移的时候,需要逆序更新。

降维后的转移方程如下:

dp[j][k] = max(dp[j][k], dp[j-1][k]+profit_{a},dp[j][k-1]+profit_{b})

所以,到这里我们关于这道题的解法已经很清楚了,代码整理如下:

参考代码一

n, a, b = map(int, input().strip().split())
dp = [[0]*(b+1) for _ in range(a+1)]
for i in range(1, n+1):
    profit_a, profit_b = map(int, input().strip().split())
    for j in range(a, -1, -1): # 因为使用了滚动数组优化空间,需要逆序更新
        for k in range(b, -1, -1): # 同上
            if j > 0: dp[j][k] = max(dp[j][k], dp[j-1][k]+profit_a)
            if k > 0: dp[j][k] = max(dp[j][k], dp[j][k-1]+profit_b)
print(dp[a][b])

这里要注意边界问题,也就是当 j=0 或 k=0 时,无须从 j-1 或 k-1 的状态更新过来。

然鹅,上面的代码是无法pass这道题的。原因就在于测试数据范围太大,而我们的算法时间复杂度是 O(n*a*b),其中 a+b \leq n,而测试数据的范围在 1e4,计算量级相当于1e12 (O(n^3))。必须考虑优化。

优化

不幸的是,关于三维背包,问哥并不知道是否存在能够优化时间复杂度到 O(n^2) 甚至更低的算法。但是上面的状态转移过程中,很显然存在很多无意义的计算,也就是派往A地和B地的车的数量存在 a+b\leq n 的限制。于是,可以通过剪枝来进行一定的优化。

在下面两行代码里,jk 分别代表派往A地和B地的车的数量,而这两个数量的范围使用了 [0,a] 和 [0,b] 。

for j in range(a, -1, -1): 
    for k in range(b, -1, -1): 

不难发现,当我们只有 i 辆车的时候:

  • 如果 a>i ,我们最多只能派 i 辆,而不是 a 辆车,去往A地;
  • 如果 a\leq i,我们最多也只需要派 a 辆车去A地。

所以,我们派往A地的车辆数量的上限是 min(i, a) 。

同样地,因为我们在 i 辆车里选择了 j 辆去A地,那么我们最多只能派 i-j 辆车去往B地。所以派往B地车辆数量的上限是 min(i-j, b) 。

我们再来看范围的下限。常识里,下限应该是0,也就是一辆都不派,但真的是这样吗?

本题所谓的下限,也就是在 i 辆车里最少派多少辆车到两地。以A地为例,因为题目要求一定要派 a 辆车到A地,那么我们在 i 辆车里最少要派的 j 辆车,加上剩下的最多能派往A地的车,必须要等于 a。剩下的 n-i 辆车里最多也只能派 n-i 辆车,也就是全部派往A地,所以最小的 j 要满足 j+(n-i)=a 。显然,如果n-i\geq a,也就是说剩下的车辆数量大于等于 a 的话,j 的最小值是 0。于是,可以得到 j 的下限是 max(0, a-n+i)

再来看B地。同样的道理,题目要求一定要派 b 辆车到B地,也就是说,我们在 i-j 辆车里最少要派的 k 辆车,加上剩下的最多能派往B地的车,必须要等于 b。但是,考虑到剩下的车里至少要保留 a-j 辆会派往A地,所以剩下能派往B地的车数量最多只有 n-i-(a-j) 辆。于是,最小的 k 要满足 k+n-i-(a-j)=b 。展开,可以得到 k 的下限是 max(0, b-n+i+a-j) 。

而在此上下限范围之外的数据,我们是压根不需要计算的。因为逻辑不合理,它们对最终结果不会产生影响,于是我们可以进行剪枝。 更新代码,最终得到pass代码如下:

参考代码二

n, a, b = map(int, input().strip().split())
dp = [[0]*(b+1) for _ in range(a+1)]
for i in range(1, n+1):
    aa, bb = map(int, input().strip().split())
    for j in range(min(i, a), max(0, a-n+i)-1, -1):
        for k in range(min(i-j, b), max(0, b-n+a+i-j)-1, -1):
            if j > 0: dp[j][k] = max(dp[j][k], dp[j-1][k]+aa)
            if k > 0: dp[j][k] = max(dp[j][k], dp[j][k-1]+bb)
print(dp[a][b])

由此可见,虽然算法的渐进时间复杂度没有改变,但是通过剪枝,还是可以通过测试用例,说明测试数据中存在许多干扰的无用数据,想必这也是考点之一吧。 

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

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

相关文章

Unity高程图生成

序大概就是根据一个灰度图&#xff0c;生成一个地形。分两步来实现吧&#xff1b;首先&#xff0c;用随机数生成地形&#xff1b;然后&#xff0c;根据灰度图生成地形。小白&#xff0c;没啥基础&#xff0c;所以只能慢慢来。参考&#xff1a;【萌新图形学】地形网格生成入门 含…

基于stm32电梯管理系统设计

基于stm32电梯管理系统设计这里记录一下以前自己做的嵌入式课程设计&#xff0c;报告中的图片和文字太多了&#xff0c;全部一个一个把搬过来太麻烦了,需要完整文本和代码自行q我963160156&#xff0c;也可在微信公众号 *高级嵌入式软件* 里回复 *电梯* 查看完整版文章摘要关键…

Oracle Apex 21.2 安装过程

什么是 Oracle APEX&#xff1f; Oracle APEX 是广受欢迎的企业级低代码应用平台。借助该平台&#xff0c;您可以构建功能先进的可扩展安全企业应用&#xff0c;并在任何位置&#xff08;云或内部部署&#xff09;部署这些应用。 使用 APEX&#xff0c;开发人员可快速开发并部…

域组策略自动更新实验报告

域组策略自动更新实验报告 域组策略自动更新实验报告 作者: 高兴源 1要求、我公司为了完善员工的安全性和系统正常漏洞的维护&#xff0c;所以采用域组策略自动更新的方法来提高账户安全性&#xff0c;减少了用户的错误。 1.实验环境如下1台2008r2一台创建域&#xff0c;一台wi…

【云原生】k8s中Pod进阶资源限制与探针

一、Pod 进阶 1、资源限制 当定义 Pod 时可以选择性地为每个容器设定所需要的资源数量。 最常见的可设定资源是 CPU 和内存大小&#xff0c;以及其他类型的资源。 当为 Pod 中的容器指定了 request 资源时&#xff0c;调度器就使用该信息来决定将 Pod 调度到哪个节点上。当还…

嵌入式 STM32 步进电机驱动,干货满满,建议收藏

目录 步进电机 1、步进电机驱动原理 2、步进电机驱动 3、步进电机应用 1、第一步&#xff1a;初始化IO口 2、设置行进方式 四、源码 步进电机 步进电机被广泛应用于ATM机、喷绘机、刻字机、写真机、喷涂设备、医疗仪器及设备、计算机外设及海量存储设备、精密仪器、工业…

_improve-3

createElement过程 React.createElement()&#xff1a; 根据指定的第一个参数创建一个React元素 React.createElement(type,[props],[...children] )第一个参数是必填&#xff0c;传入的是似HTML标签名称&#xff0c;eg: ul, li第二个参数是选填&#xff0c;表示的是属性&#…

String、StringBuffer和StringBuilder的详解

目录 一、String讲解 1.String&#xff08;String字符串常量&#xff09; 2.String 拼接方式与性能的影响 二、StringBuffer 和 StringBuilder 讲解 1.StringBuffer 和 StringBuilder 使用场景:(StringBuffer、StringBuilder字符串变量) 2.StringBuffer的使用 3.StringB…

shell脚本常用命令

shell概述 shell是一个命令行解释器&#xff0c;它接收应用程序/用户命令&#xff0c;然后调用操作系统内核。 shell还是一个功能强大的编程语言&#xff0c;易编写、易调试、灵活性强。 shell解析器 查看系统自带的所有shell解析器 cat /etc/shells查看系统默认的shell解析…

超算中心、并行计算

现在超算中心已经迅速发展 合肥&#xff1a; 合肥先进中心 合肥曙光超算中心平台 合肥安徽大学超算中心 合肥中科大超算中心 合肥中科院超算中心 合肥大一点的公司都会有自己的集群&#xff0c; 超算中心又称为集群&#xff0c;一般集群是小型服务器组成&#xff0c;超…

EasyRecovery16免费的电脑的数据恢复工具

常见的数据恢复有两种方式&#xff0c;第一种方式是找别人恢复&#xff0c;按照市场价来说&#xff0c;数据恢复的价格每次在100-500之间&#xff0c;但这种方式容易使自己设备上的隐私资料泄露出去&#xff0c;不安全。 另一种方式则是自己学会数据恢复的方法&#xff0c;有问…

逻辑回归

逻辑回归 在分类问题中&#xff0c;要预测的变量y为离散值&#xff08;y0~1&#xff09;&#xff0c;逻辑回归模型的输出变量范围始终在 0 和 1 之间。 训练集为 {(x(1),y(1)),(x(2),y(2)),...,(x(m),y(m))}\{(x^{(1)},y^{(1)}),(x^{(2)},y^{(2)}),...,(x^{(m)},y^{(m)})\} {…

地址,指针,指针变量是什么?他们的区别?符号(*)在不同位置的解释?

指针是C语言中的一个重要概念&#xff0c;也是C语言的一个重要特色&#xff1b;使用指针&#xff0c;可以使程序简洁、紧凑、高效。不掌握指针&#xff0c;就没有掌握C语言的精华。 目录 一、定义 1.1地址 1.2指针 1.3指针变量 1.4指针和指针变量的区别 二、使用指针变量…

C#关于HWindowControl实现一些便捷功能——(缩放与拖动图像)

C#关于HWindowControl实现一些便捷功能——&#xff08;缩放与拖动图像&#xff09;一、关于Hwindow窗体显示的part二、以鼠标为中心的缩放三、以鼠标拖动移动图片一、关于Hwindow窗体显示的part 首先 HWindowControl 控件的尺寸是固定的&#xff0c;当我们在这个固定的尺寸中…

C++类和对象:构造函数和析构函数

目录 一. 类的六个默认成员函数 二. 构造函数 2.1 什么是构造函数 2.2 编译器自动生成的默认构造函数 2.3 构造函数的特性总结 三. 析构函数 3.1 什么是析构函数 3.2 编译器自动生成的析构函数 3.3 析构函数的特性总结 一. 类的六个默认成员函数 对于任意一个C类&…

零基础如何入门网络安全(黑客)

我经常会看到这一类的问题&#xff1a; 学习XXX知识没效果&#xff1b;学习XXX技能没方向&#xff1b;学习XXX没办法入门&#xff1b; 给大家一个忠告&#xff0c;如果你完全没有基础的话&#xff0c;前期最好不要盲目去找资料学习&#xff0c;因为大部分人把资料收集好之后&a…

三天吃透Java虚拟机面试八股文

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

趣味数学题存疑待证1

原文出自&#xff1a;球面上随机 N 个点在同一个半球上的概率 要求任意N个点&#xff0c;全在同一个半球上的概率&#xff0c;我们需要构造使得分母为有限的样本集合&#xff0c;分子则为有N个点在同一半球的情况集 首先对任意N个点取其对称点使得可划分点为2N&#xff0c;在…

【数据库】第七章 数据库设计

第七章数据库设计 数据库设计概述 数据库设计的基本步骤 需求分析概念结构设计逻辑结构设计物理结构设计数据库实施数据库运行和维护 需求分析 收集需求&#xff0c;理解需求 收集各个角色的需求 概念数据库设计 建立概念模型 &#xff0c;E-R图/IDEF1x图 消除冲突&…

【JDK8新特性之日期时间API-案例实操】

一.JDK8新特性之日期时间API-案例实操 之前我们学习了Stream流、Lambda表达式以及方法引用等相关的内容&#xff0c;如果想学习的同学可以看一下之前的文章&#xff0c;接下来我们一起学习一下关于JDK8中新日期时间API的使用。 二.JDK中原始日期时间存在的问题 设计不合理&…