原创 | 你能想出解法,让你的基友少氪金吗?
大家好,今天codeforces专题选择的是上周education比赛的C题。
Education是codeforces的一种特殊赛事,它的主要作用是教育,也就是让更多的人了解codeforces的比赛机制。所以education赛事的题会相对来说容易一些,更加适合新手。我选的这道题虽然是C题,但是难度并不高非常基础。
链接:https://codeforces.com/contest/1418/problem/C
这题通过的人数有3600多,相比之前介绍的题算是比较少,但是题目难度其实更低一些。我们废话不多说来看题目。
题意
这道题的题意也很有意思,背景也是游戏。说是有一天你和你的基友一起在家打游戏,这个游戏一共有n个boss。这些boss的难度不同,有些boss简单,有些boss困难。你的技术要比基友的好一些,你们两人轮流打boss。
游戏规定每次进行游戏最少打1个boss,最多打两个boss。由于你的实力更好,你可以战胜所有的boss。但是你的基友比较菜,只能打得过简单的boss,如果碰上hard模式的boss就只能氪金。基友的钱也是钱,你们希望在尽量少氪金的前提下把游戏通关。现在已知所有boss的难易情况并且基友先开始游戏,请问在最佳策略下,最少需要氪金多少次?
样例
首先给定一个数字t,表示测试数据组数。对于每组数据,给定一个数字n,表示boss的数量。接着给定n个0或者1的整数,0表示boss是简单模式,1表示是困难模式。要求返回一个数字,即最少的氪金次数。其中
input:
8
1 0 1 1 0 1 1 1
output:
2
基友先杀1和2两个boss,氪金一次。
“我”杀3和4号boss
基友杀5号boss
“我”杀6和7号boss
基友击杀8号boss,氪金一次,总共氪金两次。
题解
这道题我们最先想到的可能就是贪心,比如我们可以想到一种贪心策略,就是每次基友杀怪的时候先杀1个,然后看第二个是0还是1,如果是0的则一起杀了,否则不杀留给“我”。
我们可以用之前介绍过的等价判断法来判断一下这个贪心策略可不可行,对于这道题而言,贪心的本质是让氪金的次数最少。所以当基友的第二个怪是0的时候,杀和不杀对于当前的氪金次数来说是没有影响的。但是对于后面的局面是会有影响的,并且可能会出现不同的结果。
比如我们可以找到一个例子10011,基友杀不杀第二个怪,直接影响后面的结果。如果基友杀了,那么不论“我”怎么选,基友都必须要至少再氪金一次。如果基友不杀,那么“我”杀第二个怪,基友再杀第三个怪,最后两个boss都交给“我”,那么基友全局只需要氪金一次。所以贪心算法不可行。
动态规划
如果你熟悉动态规划的话,那么几乎可以发现这是一道经典的动态规划问题。对于每一个怪来说,它都有两种状态,分别是被基友杀或者是被“我”杀。我们用0和1来分别表示,0表示被基友杀,1表示被“我”杀。一共有n个怪,所以我们可以用一个n * 2的数组来记录所有怪的状态。
对于第i个怪而言,如果它是被“我”杀的,那么它可以由基友杀了i-1或者是i-2个怪的状态转移得到。比如如果从基友杀了i-1转移得到,说明“我”杀了i,否则说明“我”不仅杀了i,还杀了i-1。同理i被基友杀的情况也是一样,所以这个状态转移方程就非常明显了。
import sys
t = int(input())
for _ in range(t):
n = int(input())
arr = list(map(int, input().split(' ')))
dp = [[sys.maxsize, sys.maxsize] for _ in range(n+2)]
dp[0][1] = 0
for i in range(1, n+1):
if i > 1:
# 如果i > 1,那么说明可以杀两个
# 0表示基友杀怪的情况,基友可以杀1个从i-1转移得到,也可以杀2个从i-2转移得到
# 需要加上氪金的次数
dp[i][0] = min(dp[i-1][1] + arr[i-1], dp[i-2][1] + arr[i-2] + arr[i-1])
# 我杀怪不用氪金,直接赋值即可
dp[i][1] = min(dp[i-1][0], dp[i-2][0])
else:
# i=1,那么只能杀一个
dp[i][0] = dp[i-1][1] + arr[i-1]
dp[i][1] = dp[i-1][0]
print(min(dp[n][0], dp[n][1]))
这道题非常的基础,可以说是动态规划的基础问题了。如果对动态规划这个概念不是很熟悉的话,非常建议动手做一做,加深一下印象。
- Thrift抛直接内存OOM一点解决思路
- 小顶堆Java实现
- Tomcat源码分析一:源码导入
- 如何使用Metasploit对安卓手机进行控制
- 关于MySQL DNS解析探究之二:unauthenticated user
- Thrift Direct Memory OOM问题解决方法
- Mapreduce程序中reduce的Iterable参数迭代出是同一个对象
- 内部威胁那些事儿(二):系统破坏
- 从用户行为去理解内容-item2vec及其应用
- Dubbo与Zookeeper、SpringMVC整合和使用(入门级)
- Websocket HandShake Sec-WebSocket-Accept 生成策略
- 关于JVM CPU资源占用过高的问题排查
- ActiveMQ简单介绍以及安装
- Java Process destroy方法kill进程,返回码测试
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Docker安装harbor仓库、更改端口、跨服务器访问
- 字符串处理算法题 -> 替换空格
- 简单二分法查找
- 链表之Python与C
- Python MQTT
- 从键盘输入一个十进制个位数,在屏幕上显示相应数量的该数。 例如,输入3,屏幕上将显示“333”。
- ESP32 OTA详解-中文翻译版
- 汇编语言从键盘输入一个字符串(串长不大于80)以十进制输出字符串中非字母字符的个数(不是a to z或 A to Z)
- 求100以内所有奇数的和,存于字变量X中。
- pyinstaller打包出错numpy.core.multiarray failed to import
- 从包含10个无符号数的字节数组array中选出最小的一个数存于变量MIN中,并将该数以十进制形式显示出来。
- 可修改内容的优先级队列
- STM32定时器与中断整理
- 计算CNN卷积神经网络中各层的参数数量「附代码」
- C++ 万字长文第一篇---拿下字节面试