【学习笔记】路径压缩dp

NCC79601

2019-06-18 13:35:19

Personal

## 例题 [P1052](https://www.luogu.org/problemnew/show/P1052) 瞟一眼一下这道题,感觉好像是个很简单的$\text{dp}$,只需要用$f[i]$维护在$i$位置需要踩的最少石子数,然后按照$f[i]=\min(f[i-j]\vert i-j\geq0,j\in[s,t])+stone[i]$转移一发即可。 但是仔细一看:$N\leq\text{1e9}$,完蛋。 转移方法肯定是不会改变的,那么肯定需要用某种方式进行离散化(也就是所谓的**路径压缩**),然后再进行转移。 # 分析 观察这道题的$s,t\in[1,10]$,然后一想:$lcm(1,2,\cdots,10)=2520$,然后$2520\times100=252000=\text{2e5}$,这好像就变成正常数据范围了。 事实上这是**正确的**。这里使用的路径压缩就是用$lcm(1,2,\cdots,10)=2520$来对每一段距离取模,从而达到缩短路径的目的。可以简单思考一下, 不管青蛙怎么样跳,它肯定可以跳到$2520$处,合情推理一下,对$2520$取模之后的距离可以表示原距离的**所有情况**,也就是说是不是一定会踩到石头上只与对$2520$取模之后的余数相关,而与多出去的那一部分无关。这个也不难理解,总可以用某种距离组合来得出压缩之前的情况。 因此,可以大胆地进行**路径压缩**!具体方式为找到一种能将长距离状态简化为短距离状态的方法,此后再进行$\text{dp}$。 ```cpp #include<bits/stdc++.h> using namespace std; const int LCM = 2520; const int MAXN = 110; int l, s, t, m; int a[MAXN], d[MAXN], f[MAXN * LCM]; bool stone[MAXN * LCM]; int main() { scanf("%d%d%d%d", &l, &s, &t, &m); for(int i = 1; i <= m; i++) scanf("%d", &a[i]); sort(a + 1, a + m + 1); for(int i = 1; i <= m; i++) d[i] = (a[i] - a[i - 1]) % LCM; for(int i = 1; i <= m; i++) { a[i] = a[i - 1] + d[i]; stone[a[i]] = true; } l = a[m]; // descrete // printf("%d\n", l); for(int i = 1; i <= l + t; i++) f[i] = m; f[0] = 0; for(int i = 1; i <= l + t; i++) for(int j = s; j <= t; j++) { if(i - j >= 0) f[i] = min(f[i], f[i - j]); f[i] += stone[i]; } int ans = 0x3f3f3f3f; // printf("%d", ans); for(int i = l; i < l + t; i++) ans = min(ans, f[i]); printf("%d", ans); return 0; } ```