【洛谷 P3842 [TJOI2007]线段】解题报告(动态规划)
题面
在一个 的平面上,在每一行中有一条线段,第 行的线段的左端点是 ,右端点是 。
你从 点出发,要求沿途走过所有的线段,最终到达 点,且所走的路程长度要尽量短。
更具体一些说,你在任何时候只能选择向下走一步(行数增加 )、向左走一步(列数减少 )或是向右走一步(列数增加 )。当然,由于你不能向上行走,因此在从任何一行向下走到另一行的时候,你必须保证已经走完本行的那条线段。
,。
题解
本题的状态比较显然。
由于每一层必然走过整条线段,所以显然从线段的左/右端点向下走是最优的。我们拆分子问题,求从 走到第 层线段左/右端点的最少步数,可以通过从 走到第 层线段左/右端点的最少步数求出,因此从 走到第 层线段左/右端点的最少步数就是一个子问题。
根据我们得到的子问题,我们设状态 表示从 走到第 层线段的左/右端点的最少步数。以从 向 转移为例(也就是从上一层线段左端点走到这一层线段右端点),我们首先需要横向走到这一层线段左端点,将横坐标从 走到 ,显然需要 步,然后从线段左端点走到线段右端点,显然需要 步,又因为需要从上一层走到这一层,还需要额外的 步。我们得到转移 。类似地,我们可以得到从 向 的所有转移,对于一个目标状态,把所有能转移过来的结果取最小值即可。
最终我们还需要从 或 走到 ,可以按照类似的方法求出 。
初始状态就是从 到 的距离,或者到 再到 的距离。
参考代码
//By: Luogu@rui_er(122461)
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
#define debug printf("Running %s on line %d...\n",__FUNCTION__,__LINE__)
using namespace std;
typedef long long ll;
const int N = 2e4+5;
int n, L[N], R[N], dp[N][2];
template<typename T> void chkmin(T &x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T &x, T y) {if(x < y) x = y;}
int main() {
scanf("%d", &n);
rep(i, 1, n) scanf("%d%d", &L[i], &R[i]);
dp[1][0] = 2 * R[1] - L[1] - 1;
dp[1][1] = R[1] - 1;
rep(i, 2, n) {
dp[i][0] = min(dp[i-1][0]+abs(L[i-1]-R[i])+R[i]-L[i],
dp[i-1][1]+abs(R[i-1]-R[i])+R[i]-L[i]) + 1;
dp[i][1] = min(dp[i-1][0]+abs(L[i-1]-L[i])+R[i]-L[i],
dp[i-1][1]+abs(R[i-1]-L[i])+R[i]-L[i]) + 1;
}
int ans = min(dp[n][0]+n-L[n], dp[n][1]+n-R[n]);
printf("%d\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现