奇怪的比赛(Python,递归,状态压缩动态规划dp)

news2024/12/25 22:39:14

目录

  • 前言:
  • 题目:
  • 思路:
    • 递归:
      • 代码及详细注释:
    • 状态压缩dp:
      • 代码及详细注释:
  • 总结:

前言:

这道题原本是蓝桥上的题,现在搜不到了,网上关于此题的讲解更是寥寥无几,仅有的讲解也只是递归思想,python讲解和状态压缩dp的解决方法都没有,这里就带大家用状态压缩dp方法来解决此题。

题目:

大奖赛计分规则:
每位选手需要回答10个问题(其编号为1到10),越后面越有难度。答对的,当前分数翻倍;答错了,则扣掉与题号相同的分数(选手必须回答问题,不回答按错误处理)。每位选手的起步分都是10分,某获胜选手最终得分刚好是100分,请推断出哪个题目答对了,哪个题目答错了?(答对的记为1,答错的记为0,则10个题目的回答情况可用仅含1和0的串来表示)。任务是算出所有可能情况,每个答案占一行。(填空题)

输出:
0010110011
0111010000
1011010000

思路:

递归:

分析本题首先想到递归思想,从得分100逆推出初始分数为10的方案。
首先定义一个字符串

如果本题答对了,则总得分除2,字符串首位加1(因为是从第10题往前推,所以要逆序加)

如果本题答错了,则总得分加上本题的序号数,字符串首位加0

这里还有一个小细节,如果得分是奇数,则不能除2(只能答错)。

代码及详细注释:

