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

目录

题目传送门

算法解析

最终代码

提交结果

尾声


题目传送门

[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~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

笔记79:ROS入门之前的准备

一、ROS是什么 ROS其实是一个伪操作系统&#xff0c;是基于Liunx操作系统的一个用于机器人各个节点之间通信的系统&#xff1b;ROS制定了一系列规则使得每个节点之间遵循相同的通信规则&#xff0c;使得每个人都可以有一个守则区遵守开发自己的节点&#xff0c;也能和别人开发…

Exception异常处理

1. 两种异常处理机制 1.1 使用 throw 关键字手动抛出异常 使用throw关键字抛出异常&#xff0c;代码会的显得简单明了 如下图所示 1.2 使用 try-catch 捕获异常 使用try-catch进行捕获异常&#xff0c;往往会使代码变得更加笼统&#xff0c;层层包裹 如下图所示 2. 自定义…

从零学算法287

287.给定一个包含 n 1 个整数的数组 nums &#xff0c;其数字都在 [1, n] 范围内&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一个重复的整数。 假设 nums 只有 一个重复的整数 &#xff0c;返回 这个重复的数 。 你设计的解决方案必须 不修改 数组 nums 且只…

聊聊python中面向对象编程思想

面向对象编程思想 1、什么是面向过程 传统的面向过程的编程思想总结起来就八个字——自顶向下&#xff0c;逐步细化&#xff01; → 将要实现的功能描述为一个从开始到结束按部就班的连续的“步骤” → 依次逐步完成这些步骤&#xff0c;如果某一个步骤的难度较大&#xff…

2024年品牌推广:构建品牌生态圈与注重品牌故事和文化传播

在全球经济深度融合、数字化浪潮汹涌澎湃的2024年&#xff0c;品牌推广的策略与模式正经历着前所未有的变革。在这一背景下&#xff0c;构建品牌生态圈和注重品牌故事与文化传播&#xff0c;成为了企业提升品牌竞争力和市场占有率的重要手段。 一、2024年市场经济分析与现状 …

上门预约按摩系统相比较传统按摩店有哪些优点和特色;

上门按摩系统与传统按摩店的运营对比&#xff1a; 1. 技师自由选择&#xff1a;在上门按摩系统中&#xff0c;技师可以兼职加入&#xff0c;无需固定门店。平台为技师提供订单&#xff0c;技师则携带基础服务用具上门服务。同时&#xff0c;兼职技师也需提交详尽资料和资质证明…

伟骅英才|二月二:龙年龙抬头

二月二龙抬头&#xff0c;是中国民间传统节日&#xff0c;人们期盼通过对龙的祈求来实现降雨的目的&#xff0c;寄托了老百姓对美好生活的向往。这一天&#xff0c;人们通常会去理个发&#xff0c;寓意着“二月二剃龙头&#xff0c;一年都有精神头”。现如今的二月二&#xff0…

软件测试需要学什么?学多久?软件测试技术进阶路线图

很多新手&#xff0c;不知道软件测试学习该如何开始&#xff0c;软件测试需要掌握哪些知识。下面是根据本人的理解&#xff0c;粗略整理的一个学习大纲&#xff0c;基本上涵盖了软件测试工程师需要掌握的全部技能&#xff0c;希望对刚入行或者准备学习测试的朋友提供一点指引。…

Java17 --- SpringCloud初始项目创建

目录 一、cloud项目创建 1.1、项目编码规范 1.2、注解生效激活 1.3、导入父工程maven的pom依赖 二、创建子工程并导入相关pom依赖 2.1、相关配置文件 2.1.1、数据库配置文件内容 2.1.2、自动生成文件配置内容 三、创建微服务8001子工程 3.1、导入相关pom依赖 3.…

基于51单片机超声波测距

目录 摘 要 2 ABSTRACT 3 目 录 4 1 绪论 1 1 概述 12 国内外发展现状 1 2 系统总体方案设计 21 设计要求 2 1&#xff09;可进行距离测量。 2 2&#xff09; 采用数码管显示距离数据。 2 3&#xff09; 可按键设置距离门限值 2 4&#xff09; 具有报警功能 22 方案选择 2 1 …

Vue点击切换组件颜色

例如我有一个这样的组件&#xff0c;我希望在点击组件之后由蓝色变成橙色 先把原来的代码附上(简化掉了叉号&#xff09;&#xff1a; <div v-for"(item, index) in words" :key"index" class"scrollbar-demo-item"><span>{{ item …

PL/SQL学习笔记

介绍 PL/SQL 编程语言是SQL 和 Oracle 关系数据库的过程扩展语言 是Oracle 编程环境中的一个工具 基本语法 S.No Sections & Description 1 Declarations 此部分以关键字 DECLARE 开头。 它是一个可选部分&#xff0c;定义了程序中要使用的所有变量、游标、子程序和其他…

C++虚继承的一些细节

C虚继承的一些细节 何时使用虚继承普通继承的类对象布局虚继承类对象布局虚函数表指针虚函数表内容 何时使用虚继承 看代码&#xff0c;代码主要是菱形继承&#xff0c;base里面的成员变量会存在二义性 #include<iostream> using namespace std;class base { public:in…

客户案例|100M 768 维向量数据,Zilliz Cloud 稳定支持 Shulex VOC 业

日前&#xff0c;国际化 VOC SaaS 公司数里行间&#xff08;Shulex&#xff09;将上亿数据量的核心业务从开源向量数据库 Milvus 迁移至全托管的向量数据库云服务 Zilliz Cloud。 相比于 Milvus&#xff0c;Zilliz Cloud 实现了 Shulex VOC 评论分析洞察报告生成速度 30% 的提升…

第二十天-数据分析

1.介绍 1.什么是数据分析 1.以下4个纬度结合起来的数据科学 2.数据分析的特殊性

物体检测-系列教程23:YOLOV5 源码解析13 (SPP层、Flatten模块、Concat模块、Classify模块)

&#x1f60e;&#x1f60e;&#x1f60e;物体检测-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 点我下载源码 17、SPP模块 17.1 SPP类 SPP是一种特殊的池化策略&#xff0c;最初在YOLOv3-SPP中被使用…

Axuer的中继器~增删改查

目录 1. 中继器 2. 增加 3. 全选 4. 删除 5. 修改 6. 排序 7.分页 正文&#xff1a; 1.中继器&#xff1a;拉一个中继器给中继器名称写一个数据 在右边点击样式 写入数据 1.2 双击中继器进入动态页面 在里面放3个框 1.3 退出来 给中继器添加数据 2. 增加&#xff1…

研发效能DevOps: OpenEuler 部署 drone 持续集成平台

目录 一、实验 1.环境 2.OpenEuler 部署 drone 持续集成平台 二、问题 1.drone登录失败 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统架构版本IP备注LinuxopenEuler22.03 LTS SP2 192.168.204.145&#xff08;动态&#xff09; 192.168.204.141&…

使用maven打包执行install命令时候一直卡在No proxies configured不动,如何解决?

问题描述&#xff1a; [INFO] [INFO] — frontend-maven-plugin:1.9.1:install-node-and-npm (install node and npm) frontend — [INFO] Installing node version v16.0.0 [INFO] Copying node binary from D:\Maven\maven-repository\com\github\eirslett\node\16.0.0\node…

3.11_C++_day1_作业

作业要求&#xff1a; 程序代码&#xff1a; #include <iostream> #include <string.h>using namespace std;int main() {int a0,b0,c0,d0,e0;//分别记录字符串中的大写&#xff0c;小写&#xff0c;数字&#xff0c;空格&#xff0c;其他字符个数string str;cha…