[CF762D] Maximum path 题解

news/发布时间2024/5/19 10:51:25

[CF762D] Maximum path 题解

想法

首先考虑问题的弱化版,如果不能往左走,能取到的最大值是多少。

这个问题可以用一个显然的 DP 解决,\(f_{i,j}\) 表示走到第 \(i\) 列,第 \(j\) 行,并且不会再访问这一列其它的方格,能取到的最大值。

转移可以从三个方向考虑,以 \(f_{i,1}\) 为例,黑色是当前状态,红黄蓝是可能的三个转移方向,每一个状态可以取到箭头经过的点的权值。\(f_{i,2}, f_{i,3}\) 同理。

思路

但是原题中人是可以往左回头走的,这样这个单纯的转移就不成立了,因为回头走会出现后效性。

既然如此,可以想一下有没有什么性质尚未被观察到。

根据人类的直觉,小人应该是不会回头走太多步的,这个直觉是正确的,小人最多回头走 \(1\) 格。

如图,黑色的路径和红色路径得到的权值是相等的。

猜测 所有的回头路径都有一个修正的路径对应

回头走的真谛是在遍历蓝色小人到黑色小人每一行的格子,如果能找到一条最多回头一次的路径也能遍历这些格子,就可以证明上面的引理。

观察到这种走法可以让要遍历的格子列数减去 2,而行位置不变。

那么如果要遍历的列数是奇数,可以直接一直走上图走法,剩余遍历列数是 1 时,直接往上走到目标点。

如果列数是偶数,则在剩余遍历列数是 2 时,走一次下图走法,回头一次走到目标点:

所以证明了对于任意一个最优解,都有一个最多只需要回头一次的路径得到的权值与其相等。

所以这样就好做了,我们只需要在弱化版的 DP 转移里面加上回头一次的情况。

\(f_{i, 2}\) 的转移不会出现回头的情况,而 \(f_{i,1}, f_{i, 3}\) 加上回头转移的即可,以 \(f_{i, 1}\) 为例,转移绿色路径:

问题得到了解决。

代码实现

时间复杂度:\(O(n)\)

// Problem: Maximum path
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-09-29 19:36:59#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n;
long long a[3][N], f[N][3];signed main() {ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n;for(int i = 0; i < 3; i ++) {for(int j = 1; j <= n; j ++) {cin >> a[i][j];}}memset(f, -0x3f, sizeof f);f[0][0] = 0;for(int i = 1; i <= n; i ++) {long long sum = 0;for(int j = 0; j < 3; j ++) {sum += a[j][i] + a[j][i - 1];}f[i][1] = max({f[i - 1][0] + a[0][i], f[i - 1][1], f[i - 1][2] + a[2][i]}) + a[1][i];f[i][0] = max({f[i - 1][0], f[i - 1][1] + a[1][i], f[i - 1][2] + a[2][i] + a[1][i]}) + a[0][i];f[i][2] = max({f[i - 1][2], f[i - 1][1] + a[1][i], f[i - 1][0] + a[0][i] + a[1][i]}) + a[2][i];if(i > 1) {f[i][0] = max(f[i][0], f[i - 2][2] + sum);f[i][2] = max(f[i][2], f[i - 2][0] + sum);}}cout << f[n][2] << '\n';return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程大学网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

函数基础和函数参数

第一部分:函数基础 函数的作用意义:1.为了更好地管理代码,可能对应的代码块需要重复多次使用,所以通过一个函数封装起来,便于下次直接调用2.方法实际上是通过函数实现的 例1:# type() # 内置函数 def lis():li=[1,2,3]li.append(4)li.pop(2) # 指定删除# print(li) # …

布尔数据 面的相交

布尔数据中面的相交的结果可能有交线,也可能有交点。将求交结果保存到FaceInfo中。从简单的两个平面重叠来看,将重叠的状态用变量theTangetFaces来保存。那任意两个曲面重叠如何判断呢?面的相交虽然提供类IntTools_FaceFace来计算,但是没有正确处理交线的范围,为什么BOPAl…

20211314王艺达学习笔记4

学习总结 第七章 文件操作 文件操作级别 (1)硬件级别 fdisk:将硬盘、U盘或SDC盘分区 mkfs:格式化磁盘分区,为系统做好准备 fsck:检 查和维修系统 碎片整理:压缩文件系统中的文件 (2)操作系统内核中的文件系统函数 前缀为k表示内核函数 (3)系统调用 open()、read()、…

9.28日

早上UML统一建模语言学了用例图,乒乓球课继续练习反手击球,进一步规范动作。下午离散学到了集合关系的闭包运算以及集合的划分和覆盖,数据结构则简单讲了二叉树的概念。package runoob.binarySearch;/*** 二分查找法*/ public class BinarySearch {// 二分查找法,在有序数组…

Logstash 获取通道类型 Redis 数据

Redis 服务器是 logstash 官方推荐的 broker 选择。Broker 角色也就意味着会同时存在输入和输出俩个插件。这里我们先学习输入插件。 LogStash::Inputs::Redis 支持三种 data_type(实际上是redis_type),不同的数据类型会导致实际采用不同的 Redis 命令操作:list => BLPO…