洛谷

前言

题目传送门

正解:动态规划

挺 duliu 一道题,难度较大 qwq。

PS:因为此篇题解前后改动较多,如果有什么错误请各位奆佬提出,本蒟蒻感激不尽 awa。

题意简述

给你一个 \(n\times m\) 大小的方格阵,可以把方格中的任意一个数改为 \(0\),每次从 \((1,1)\) 到 \((n,m)\) 的得分为路上所有数字的和。求每次改动数字后能得到的最大值的最小值。

法一:时间复杂度 \(Θ(m^2n^2)\) (TLE)

这但凡是个正常人都会想到吧……前两层循环枚举变为 \(0\) 的方格坐标,后两层按照正常的方格取数做法求最大值。

结果,被校 OJ 卡了没骗到分。

\(Code\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int m,n,t,a[2005][2005];
ll ans=LONG_LONG_MAX,dp[2005][2005];
int main(){
scanf("%d %d",&m,&n);
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
scanf("%d",&a[i][j]);
}
}
for(int x=1;x<=m;x++){
for(int y=1;y<=n;y++){
memset(dp,0,sizeof(dp));
t=a[x][y];
a[x][y]=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
dp[i][j]=max(dp[i][j-1],dp[i-1][j])+a[i][j];
}
}
ans=min(ans,dp[m][n]);
a[x][y]=t;
}
}
printf("%lld",ans);
return 0;
}

法二:正解,时间复杂度 \(Θ(mn)\)

PS * 2:因为 me 习惯用 \(m\) 表示行 \(n\) 表示列,所以下列题解就会这么写。

有点麻烦。

如果每个点只能遍历一次,那么必须不变值和变为 \(0\) 这两种情况需要同时考虑。

首先我们可以考虑先求出从 \((1,1)\) 点出发走到 \((i,j)\) 点和从 \((m,n)\)出发走到 \((i,j)\) 点能拿到的最大分数,分别存在 \(dp1\) 数组和 \(dp2\) 数组里。

