P6855「EZEC-4.5」走方格 TJ
前言
正解:动态规划
挺 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)\) 的点(也就是绕过这个点),你有两种情况可以绕开它:
从左边绕。
从上边绕。
给这两种方法更严谨的定义 \((i>0)\) :
从左边绕:经过点 \((x,y-i)\)。
从上边绕:经过点 \((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)\) 绕过去能拿到的最大分数就是:
\]
现在大家知道两个 \(dp\) 数组的意义了吧,就是用来求某个区域的最大分数的。因为如果每次循环到一个点就计算此点到终点的分数还需要两层循环会超时,基于走方格的方向是可逆的,我们只需要计算终点到每个点的最大分数就可以啦 OvO。
那我们现在只求了最贴近点 \((i,j)\) 的绕法,那我们如何求出往左绕的所有情况的最大值呢?
我们之前不是用了一个数组来存往左边绕的值吗?因为循环的顺序是从上到下,从左到右的,所以在求点 \((i,j)\) 的值时,我们已经把它左上方的所有值都求出来了。现在我们可以利用这些值,每一次,我们求当前 \(score\) 与之前最大分数的较大值,那每次都求最大值就是所有情况中的最大值。
那么最后的结果就是:
\]
至于为什么我们利用的是 \(l_{i,j-1}\),大家可以自己画图感知,这个格子就位于我们前面求的必须经过的那个格子 \((i,j-1)\),那么绕过它我们就会求必须经过点 \((i,j-2)\),这样我们就又需要考虑绕过这个格子的情况,就又必须经过点 \((i,j-3)\)……这么一层一层往左推,最后可以推到点 \((i,1)\),从而把每种情况都考虑到。
从上面绕分析方法也差不多,这里不多说画张图让读者感知一下。
(您看看这两张图多像,连大小都差不多26 KB)
最后求出 \(d_{i,j}\) 的式子为:
\]
最后求答案有些麻烦,因为题目要求的是变化后最能获得的最大分数的最小值,所以 \(\max\) 和 \(\min\) 是真的挺容易用混的,这里需要特别注意。
首先每对一个格子进行操作,最后得到的答案会是下列三种情况中的一种:
从左边绕过去得到的最大分数。
从上边绕过去得到的最大分数。
经过这个格子得到的最大分数。
前两个我们已经求解了,但其实第三种情况是灰常简单的!因为要保证经过点 \((i,j)\),所以我们只需要求 \(dp1_{i,j}+dp2{i,j}\) 就可以了。但是上面那个式子算了两次 \((i,j)\) 的值,而我们因为把它变成 \(0\) 了,就一次都不能算。所以还需要减去两个 \(a_{i,j}\)。
所以最后的答案终于可能被更新了 owo:
\]
最后再强调一遍要注意最大值和最小值别用反了啊! 别问我为什么知道(悲)。
\(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的更多相关文章
- LibreOJ #6007. 「网络流 24 题」方格取数 最小割 最大点权独立集 最大流
#6007. 「网络流 24 题」方格取数 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 ...
- Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流)
Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流) Description 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从 ...
- 洛谷比赛 「EZEC」 Round 4
洛谷比赛 「EZEC」 Round 4 T1 zrmpaul Loves Array 题目描述 小 Z 有一个下标从 \(1\) 开始并且长度为 \(n\) 的序列,初始时下标为 \(i\) 位置的数 ...
- macOS安装「oh my zsh」
目前常用的 Linux 系统和 OS X 系统的默认 Shell 都是 bash,但是真正强大的 Shell 是深藏不露的 zsh, 这货绝对是马车中的跑车,跑车中的飞行车,史称『终极 Shell』, ...
- Loj #2324. 「清华集训 2017」小 Y 和二叉树
Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...
- Loj #2321. 「清华集训 2017」无限之环
Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...
- LOJ #6192. 「美团 CodeM 复赛」城市网络 (树上倍增)
#6192. 「美团 CodeM 复赛」城市网络 内存限制:64 MiB 时间限制:500 ms 标准输入输出 题目描述 有一个树状的城市网络(即 nnn 个城市由 n−1n-1n−1 条道路连接 ...
- Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流)
Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流) Description 给定一个由n 行数字组成的数字梯形如下图所示.梯形的第一行有m 个数字.从梯形的顶部的m 个数字开 ...
- Luogu 2764 最小路径覆盖问题 / Libre 6002 「网络流 24 题」最小路径覆盖 (网络流,最大流)
Luogu 2764 最小路径覆盖问题 / Libre 6002 「网络流 24 题」最小路径覆盖 (网络流,最大流) Description 给定有向图G=(V,E).设P是G的一个简单路(顶点不相 ...
随机推荐
- Ruby升级的最新方法/CocoaPods安装
今天安装cocoapods时候出现了下面的提示 Error installing pods:active support requires Ruby version >= 2.2.0//这个需求 ...
- ES6学习笔记之函数(二)
5.作用域 使用默认参数时,参数会形成一个独立的作用域,此作用域与函数体中的作用域是平行关系,互不影响. var x = 1; function show(x, y= function () { x= ...
- ES6的 class的基本语法
1.类的由来 JavaScript 语言中,生成实例对象的传统方法是通过构造函数,但是这种写法跟传统的面向对象语言差异很大,容易让不熟悉这门语言的程序员感到困惑,下面有一个例子 ES6提供了更接 ...
- redis为什么那么快?
数据库有很多,为什么Redis能有如此突出的表现呢?一方面,因为它是内存数据库,所有操作都在内存上完成.另外一方面就要归功于他的数据结构.高效的数据结构是Redis快速处理的基础.今天我们就来聊聊了R ...
- 如何快速实现一个虚拟 DOM 系统
虚拟 DOM 是目前主流前端框架的技术核心之一,本文阐述如何实现一个简单的虚拟 DOM 系统. 为什么需要虚拟 DOM? 虚拟 DOM 就是一棵由虚拟节点组成的树,这棵树展现了真实 DOM 的结构.这 ...
- js代码 简单实现数字滚动增加动效(原)
<html><body> <span>look this:</span><span id="nums">10000< ...
- bugku flag在index里面
先点进去看看. 看到file,似乎在暗示着我们,php://filter/read/convert.base64-encode/resource=index.php, 这句将index.php内容用b ...
- XCTF(MISC) 图片隐写
题目描述:菜猫给了菜狗一张图,说图下面什么都没有 1.给了个pdf,打开是这玩意 2.盲猜flag是图片后面,右键直接删除图片试试. 答案出来了.
- Luogu P2051「AHOI2009」中国象棋
看见第一眼觉得是状压 \(\text{DP}\)?观察数据范围发现不可做 那按照最常规思路设状态试试? 设状态为\(dp[i][j]\)表示\(i*j\)的棋盘的方案数 好像转移不了欸 要不再来一维? ...
- Linux- RPM与yum软件包安装
Linux安装及管理程序一.Linux应用程序基础1)应用程序与系统命令的关系2)典型应用程序的目录结构3)常见的软件包封装类型二.RPM包管理工具① RPM软件包管理器Red-Hat Package ...