【蓝桥杯】每日练习 Day19,20

news2025/4/5 12:49:47

目录

前言

蒙德里安的梦想

分析

最短Hamilton路径

分析

代码

乌龟棋

分析

代码

松散子序列

分析

代码

代码


前言

今天不讲数论(因为上课学数论真是太难了,只学了高斯消元)所以今天就不单独拿出来讲高斯消元了。今天讲一下昨天和今天做的四种题型(两个状压dp,两个线性dp),时间仓促,我们马上开始吧。


蒙德里安的梦想


分析

首先来分析一下暴力的时间复杂度,因为只有两种图形,所以我们只需要考虑一种图形的放法,随后在将另一种放进去即可,所以我们分析时间复杂度也只分析一种图形的放法,我们将两个相邻的方块看成一个整体,显然时间复杂度为:2^{N * M / 2}

考虑极限情况N = M = 11,结果是2^55左右,显然是超时的。所以暴力搜索不考虑。

我们注意到NM都很小(这种一般就可以考虑能否使用状态压缩dp了,经验之谈),所以我们考虑状态压缩dp,用二进制枚举所有的选法。

因为我也不太能想出来这道题的解题思路是怎么来的,所以我这边就直接用答案套题目了。

我们枚举每一列的所有选法,状态总数就是

1 << n

我们设

m = 1 << n

状态表示:dp[m][n] 枚举第n列使用m排列的所有选法。

状态转移:dp[m][n] += dp[s][n - 1],当然这两个状态必须是能够同时存在并且合法的。

现在我们来分析一下什么样的状态算是合法的,因为我们枚举的是横着摆放的所有方法,所以就必须满足放完之后能够使用竖着摆放的方块填满整个矩形,显然要想满足这个条件这一列连续不放置的方块数量就必须是偶数(我们是用每一列来划分的,所以默认前面的所有列都是已经放满的,无需考虑复杂情况。)

随后我们再来思考一下怎样算是这一层与上一层的状态是共存的,显然不能重复放置。所以我们的划分就已经完成了。

时间复杂度分析:O(n * 2 ^ n * 2 ^ n)大概是1亿左右,可以通过。

/*
    数位dp,因为只有两种方块并且必须填满才算合法情况,所以我们只枚举一种的放法
    随后判断这种方法是否合法。
    第一列所有状态都是合法的。
*/
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 15, M = 1 << N;
int n, m;
LL dp[M][N]; //第N列以M的摆放方式放置的所有方案
bool is_realy[M];
//LL l;
int main()
{
    while(scanf("%d%d", &n, &m) && n && m)
    {
        //memset(is_realy, false, sizeof is_realy);
        for(int i = 0; i < 1 << n; i++) //枚举所有状态
        {
            int cnt = 0; //记录非0长度
            is_realy[i] = true;
            for(int j = 0; j < n; j++) //查看每一位
            {
                if((i >> j) & 1) //是1
                {
                    if(cnt & 1) //奇数
                    {
                        is_realy[i] = false; //非法状态
                        break;
                    }
                    cnt = 0;
                }
                else
                    cnt++;
            }
            if(cnt & 1) is_realy[i] = false;
        }
        
        memset(dp, 0, sizeof dp); //初始化
        for(int i = 0; i < 1 << n; i++) 
            if(is_realy[i])
                dp[i][1] = 1;
        for(int i = 2; i <= m; i++) //枚举每一层 0, m - 1是合法层
        {
            for(int j = 0; j < 1 << n; j++) //枚举当前状态
                for(int k = 0; k < 1 << n; k++) //枚举上一层状态
                    if((j & k) == 0 && is_realy[j | k])
                        dp[j][i] += dp[k][i - 1];
        }
        printf("%lld\n", dp[0][m]);
    }
}

最短Hamilton路径


分析

哈密尔顿路问题,每个结点都要经过一次且仅一次

看到这个最终状态其实就能够感觉出来是动态规划了,话不多说,我们来考虑状态压缩dp

状态表示:dp[i][i]表示以i结尾的包含i的路径的所有选法

老规矩,分析上一个结点,所以我们枚举上一个结点。

状态转移:

dp[i][j] = min(dp[i][j], dp[i - 1 << j][k] + map[k][j])

时间复杂度分析:枚举路径数是2^n = 1e6,枚举每个结点和上一层结点是n * n,所以最终的时间复杂度就是O(2^n * n^2),大概是4亿左右,这道题时间限制是5s可以通过。


代码

