Luogu P3842 [TJOI2007]线段 解题报告
在一个
n*n 的平面上,在每一行中有一条线段,第i 行的线段的左端点是(i, l_i) ,右端点是(i, r_i) ,其中i \leq l_i \leq r_i \leq n 。你从
(1, 1) 点出发,要求沿途走过所有的线段,最终到达(n, n) 点,且所走的路程长度要尽量短。更具体一些说,你在任何时候只能选择向下走一步(行数增加 1)、向左走一步(列数减少 1)或是向右走一步(列数增加 1)。当然,由于你不能向上行走,因此在从任何一行向下走到另一行的时候,你必须保证已经走完本行的那条线段。
解题思路:
线性 DP.
首先我们观察题目中的三种操作,可以概括成:
- 在横坐标上 +1 -1
- 在纵坐标上 +1
初步定义
然而我们很快可以发现,这里的
我们再对这个过程进行深入分析,发现其实在走线段的过程中只有横坐标会变化,而且这个变化并不用 DP 维护,暴力加上即可。
所以我们再定义
这样我们的转移也呼之欲出了,
上述方程表示由上一行的线段的左端点转移来时,先走到当前行的右端点再向下走,最后走一遍到当前线段的左端点。右端点同理。
同理,
上述方程表示由上一行的线段的左端点转移来时,先走到当前行的左端点再向下走,最后走一遍到当前线段的左端点。右端点同理。
因为其满足各个维度线性增长,故其为线性 DP ,时间复杂度
代码:
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
#define ll long long
#define ri register int
char buf[1 << 20], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2)?EOF: *p1++)
template <typename T> inline void read(T &t) {
ri v = getchar();T f = 1;t = 0;
while (!isdigit(v)) {if (v == '-')f = -1;v = getchar();}
while (isdigit(v)) {t = t * 10 + v - 48;v = getchar();}
t *= f;
}
template <typename T,typename... Args> inline void read(T &t,Args&... args) {
read(t);read(args...);
}
template <typename T> inline T min(T x,T y) {return x<y?x:y;}
template <typename T> inline T max(T x,T y) {return x>y?x:y;}
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3fLL;
const int N = 25100;
int f[N][2],l[N],r[N],n;
inline int dis(int x,int y) {return abs(x-y);}
signed main() {
read(n);
for (int i = 1;i <= n;++i) read(l[i],r[i]);
f[1][1] = dis(1,r[1]),f[1][0] = dis(1,r[1]) + dis(l[1],r[1]);
for (int i = 2;i <= n;++i) {
int len = dis(l[i],r[i]);
f[i][0] = min(f[i-1][0]+dis(l[i-1],r[i]),f[i-1][1]+dis(r[i-1],r[i])) + len + 1;
f[i][1] = min(f[i-1][0]+dis(l[i-1],l[i]),f[i-1][1]+dis(r[i-1],l[i])) + len + 1;
}
int ans = min(f[n][0] + dis(l[n],n),f[n][1] + dis(r[n],n));
printf("%d\n", ans);
return 0;
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
评论已关闭