洛谷 P8816 [CSP-J 2022] 上升点列(T4)

news2024/11/20 13:31:20

目录

题目传送门

算法解析

最终代码

提交结果

尾声


题目传送门

[CSP-J 2022] 上升点列 - 洛谷icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P8816

算法解析

k = 0 且 xi, yi 值域不大时,这题是非常简单的 DP,类似「数字三角形」。

记 dp(x,y) 为「以 (x,y) 为终点,最长合法序列的长度」。

则对于所有(已经存在的)整点,有:

dp(x,y) = max {dp(x − 1, y), dp(x, y − 1)} + 1

xi, yi 值域比较大时:

可以考虑记 dp(n) 表示「以 n 号点结尾的合法序列,最长能有多长」。

dp(n) = max {dp(i) + 1}

i → n ✓

不会存在环状结构——因为合法序列必须向右、上方发展。

把刚刚的DP改造一下,就是本题正解:

记 dp(n, k) 表示「以 n 号点结尾,已经使用掉了 k 个自由点,获得的收益」。

dp(n,k) = max {dp(i, k − cost) + cost + 1}

i → n ✓

实现细节:本题的求值顺序值得注意,合法路径可能形如 P1 → P3 → P2。

有两种解决方法:

  • 记忆化搜索(记忆化搜索最擅长解决求值顺序混乱的 DP)
  • 预先按 x, y 排序,使得编号大的点一定是从编号小的点转移过来

这里记忆化搜索比较好写一些,我这里就只讲记忆化搜索了

先写一下求 a 到 b 需要补多少个点的函数,即两点曼哈顿距离再减一(a 在左下,b 在右上,否则返回无穷)

代码中 x[u] 表示 u 点的横坐标,y[u] 表示 u 点的纵坐标

int dis(int a, int b) {
	if(x[a] > x[b])
		return inf;
	
	if(y[a] > y[b])
		return inf;
	
	return x[b] - x[a]  + y[b] - y[a] - 1;
}

然后是 dp 函数,定义上面已经说过了

int dp(int now, int k)

首先判断如果自由点已经用完了,即 k < 0,那么返回负无穷(因为最后是取最大值)

int dp(int now, int k) {
	if(k < 0)
		return -inf;
}

既然是记忆化,那么就需要记忆

用 vis[n][k] 数组记录 dp(n, k) 是否访问过,val[n][k] 数组记录如果访问过的 dp(n, k) 的值

这样如果 vis[now][k] == true(访问过),则返回 val[now][k]

int dp(int now, int k) {
	if(k < 0)
		return -inf;
	
	if(vis[now][k])
		return val[now][k];
}

然后就该枚举它的前驱(代码中的 to),然后取里面最大的收益

这个记录最大收益的变量(代码中的 res)的初值一定要是 1,因为如果哪也去不了,那么就只能走到现在这一个点,也就是 now

int dp(int now, int k) {
	if(k < 0)
		return -inf;
	
	if(vis[now][k])
		return val[now][k];
	
	int res = 1;
	
	for(int to = 1; to <= n; ++to)
	
	return res;
}

接下来需要判断 to != now,然后计算出 to 到 now 需要补多少个点(代码中的 cost)

int dp(int now, int k) {
	if(k < 0)
		return -inf;
	
	if(vis[now][k])
		return val[now][k];
	
	int res = 1;
	
	for(int to = 1; to <= n; ++to)
		if(to != now) {
			int cost = dis(to, now);
		}
	
	return res;
}

再判断费用超出运算,就 contunue(如果走不到,dis 就会返回无穷,一定大于 k,所以不用特判走不到)

int dp(int now, int k) {
	if(k < 0)
		return -inf;
	
	if(vis[now][k])
		return val[now][k];
	
	int res = 1;
	
	for(int to = 1; to <= n; ++to)
		if(to != now) {
			int cost = dis(to, now);
			
			if(cost > k)
				continue;
		}
	
	return res;
}

接着就是往下递归了,now 变成了 to,预算费用还剩 k - cost,所以传进去是

dp(to, k - cost)

然后长度还需要加上 to 到 now 的距离,即 cost + 1,然后更新最大值(代码里的 res)

代码中的 bemax 函数是把第一个参数赋成两个参数的最大值用的,具体实现方法就是用一个三目运算符

void bemax(int &a, int b) {
	a = a > b ? a : b;
}
int dp(int now, int k) {
	if(k < 0)
		return -inf;
	
	if(vis[now][k])
		return val[now][k];
	
	int res = 1;
	
	for(int to = 1; to <= n; ++to)
		if(to != now) {
			int cost = dis(to, now);
			
			if(cost > k)
				continue;
			
			bemax(res, dp(to, k - cost) + cost + 1);
		}
	
	return res;
}

最后再将 vis[now][k] 设成 true,val[now][k] 设成 res

最后 return res 就行了

int dp(int now, int k) {
	if(k < 0)
		return -inf;
	
	if(vis[now][k])
		return val[now][k];
	
	int res = 1;
	
	for(int to = 1; to <= n; ++to)
		if(to != now) {
			int cost = dis(to, now);
			
			if(cost > k)
				continue;
			
			bemax(res, dp(to, k - cost) + cost + 1);
		}
	
	vis[now][k] = true;
	val[now][k] = res;
	
	return res;
}