// Hamilton路径——经过所有点一次且仅一次的路径
// dp[M][N] 从1出发,以n结尾的所有路径的最短值。
#include<iostream>
#include<cstring>
using namespace std;
const int N = 21, M = 1 << N;
int n;
int map[N][N];
int dp[M][N];
​
int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            scanf("%d", map[i] + j);
    // 枚举所有路径
    memset(dp, 0x3f, sizeof dp);
    dp[1][0] = 0;
    for(int i = 0; i < 1 << n; i++) //枚举所有路径
    {
        for(int j = 0; j < n; j++) //枚举以哪个点结尾
        {
            if(i >> j & 1) // 路径中包含i,枚举上一个点结尾
                for(int k = 0; k < n; k++)
                {
                    if(i >> k & 1) //也在路径中
                        dp[i][j] = min(dp[i][j], dp[i - (1 << j)][k] + map[k][j]);
                }
            
        }
    }
    printf("%d", dp[(1 << n) - 1][n - 1]);
}

乌龟棋


分析

本来以为是背包但是发现结果与选的个数无关只与顺序有关并且是一定可以选完的,所以排除掉背包。

发现题目中强调了只有四种跳跃幅度,所以从这里入手,如何来表示状态呢?

欸?有一种“非常暴力”的表示方法——dp[N][M][M][M][M],显然这个状态数量有点多了,大概是350 * 41 * 41 * 41 * 41 = 8.9亿,严重超时,超空间。

随后我们来分析一下能不能优化呢?可以发现N和每一个M彼此之间都是有关联的,只需要知道其余4个就可以求出第5个,所以我们可以将状态优化到4,因为N最大,所以我们优先考虑优化掉N,状态就变为了:dp[M][M][M][M],大概数41 * 41 * 41 * 41 = 2560000空间上大概是9M,可以通过。

状态表示有了接下来我们就来思考一下状态计算。

显然dp[a][b][c][d] = max(dp[a - 1][b][c][d], dp[a][b - 1][c][d], dp[a][b][c - 1][d], dp[a][b][c][d - 1]) + s


代码

/*
    01背包? 
    对于每个位置,枚举使用哪张卡片到达? 会出现重复吧。。。
    学到了新的思路,每个卡片都存储一下选择了多少张,这样就不会出现重复的了还能保证没有先后顺序的
    随机查找
*/
#include<iostream>
using namespace std;
const int N = 360, M = 42;
int n, m;
int a[N], s[5];
int dp[M][M][M][M]; //四维状态存储每种卡片的使用情况
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for(int i = 1; i <= m; i++)
    {
        int x; scanf("%d", &x);
        s[x]++; 
    }
    //dp[0][0][0][0] = a[1];
    for(int A = 0; A <= s[1]; A++)
        for(int B = 0; B <= s[2]; B++)
            for(int C = 0; C <= s[3]; C++)
                for(int D = 0; D <= s[4]; D++)
                {
                    if(A) dp[A][B][C][D] = max(dp[A][B][C][D], dp[A - 1][B][C][D]); 
                    if(B) dp[A][B][C][D] = max(dp[A][B][C][D], dp[A][B - 1][C][D]); 
                    if(C) dp[A][B][C][D] = max(dp[A][B][C][D], dp[A][B][C - 1][D]); 
                    if(D) dp[A][B][C][D] = max(dp[A][B][C][D], dp[A][B][C][D - 1]); 
                    dp[A][B][C][D] += a[1 + A + 2 * B + 3 * C + 4 * D];
                }
    printf("%d", dp[s[1]][s[2]][s[3]][s[4]]);
    return 0;
}

松散子序列


分析

题目要求就是找出acsii相加并且每个字符在原串中的距离要大于1的所有子序列的最大值(语文题)。

这道题很简单,主播给大家提供两种思路。

第一种,以子序列的最后一个结点划分,运用最长上升子序列的思想,然每次更新最大值即可。(这也是我认为最简单的思路)。

状态表示:dp[i]表示以第i个字符结尾的子序列的最大值

状态转移:dp[i] = max(dp[i - 2], dp[i - 3], ...) + str[i],因为我们求的是线性值,所以可以使用贪心优化,每次只拿最大值判断即可。


代码

#include<iostream>
using namespace std;
const int N = 1000010;
int mxx;
int dp[N];
int n;
char str[N];
​
int main()
{
    scanf("%s", str + 1);
    for(int i = 1; str[i]; n = i++)
    {
        dp[i] = mxx + str[i] - 'a' + 1; //记录最大值
        mxx = max(mxx, dp[i - 1]);
    }
    printf("%d", max(dp[n], dp[n - 1]));
    return 0;
}

第二种呢,是使用状态机dp,显然每个字符只有两种状态——选或不选,随后运用背包的思想表示出状态即可。