这两个数组很好求,按照每个初学 DP 者都要打的取数板子就珂以了。至于为什么要求这两个数组,我先卖个关子,待会儿就知道了(逃。

我们知道,如果你从 \((1,1)\) 出发,在走的时候不经过某个坐标为 \((i,j)\) 的点(也就是绕过这个点),你有两种情况可以绕开它:

  1. 从左边绕。

  2. 从上边绕。

给这两种方法更严谨的定义 \((i>0)\) :

  1. 从左边绕:经过点 \((x,y-i)\)。

  2. 从上边绕:经过点 \((x-i,y)\)。

对于这两种情况,我们可以分别用两个二维数组 \(l\) (左边绕)和 \(d\) (上边绕)来存:

  • \(l_{i,j}\) 表示从左边绕过 \((i,j)\) 点能获得的最大值。

  • \(d_{i,j}\) 表示从上边绕过 \((i,j)\) 点能获得的最大值。

但是如何求这两个数组呢?

直接切入可能比较麻烦。这个时候我们可以先分析这种情况:

不管是往哪边绕,也不管前面怎么走,都紧贴着点 \((i,j)\) 过路方便分析,即,从左绕一定经过点 \((i,j-1)\),从上绕一定经过点 \((i-1,j)\)。

首先分析从左边绕的情况。

看图,蓝色区域表示从 \((1,1)\) 出发到点 \((i,j-1)\) 有可能会经过的区域,红色区域表示从点 \((i+1,j-1)\) 到 \((m,n)\) 有可能会经过的区域。至于为什么选这两个点呢,相信大家看图也能明白,因为选择这两个点可以做到经过的格子不重不漏,考虑到每种情况。

如果这么算,那么从点 \((i,j-1)\) 绕过去能拿到的最大分数就是:

\[score=dp1_{i,j-1}+dp2_{i+1,j-1}
\]

现在大家知道两个 \(dp\) 数组的意义了吧,就是用来求某个区域的最大分数的。因为如果每次循环到一个点就计算此点到终点的分数还需要两层循环会超时,基于走方格的方向是可逆的,我们只需要计算终点到每个点的最大分数就可以啦 OvO。

那我们现在只求了最贴近点 \((i,j)\) 的绕法,那我们如何求出往左绕的所有情况的最大值呢?

我们之前不是用了一个数组来存往左边绕的值吗?因为循环的顺序是从上到下,从左到右的,所以在求点 \((i,j)\) 的值时,我们已经把它左上方的所有值都求出来了。现在我们可以利用这些值,每一次,我们求当前 \(score\) 与之前最大分数的较大值,那每次都求最大值就是所有情况中的最大值。

那么最后的结果就是:

\[l_{i,j}=\max(dp1_{i,j-1}+dp2_{i+1,j-1},i_{i,j-1})
\]

至于为什么我们利用的是 \(l_{i,j-1}\),大家可以自己画图感知,这个格子就位于我们前面求的必须经过的那个格子 \((i,j-1)\),那么绕过它我们就会求必须经过点 \((i,j-2)\),这样我们就又需要考虑绕过这个格子的情况,就又必须经过点 \((i,j-3)\)……这么一层一层往左推,最后可以推到点 \((i,1)\),从而把每种情况都考虑到。

从上面绕分析方法也差不多,这里不多说画张图让读者感知一下。

(您看看这两张图多像,连大小都差不多26 KB)

最后求出 \(d_{i,j}\) 的式子为:

\[d_{i,j}=\max(dp1_{i-1,j}+dp2_{i-1,j+1},d_{i-1,j})
\]

最后求答案有些麻烦,因为题目要求的是变化后最能获得的最大分数的最小值,所以 \(\max\) 和 \(\min\) 是真的挺容易用混的,这里需要特别注意。

首先每对一个格子进行操作,最后得到的答案会是下列三种情况中的一种:

  1. 从左边绕过去得到的最大分数。

  2. 从上边绕过去得到的最大分数。

  3. 经过这个格子得到的最大分数。

前两个我们已经求解了,但其实第三种情况是灰常简单的!因为要保证经过点 \((i,j)\),所以我们只需要求 \(dp1_{i,j}+dp2{i,j}\) 就可以了。但是上面那个式子算了两次 \((i,j)\) 的值,而我们因为把它变成 \(0\) 了,就一次都不能算。所以还需要减去两个 \(a_{i,j}\)。

所以最后的答案终于可能被更新了 owo:

\[ans=\min\big(ans,\max(l_{i,j},d_{i,j},dp1_{i,j}+dp2_{i,j}-2\times a_{i,j})\big)
\]

最后再强调一遍要注意最大值和最小值别用反了啊! 别问我为什么知道(悲)。

\(Code\)

#include<bits/stdc++.h>
#define ll long long //记得要开long long哦!
using namespace std;
//温馨提示细节:因为最后答案还是求的最小值,所以 ans 需要定义极大值
ll m,n,ans=LONG_LONG_MAX,a[2005][2005],dp1[2005][2005],dp2[2005][2005],l[2005][2005],d[2005][2005];
int main(){
//输入
scanf("%lld %lld",&m,&n);
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
scanf("%lld",&a[i][j]);
}
}
//求两个 dp 数组的值
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
dp1[i][j]=max(dp1[i-1][j],dp1[i][j-1])+a[i][j];
}
}
for(int i=m;i;i--){
for(int j=n;j;j--){
dp2[i][j]=max(dp2[i+1][j],dp2[i][j+1])+a[i][j];
}
}
//核心代码开始
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
l[i][j]=max(l[i][j-1],dp1[i][j-1]+dp2[i+1][j-1]);
d[i][j]=max(d[i-1][j],dp1[i-1][j]+dp2[i-1][j+1]);
ans=min(ans,max(max(l[i][j],d[i][j]),dp1[i][j]+dp2[i][j]-2*a[i][j]));
}
}
//核心代码结束,输出答案
printf("%lld",ans);
return 0;
}

写在最后

这真的是一道很好的动态规划题,很考验思维,也有很多需要注意的细节。最后,看在本人写了那么久的份上,就请您随手点一下左下角那个小小的赞吧 qwq。