def ScoreOut(qs, score, out):
    # 如果qs为0
    if qs == 0:
        # 如果score等于10
        if score == 10:
            print(out)  # 输出当前字符串out
            return out  # 返回当前字符串out
        else:
            return ""  # 返回空字符串

    else:
        # 如果score为偶数
        if score % 2 == 0:
            # 递归调用函数,分别将score除以2并在开头添加字符"1",以及将score加上qs并在开头添加字符"O"
            return ScoreOut(qs - 1, score // 2, "1" + out) + ScoreOut(qs - 1, score + qs, "O" + out)
        else:
            # 如果score为奇数,只递归调用将score加上qs并在开头添加字符"O"
            return ScoreOut(qs - 1, score + qs, "O" + out)

# 初始调用函数
ScoreOut(10, 100, "")

状态压缩dp:

什么是状态压缩

利用一串0/1数字(二进制数)来表示各个点的状态

这就要求使用状态压缩的对象的状态必须只有两种,0 或 1(如果有三种状态用三进制来表示也未尝不可)。

需要将状态数据实现为基本数据类型,比如 int,long 等,即状态压缩。比如说棋盘上的格子,放棋子或者不放;硬币的正面或反面;题目的对或错等。

状态压缩要求状态数据中的单元个数不能太大,比如用 int 来表示一个状态的时候,状态的单元个数不能超过 32(32位CPU),所以题目一般都是至少有一维的数据范围很小。

当然Python不需要考虑数的大小是否受限。

分析本题,上面用递归讲解过本题,有递归就用动态规划来解决,众所周知,递归很好理解和书写,但是递归的时间复杂度都不低,会有大量冗余计算。像本题中

如图

在这里插入图片描述
会出现大量相同得分情况,这就需要借助动态规划来处理重复计算了。

动态规划五部曲来分析

  1. 确定dp数组的定义

对于数组dp[i] :

下标表示10道题目的对错状态,dp[i]表示对应得分

下标 i 的二进制数表示对错状态,如 i = 2 时 i 的二进制数为 10 ,此时10(第1题答错,之后的提还没有做)(二进制数)的得分为多少,i = 7 时,i的二进制数为111,此时(第1题答对,第2题答对,之后的题还没有做)的得分为多少
特别注意!!! 这里首位的1(二进制后的数)没有实际含义,不表示题目对错,这个在下面会讲解

题目中有10道题,总共的有2 ** 10 - 1 种可能种情况(可能的情况用2进制计算就可以得出,如从000000000到111111111的可能数)

对于0000000000(这些都是10个数,不用挨个数了),二进制是这么表示,但10进制中,这个数就是0,

要想这个二进制数有意义,最前面就要加个1,否则无论有几个0,都表示0这个数,所以我们在定义dp数组长度的时候,不能为2 ** 10 而是为 2 ** 11(因为二进制1000000000(1后面10个0)的10进制数为 2 ** 11)

大家之前做题接触的二进制情况肯定不多,所以这里会稍微有点绕。这里主要理解前面自定义的1就好,跟补码的符号位有异曲同工之妙,只不过这里仅仅用来补位。

  1. dp数组的递推公式

状态压缩dp的递推公式还跟别的动规的递推公式不太一样,这里需要用到位运算符

这里简单讲解一下位运算符:

<< : 表示运算数的各二进制位全部左移若干位,低位补0
7 << 1 = 14, 因为7的二进制数为111,左移一位后为1110,1110的十进制数为14

右移跟左移同理。

还有python的二进制数可以用bin()函数来求,如bin(7) = 0b111,0b表示二进制,bin(7)的类型为字符串型。但是直接写0b111 就表示整型

因为第二道题的得分是由第一道题的得分算出来的,如

dp[0b1 << 1] = dp[0b1] - 1          #   dp[0b10]即dp[2]
dp[(0b1 << 1) +1] = dp[0b1] * 2      #  dp[0b11]即dp[3]

对于第cur道题,

如果答错dp[i << 1] = dp[i] - cur
如果答对dp[(i << 1) + 1] = dp[i] * 2

在推导递推公式的时候如果不太清楚,就看看dp数组的定义,切记第一个1无实义

  1. dp数组如何初始化

因为第一个1无实义,所以

dp[0b1] = 10  # dp[1]初始化得分为10
  1. dp数组遍历顺序

毫无疑问,题是从前往后答的,dp数组也是从左往右推

  1. 打印dp数组

画的有点丑,下标最好写成二进制形式。
在这里插入图片描述

求出dp数组后就把得分为100且10道题全都做了的结果输出就行。比较简单,有很多种方法,就不细说了。

代码及详细注释:

import math

# 初始化一个长度为2^11的数组,初始值为0
dp = [0] * 2 ** 11
dp[1] = 10  # dp[1]初始化得分为10
# dp[0b1 <<1] = dp[0b1] - 1             dp[0b10]即dp[2]
# dp[(0b1 <<1) +1] = dp[0b1] * 2        dp[0b11]即dp[3]

maxscore = 160  # 最大分数为160(因为160怎么减题目序号都到不了100,也可以设170等等)
for i in range(1, 2 ** 10):
    if dp[i] == -1:
        continue
    else:
        cur = int(math.log2(i << 1))  # 计算准备做的题目序号,因为第一个1无实际意义,1之后的数才表示题的对错
        if dp[i] - cur <= 0:
            dp[i << 1] = -1
        else:
            dp[i << 1] = dp[i] - cur
        if dp[i] * 2 >= maxscore:
            dp[(i << 1) + 1] = -1
        else:
            dp[(i << 1) + 1] = dp[i] * 2

result = []
while True:
    if 100 in dp:
        index = dp.index(100)  # 找到值为100的索引
        dp[index] = 0
        if len(bin(index)) == 13:
            result.append(bin(index))  # 将长度为13(10道题都做完了,头三位为0b1)的二进制数添加到结果列表中
    else:
        break

for res in result:
    print(res[3:])  # 输出结果列表中每个元素的第3位之后的内容

总结:

状态压缩dp还是比较难的,但把dp的定义吃透就比较好理解了。

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

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

相关文章

【ESP32接入国产大模型之MiniMax】

1. MiniMax 讲解视频&#xff1a; ESP32接入语言大模型之MiniMax MM智能助理是一款由MiniMax自研的&#xff0c;没有调用其他产品的接口的大型语言模型。MiniMax是一家中国科技公司&#xff0c;一直致力于进行大模型相关的研究。 随着人工智能技术的不断发展&#xff0c;自然语…

springboot绩效管理系统(源码私信呢)

链接如下: 20240316_173655_哔哩哔哩_bilibili 代码解析理解&#xff1a; 前置知识:三层架构: con...>ser接口>imp接口实现类>mapper写sql语句Controller 层控制层-->调用业务方法来控制业务逻辑 &#xff0c;功能的请求和响应控制&#xff0c;controller层负责前…

减肥实践和经验分享

在当下竞争激烈、物质丰富的现代社会&#xff0c;每个人都会同时面临两个不同指向的问题 和别人竞争&#xff0c;实现个人价值&#xff0c;创造个人财富&#xff0c;此为&#xff1a;显性指向&#xff08;explicit&#xff09;和自己比拼&#xff0c;实现个人内在提升&#xf…

数据库——书籍+内容0.1版本

背景&#xff1a;将一本书&#xff0c;存入我们的数据库中&#xff0c;并可以查出来 采用&#xff1a;第三范式&#xff08;3NF&#xff09;设计模式 设计数据库模板 第一范式&#xff08;1NF&#xff09;&#xff1a;确保表的每一列都是不可分割的原子数据项。 第二范式&…

什么时候去检测大数据信用风险比较合适?

什么时候去检测大数据信用风险比较合适?在当今这个数据驱动的时代&#xff0c;大数据信用风险检测已经成为个人的一项重要需求。本文将从贷前检测、信息泄露检测和定期检测三个方面&#xff0c;阐述何时进行大数据信用风险检测较为合适。 一、贷前检测 大数据信用风险检测在贷…

1.Spring入门

1.1 Spring简介 Spring是一个轻量级Java 企业级应用程序开发框架&#xff0c;目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架&#xff0c;为开发Java应用程序提供全面的基础架构支持。 Spring Fra…

力扣701. 二叉搜索树中的插入操作

思路&#xff1a;往二叉搜索树中插入一个值&#xff0c;树的结构有多种符合的情况&#xff0c;那我们可以选一种最容易的插入方式&#xff0c;反正只需要插入一个值而已&#xff0c;我们不难发现&#xff0c;不管插入什么值&#xff0c;都可以安排插入到叶子节点上。 再利用二叉…

Internet协议的安全性

Internet协议的安全性 文章目录 Internet协议的安全性1. 网络层1. IP*62. ARP*33. ICMP * 3 2. 传输层协议1. TCP1. * SYN-Flood攻击攻击检测* 防御 2. TCP序号攻击攻击 3. 拥塞机制攻击 2. UDP 3. 应用层协议1. DNS攻击*3防范*3: 2. FTP3. TELNET: 改用ssh4. 电子邮件1. 攻击2…

Microsoft Visio 编辑属性值

Microsoft Visio 编辑属性值 1. 编辑属性值References 1. 编辑属性值 单击长度或高度位置&#xff0c;弹出形状的各属性值&#xff0c;点击编辑对应的属性值。 ​ References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

解决:IDEA编译Java程序时报编译失败

1、问题展示&#xff1a; 2、解决方法&#xff1a;

自制一个操作系统 第一天

目录 环境准备引导程序 环境准备 自制操作系统的第一个困难是假设我们写好了操作系统&#xff0c;我们怎么模拟运行我们的操作系统&#xff1f;不用担心&#xff0c;已经有现成的模拟工具了&#xff0c;QEMU(Quick Emulator) 是一个广泛使用的开源计算机仿真器和虚拟机。使用它…

2023年中国抗DDoS市场规模现状及竞争格局,公有云抗DDoS是主要增长点

分布式拒绝服务&#xff08;DDoS&#xff09;是在DoS基础之上产生的一种新的攻击方式&#xff0c;具有多对一的攻击模式。它通过制造伪造的流量&#xff0c;使得被攻击的服务器、网络链路或是网络设备&#xff08;如防火墙、路由器等&#xff09;负载过高&#xff0c;无法处理正…

webpack5零基础入门-5使用webpack处理stylus文件

1.需要下载一个包 npm i stylus-loader 2.功能介绍 stylus-loader:负责将stylus文件编译成css文件 3.配置&#xff1a; const path require(path);//nodejs用来处理路径问题的模块module.exports {/**入口 */entry: ./src/main.js,/**输出 相对路径*/output: {/**文件输…

如何有效地组织和管理自己的代码?

如何有效地组织和管理自己的代码&#xff1f; &#x1f9e9; &#x1f6e0;️ 如何有效地组织和管理自己的代码&#xff1f; &#x1f9e9;摘要引言正文1. 使用合适的目录结构2. 模块化设计3. 命名规范4. 版本控制 总结参考资料 博主 默语带您 Go to New World. ✍ 个人主页——…

008:安装Docker

安装Docker 如果不太熟悉Linux命令&#xff0c;不想学习Linux命令&#xff0c;可以直接看文末NAS面板章节&#xff0c;通过面板&#xff0c;像使用Window一样操作NAS。 一、安装 Docker 1.安装 Docker wget -qO- https://get.docker.com/ | sh2.启动 Docker 服务 sudo sys…

复习知识点

1. Java常用API 1.1 String类 在java中&#xff0c;String类代表字符串&#xff0c;字符串是常量的&#xff0c;不能被改变。如果想改变字符串。可以用字符串的缓冲区&#xff0c;StringBuffer、StringBuilder 1.1.1 String类的创建方式 第一种&#xff08;常用&#xff09…

数学建模--MATLAB基本使用

1.线性方程组 这个是一个线性方程组&#xff08;属于线性代数的范畴&#xff09;&#xff0c;Axb类型的方程&#xff0c;如果使用MATLAB进行求解&#xff0c;就需要分别表示A矩阵&#xff08;线性方程组未知数前面的系数&#xff09;&#xff0c;b矩阵&#xff08;表示等式右边…

刷题日记——非素数个数(厦大机试)

题目 分析 使用欧拉筛法计算从1到b的素数个数&#xff0c;方法如下&#xff1a; 找到一个素数后&#xff0c;就将它的倍数标记为合数&#xff0c;也就是把它的倍数“筛掉”&#xff1b;如果一个数没有被比它小的素数“筛掉”&#xff0c;那它就是素数。计算出从1到b的…

Ubuntu Desktop - gnome-calculator (计算器)

Ubuntu Desktop - gnome-calculator [计算器] 1. Ubuntu Software -> gnome-calculator -> Install -> Continue2. Search your computer -> Calculator -> Lock to LauncherReferences 1. Ubuntu Software -> gnome-calculator -> Install -> Continu…

mysql颗粒归仓

B B树&#xff1a;节点排序 一个节点存多个元素 多个元素也排序了 叶子节点间有指针&#xff0c;非叶子节点上的元素在叶子节点冗余&#xff1a;叶子节点存储排好序的all元素 通过数据排序提高查询速度&#xff0c;节点存储多个元素 高度不会太高&#xff0c;一个innodb页B树…