状态表示:dp[i][2]表示第i个字符选或不选。

状态转移:dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]),dp[i][1] = dp[i - 1][0] + s[i]


代码

#include<iostream>
using namespace std;
const int N = 1000010;
int dp[N][2];
int n;
char str[N];
​
int main()
{
    scanf("%s", str + 1);
    for(int i = 1; str[i]; n = i++)
    {
        dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]);
        dp[i][1] = dp[i - 1][0] + str[i] - 'a' + 1;
    }
    printf("%d", max(dp[n][0], dp[n][1]));
    return 0;
}

对于这个状态呢,我们可以将其优化到一维——dp[i]存储选或不选的最大值(类似背包)。

状态表示:dp[i] = max(dp[i - 1], dp[i - 2]) + str[i],前面是不选的最值,后面是选的最值。

#include<iostream>
using namespace std;
const int N = 1000010;
int dp[N];
int n;
char str[N];
​
int main()
{
    scanf("%s", str + 1);
    for(int i = 1; str[i]; n = i++)
        dp[i] = max(dp[i - 1], dp[i - 2] + str[i] - 'a' + 1);
    printf("%d", dp[n]);
    return 0;
}

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

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

相关文章

《AI大模型应知应会100篇》第7篇:Prompt Engineering基础:如何与大模型有效沟通

第7篇&#xff1a;Prompt Engineering基础&#xff1a;如何与大模型有效沟通 摘要 Prompt Engineering&#xff08;提示工程&#xff09;是与大模型高效沟通的关键技能。通过精心设计的Prompt&#xff0c;可以让模型生成更准确、更有用的结果。本文将从基础知识到高级策略&…

Spring实现WebScoket

SpringWeb编程方式分为Servlet模式和响应式。Servlet模式参考官方文档&#xff1a;Web on Servlet Stack :: Spring Framework&#xff0c;响应式&#xff08;Reacive&#xff09;参考官方文档&#xff1a;Web on Reactive Stack :: Spring Framework。 WebSocket也有两种编程方…

odoo-045 ModuleNotFoundError: No module named ‘_sqlite3‘

文章目录 一、问题二、解决思路 一、问题 就是项目启动&#xff0c;本来好好地&#xff0c;忽然有一天报错&#xff0c;不知道什么原因。 背景&#xff1a; 我是在虚拟环境中使用的python3.7。 二、解决思路 虚拟环境和公共环境直接安装 sqlite3 都会报找不到这个库的问题…

前端JS高阶技法:序列化、反序列化与多态融合实战

✨ 摘要 序列化与反序列化作为数据转换的核心能力&#xff0c;与多态这一灵活代码设计的核心理念&#xff0c;在现代前端开发中协同运作&#xff0c;提供了高效的数据通信与扩展性支持。 本文从理论到实践&#xff0c;系统解析&#xff1a; 序列化与反序列化的实现方式、使用…

RustDesk 开源远程桌面软件 (支持多端) + 中继服务器伺服器搭建 ( docker版本 ) 安装教程

在需要控制和被控制的电脑上安装软件 github开源仓库地址 https://github.com/rustdesk/rustdesk/releases 蓝奏云盘备份 ( exe ) https://geek7.lanzouw.com/iPf592sadqrc 密码:4esi 中继服务器设置 使用docker安装 sudo docker image pull rustdesk/rustdesk-server sudo…

STM32单片机入门学习——第3-4节: [2-1、2]软件安装和新建工程

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难&#xff0c;但我还是想去做&#xff01; 本文写于&#xff1a;2025.04.01 STM32开发板学习——第一节&#xff1a; [1-1]课程简介 前言开发板说明引用解答和…

intellij Idea 和 dataGrip下载和安装教程

亲测有效 第一步&#xff1a;卸载老版本idea/Datagrip &#xff08;没有安装过的可跳过此步骤&#xff09; 第二步&#xff1a;下载idea/dataGrip安装包 建议选择2022以后的版本 官网&#xff1a; https://www.jetbrains.com/datagrip/download/other.html 选择dataGrip 的…

轻量级搜索接口技术解析:快速实现关键词检索的Java/Python实践

Hi&#xff0c;你好&#xff01; 轻量级搜索接口技术解析&#xff1a;快速实现关键词检索的Java/Python实践 接口特性与适用场景 本接口适用于需要快速集成搜索能力的开发场景&#xff0c;支持通过关键词获取结构化搜索结果。典型应用场景包括&#xff1a; 垂直领域信息检索…

架构设计基础系列:事件溯源模式浅析