P6855「EZEC-4.5」走方格 TJ的更多相关文章

  1. LibreOJ #6007. 「网络流 24 题」方格取数 最小割 最大点权独立集 最大流

    #6007. 「网络流 24 题」方格取数 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   题目描述 ...

  2. Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流)

    Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流) Description 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从 ...

  3. 洛谷比赛 「EZEC」 Round 4

    洛谷比赛 「EZEC」 Round 4 T1 zrmpaul Loves Array 题目描述 小 Z 有一个下标从 \(1\) 开始并且长度为 \(n\) 的序列,初始时下标为 \(i\) 位置的数 ...

  4. macOS安装「oh my zsh」

    目前常用的 Linux 系统和 OS X 系统的默认 Shell 都是 bash,但是真正强大的 Shell 是深藏不露的 zsh, 这货绝对是马车中的跑车,跑车中的飞行车,史称『终极 Shell』, ...

  5. Loj #2324. 「清华集训 2017」小 Y 和二叉树

    Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...

  6. Loj #2321. 「清华集训 2017」无限之环

    Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...

  7. LOJ #6192. 「美团 CodeM 复赛」城市网络 (树上倍增)

    #6192. 「美团 CodeM 复赛」城市网络 内存限制:64 MiB 时间限制:500 ms 标准输入输出   题目描述 有一个树状的城市网络(即 nnn 个城市由 n−1n-1n−1 条道路连接 ...

  8. Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流)

    Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流) Description 给定一个由n 行数字组成的数字梯形如下图所示.梯形的第一行有m 个数字.从梯形的顶部的m 个数字开 ...

  9. Luogu 2764 最小路径覆盖问题 / Libre 6002 「网络流 24 题」最小路径覆盖 (网络流,最大流)

    Luogu 2764 最小路径覆盖问题 / Libre 6002 「网络流 24 题」最小路径覆盖 (网络流,最大流) Description 给定有向图G=(V,E).设P是G的一个简单路(顶点不相 ...

随机推荐

  1. CosId 通用、灵活、高性能的分布式 ID 生成器

    CosId 通用.灵活.高性能的分布式 ID 生成器 介绍 CosId 旨在提供通用.灵活.高性能的分布式系统 ID 生成器. 目前提供了俩大类 ID 生成器:SnowflakeId (单机 TPS ...

  2. .NET Core/.NET5/.NET6 开源项目汇总10:实用工具

    系列目录     [已更新最新开发文章,点击查看详细] 开源项目是众多组织与个人分享的组件或项目,作者付出的心血我们是无法体会的,所以首先大家要心存感激.尊重.请严格遵守每个项目的开源协议后再使用.尊 ...

  3. Kubernetes使用节点污点和pod容忍度阻止节点调度到特定节点

    Kubernetes允许你去影响pod被调度到哪个节点.起初,只能通过在pod规范里指定节点选择器来实现,后面其他的机制逐渐加入来扩容这项功能,本章将包括这些内容. 现在要介绍的高级调度的两个特性是节 ...

  4. 第9章:Ingress

    9.1 Ingress为弥补NodePort不足而生 1 单独用service暴露服务的方式,在实际生产环境中不太合适 ClusterIP 只能在集群内部访问. NodePort 方式的话,测试环境使 ...

  5. sys用户权限不足,本地登录失败 |ORA-01031 insufficient privileges|

    机器总喜欢挑放假的时候出问题,"双节"(中秋.国庆)快到了,对于搞系统运维的工程师来说其实并不轻松,于是今天赶紧装起一台数据库备用服务器以备半夜"机"叫. 安装 ...

  6. frp+nginx内网穿透

    frp+nginx内网穿透 背景:自己有台内网Linux主机,希望被外网访问(ssh.http.https): 准备工作 内网Linux主机-c,可以访问c主机和外网的主机-s(windows/lin ...

  7. JPA事务中的异常最后不也抛出了,为什么没被catch到而导致回滚?

    上周,我们通过这篇文章<为什么catch了异常,但事务还是回滚了?>来解释了,之前test4为什么会回滚的原因. 但还是收到了很多没有理解的反馈,主要是根据前文给出的线索去跟踪,是获得到了 ...

  8. Tomcat 中文乱码,设置UTF-8

    1.修改tomcat的conf目录下 server.xml文件加上 URIEncoding="UTF-8" <Connector port="8080" ...

  9. MySQL 删除devices表中id最大的一行

    原想法:delete from devices where id in (select max(id) from devices); 报错:ERROR 1093 (HY000): You can't ...

  10. Arduino IDE 2.0 beta安装

    1.在官网(Software | Arduino)下载安装包,此次提供操作系统有:Windows.Linux和macOC系统 2.点击安装包进行安装 3.点击我同意 4.点击下一步 5.选择安装路径( ...