主函数里需要枚举 i = 1 ~ n,j = 0 ~ k,然后传进去(n 为点的个数,k 为自由点的个数)

注意长度还需要加上没用的 k - j 个点,然后更新答案(代码中的 ans)

for(int i = 1; i <= n; ++i)
	for(int j = 0; j <= k; ++j)
		bemax(ans, dp(i, j) + k - j);

最后输出 ans 即可 

最终代码

#include <cstdio>
#define N 1005
using namespace std;

const int inf = 0x7fffffff;

int n, k;
int x[N], y[N];
bool vis[N][N];
int val[N][N];
int ans;

void bemax(int &a, int b) {
	a = a > b ? a : b;
}

int dis(int a, int b) {
	if(x[a] > x[b])
		return inf;
	
	if(y[a] > y[b])
		return inf;
	
	return x[b] - x[a]  + y[b] - y[a] - 1;
}

int dp(int now, int k) {
	if(k < 0)
		return -inf;
	
	if(vis[now][k])
		return val[now][k];
	
	int res = 1;
	
	for(int to = 1; to <= n; ++to)
		if(to != now) {
			int cost = dis(to, now);
			
			if(cost > k)
				continue;
			
			bemax(res, dp(to, k - cost) + cost + 1);
		}
	
	vis[now][k] = true;
	val[now][k] = res;
	
	return res;
}

int main() {
	scanf("%d%d", &n, &k);
	
	for(int i = 1; i <= n; ++i)
		scanf("%d%d", &x[i], &y[i]);
	
	for(int i = 1; i <= n; ++i)
		for(int j = 0; j <= k; ++j)
			bemax(ans, dp(i, j) + k - j);
	
	printf("%d\n", ans);
	
	return 0;
}

提交结果

提交一下哈

㇏(〃'▽'〃)㇀ AC ! ! !

尾声

如果这篇博客对您(您的团队)有帮助的话,就帮忙点个赞,加个关注!

最后,祝您(您的团队)在 OI 的路上一路顺风!!!

┬┴┬┴┤・ω・)ノ Bye~Bye~

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

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

相关文章

GaussDB(DWS)运维利刃:TopSQL工具解析

在生产环境中&#xff0c;难免会面临查询语句出现异常中断、阻塞时间长等突发问题&#xff0c;如果没能及时记录信息&#xff0c;事后就需要投入更多的人力及时间成本进行问题的定位和解决&#xff0c;有时还无法定位到错误出现的地方。在本期《GaussDB(DWS)运维利刃&#xff1…

Dubbo基础入门二

8、Dubbo协议 服务调用 8.1 服务端 启动过程深入分析 我们查看一下服务启动的过程 ProtocolFilterWrapper.export 好我们进入DubboProtocol.export 创建服务 分析我们的Handler 我们接着返回刚才位置 下面的super方法里面会创建服务&#xff0c;ChannelHandlers.wrap会对hand…

套接字编程 --- 一

目录 1. 预备知识 1.1. 端口号 1.2. 认识TCP协议 1.3. 认识UDP协议 1.4. 网络字节序 2. socket 2.1. socket 常见系统调用 2.1.1. socket 系统调用 2.1.2. bind 系统调用 2.1.3. recvfrom 系统调用 2.1.4. sendto系统调用 2.3. 其他相关接口 2.3.1. bzero 2.3.2…

代码随想录算法训练营第day10|232.用栈实现队列、 225. 用队列实现栈

目录 a.232.用栈实现队列 b. 225. 用队列实现栈 a.232.用栈实现队列 题目链接 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素…

专题一 - 双指针 - leetcode 202. 快乐数 | 简单难度

leetcode 202. 快乐数 leetcode 202. 快乐数 | 简单难度1. 题目详情1. 原题链接2. 基础框架 2. 解题思路1. 题目分析2. 算法原理3. 时间复杂度 3. 代码实现4. 知识与收获 leetcode 202. 快乐数 | 简单难度 1. 题目详情 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」…

掼蛋的牌型与规律(上篇)

掼蛋是一项配合类的棋牌竞技游戏&#xff0c;掼蛋的最大魅力以及最集中的特点在于变化&#xff0c;在于组牌的变数。有的掼蛋新手往往先把牌配死&#xff0c;并且直接决定好出牌计划&#xff0c;然后守株待兔。掼蛋的取胜之道在于静态组合加上动态变化。本文主要介绍一下掼蛋的…

【广度优先搜索】【图论】【并集查找】2493. 将节点分成尽可能多的组

作者推荐 视频算法专题 本文涉及知识点 广度优先搜索 图论 并集查找 LeetCod2493. 将节点分成尽可能多的组 给你一个正整数 n &#xff0c;表示一个 无向 图中的节点数目&#xff0c;节点编号从 1 到 n 。 同时给你一个二维整数数组 edges &#xff0c;其中 edges[i] [ai…

神州大地人类来源猜想