图片来源网络&#xff0c;侵权删 ‌1. 引言‌ ‌1.1 研究背景‌ 传统CRUD模型的局限性&#xff1a;状态覆盖导致审计困难、无法追溯历史。分布式系统复杂性的提升&#xff1a;微服务架构下数据一致性、回滚与调试的需求激增。监管合规性要求&#xff1a;金融、医疗等领域对数…

【力扣hot100题】(035)二叉树的中序遍历

正常方法递归很简单&#xff0c;于是又学了一种栈的方法。 原理如下&#xff1a;每次循环先尽量将目前节点入栈并左移&#xff0c;没有左节点时回到栈首节点将目前节点放入结果容器中并移出栈外&#xff0c;目前节点变为该节点的右节点&#xff0c;循环结束条件是目前节点为nu…

《数字图像处理》教材寻找合作者

Rafael Gonzalez和Richard Woods所著的《数字图像处理》关于滤波器的部分几乎全错&#xff0c;完全从零开始写&#xff0c;困难重重。关于他的问题已经描述在《数字图像处理&#xff08;面向新工科的电工电子信息基础课程系列教材&#xff09;》。 现寻找能够共同讨论、切磋、…

批量删除 txt/html/json/xml/csv 等文本文件中的重复行

在文本文件中&#xff0c;可能会存在一些重复的数据行&#xff0c;这可能不是我们期望的&#xff0c;因此我们会碰到需要删除文本文件中重复行的情况。如果是人工删除&#xff0c;当文件较大或者数量较多的时候&#xff0c;处理的难度会较大。今天就给大家介绍一下批量删除文本…

基于微信小程序的智慧乡村旅游服务平台【附源码】

基于微信小程序的智慧乡村旅游服务平台&#xff08;源码L文说明文档&#xff09; 目录 4系统设计 4.1系统功能设计 4.2系统结构 4.3.数据库设计 4.3.1数据库实体 4.3.2数据库设计表 5系统详细实现 5.1 管理员模块的实现 5.1.1旅游景点管理…

80. Linux内核定时器实验

一、Linux内核定时器原理 1.1、内核时间管理 1、Cortex-M内核使用systick作为系统定时器。 2、硬件定时器、软件定时器&#xff0c;原理是依靠系统定时器来驱动。 3、linux内核频率可以配置&#xff0c;图形化界面配置。 4、重点&#xff0c;HZ表示系统节拍率&#xff0c; 1.…

C++类与对象(上):从入门到实践

目录 一、引言 二、面向过程和面向对象初步认识 2.1 面向过程编程 2.2 面向对象编程 三、类的引入 四、类的定义 4.1 定义格式 4.2 定义方式 4.3 成员变量命名规则建议 五、类的访问限定符及封装 5.1 访问限定符 5.2 封装 六、类的作用域 七、类的实例化 7.1 概念…

Lumerical ------ Edge coupler design

Lumerical ------ Edge coupler design 引言正文无 Si Substrate 的仿真步骤有 Si Substrate 的仿真步骤引言 本文,我们将使用官方提供的 Edge coupler 设计教程,但是中间会带有作者本人的设计的感悟。 正文 无 Si Substrate 的仿真步骤 打开 Edge_Coupler_No_Substrate.l…

大语言模型本质上还是自动化,而不是智能化

大语言模型本质上仍然是自动化或高级自动化&#xff0c;而非真正的智能化&#xff0c;原因可以从以下几个方面进行分析&#xff1a;1、自动化与智能化的本质区别自动化&#xff1a;大语言模型通过预训练和微调&#xff0c;基于大量数据和规则生成输出。它的行为是基于输入数据的…

python数据结构——链表、栈、队列

一、思维梳理&#xff1a; 二、双向循环链表&#xff1a; class Node:def __init__(self,data):self.data dataself.next Noneself.prev Noneclass DoubleLink:def __init__(self):self.size 0self.head Nonedef is_empty(self):return self.size 0def add_end(self,dat…

centos操作系统如何更换yum镜像源

CentOS Linux 是一个免费提供的、社区支持的Linux发行版,由CentOS项目社区贡献者开发、分发和维护。2020年CentOS项目宣布将把全部投资转移到CentOS Stream,作为即将发布的 Red Hat Enterprise Linux版本的上游开发平台。因此,CentOS Linux更新和发布将在2021年至2024年期间…

【Linux篇】自主Shell命令行解释器

&#x1f4cc; 个人主页&#xff1a; 孙同学_ &#x1f527; 文章专栏&#xff1a;Liunx &#x1f4a1; 关注我&#xff0c;分享经验&#xff0c;助你少走弯路&#xff01; 文章目录 1. 获取用户名的接口2. 等待用户输入接口3. 将上述代码进行面向对象式的封装4. 命令行解析5.…