在公元前2000年以前&#xff0c;伟大的中华民族还是石器时代&#xff0c;我们有很多美好的神话和传说&#xff0c;三皇五帝就是这个时代伟大部落或者部落首领的故事。 关于人类的历史&#xff0c;从基因学上最新的研究成果大概是这样的&#xff0c;虽然从300万年前就诞生了人类…

3D资产管理

3D 资产管理是指组织、跟踪、优化和分发 3D 模型和资产以用于游戏、电影、AR/VR 体验等各种应用的过程。 3D资产管理也称为3D内容管理。 随着游戏、电影、建筑、工程等行业中 3D 内容的增长&#xff0c;实施有效的资产管理工作流程对于提高生产力、减少错误、简化工作流程以及使…

Xinstall微信调起APP,提升用户体验与转化率

在移动互联网时代&#xff0c;APP已经成为人们日常生活中不可或缺的一部分。然而&#xff0c;随着市场竞争的加剧&#xff0c;如何让用户更便捷地使用APP&#xff0c;提高分享营销的下载转化率&#xff0c;成为了开发者们亟待解决的问题。今天&#xff0c;我们将向大家介绍一款…

C/C++的内存管理与初阶模板

引言 我们在学习C的时候&#xff0c;会经常在堆上申请空间&#xff0c;所以这个时候就体现了内存管理遍历。 图下是我们常见的计算机的内存划分&#xff1a; 我也在图下对部分变量存在的位置&#xff0c;及时标注。(如果有任何问题可以联系博主修改&#xff0c;感谢大家。) 那…

如何在Windows上使用Docker,搭建一款实用的个人IT工具箱It- Tools

文章目录 1. 使用Docker本地部署it-tools2. 本地访问it-tools3. 安装cpolar内网穿透4. 固定it-tools公网地址 本篇文章将介绍如何在Windows上使用Docker本地部署IT- Tools&#xff0c;并且同样可以结合cpolar实现公网访问。 在前一篇文章中我们讲解了如何在Linux中使用Docker搭…

Docker安装主从数据库

首先开启docker后直接执行命令 docker run -d \ -p 3307:3306 \ -v /xk857/mysql/master/conf:/etc/mysql/conf.d \ -v /xk857/mysql/master/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD123456 \ --name mysql-master \ mysql:8.0.29 默认情况下MySQL的binlog日志是自动开…

深入浅出Redis(八):Redis的集群模式

引言 Redis是一款优秀的键值对、内存非关系型数据库&#xff0c;单机节点下的Redis存在无法保证高可用、容量不足等问题 上篇文章介绍的哨兵主要能够保证主从架构下Redis的可用性&#xff0c;但是仍然存在容量不足、推举新的主节点时不能访问Redis的问题&#xff0c;集群可水…

#QT(串口助手-界面)

1.IDE&#xff1a;QTCreator 2.实验&#xff1a;编写串口助手 3.记录 接收框:Plain Text Edit 属性选择&#xff1a;Combo Box 发送框:Line Edit 广告&#xff1a;Group Box &#xff08;1&#xff09;仿照现有串口助手设计UI界面 &#xff08;2&#xff09;此时串口助手大…

ai脚本创作的软件有哪些?分享3款好用的工具!

随着人工智能技术的飞速发展&#xff0c;AI脚本创作软件已经成为内容创作者们的新宠。这些软件不仅能够帮助我们更高效地生成文章、视频脚本等&#xff0c;还能为我们提供独特的创意视角和无限的灵感。本文将带您深入了解几款备受瞩目的AI脚本创作软件&#xff0c;看看它们如何…

带胶囊按钮的标题是如何实现的

使用uni-app开发小程序经常会遇到胶囊按钮和标题之间融合的问题&#xff0c;因为这样可以大大提高页面的美观和整体性&#xff0c;那么接下来简单拆分步骤看下是如何实现的吧 &#x1f601; 可以看到我们设置的标题是在默认标题栏之下的&#xff08;这不是我们想要的效果 &…

许多人可能还不了解这个信息差:美赛的第一批 EI 已经录用,不用再犹豫啦

格局打开&#xff0c;美赛论文转学术论文发表 &#x1f680;&#x1f680; 各位同学&#xff0c;美赛已经结束了一段时间&#xff0c;你们是否还在焦急地等待最终成绩的公布&#xff1f;一些有远见的同学已经提前收到了一份喜讯&#xff1a;他们的美赛论文已被转化为学术论文并…

VMware虚拟机

1、虚拟机介绍 虚拟机&#xff08;Virtual Machine&#xff09;是一种软件&#xff0c;可以用来模拟具有完整硬件功能的完整的计算机系统的软件&#xff0c;并且可以和主机环境隔离开&#xff0c;互不影响。也就是&#xff0c;在实体计算机中能够完成的工作都可以通过虚拟机实…

【SQL】1068. 产品销售分析 I

题目描述 leetcode题目&#xff1a;1068. 产品销售分析 I 写法 select Product.product_name, Sales.year, Sales.price from Sales left join Product on Sales.product_id Product.product_id记录细节&#xff1a;加上表名检索效率更高。 -- ERROR: 时间超出限制 > 加…