P11952 行走 题解
一道非常经典和锻炼人的 dp 题,建议一定好好做。
先理清题目:有一个网格,每条边都有一个边权,存储方式如下:
然后每次删去一条边,求最短路。
先放代码,思路最好结合来理解:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// 全局变量 seed 和 B
unsigned long long seed;
int B;
const long long N = 5005, inf = 1e16;
long long f[N][N], f2[N][N], ea[N][N], eb[N][N];
// xorshift64 算法,不能修改
unsigned long long xorshift64(){
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
return seed;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
cin >> seed >> B;
// 生成横向边权:遍历 i=1..n, j=1..n-1
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n-1; j++){
xorshift64();
ea[i][j] = seed & ((1ULL << B) - 1);
}
}
// 生成纵向边权:遍历 i=1..n-1, j=1..n
for (int i = 1; i <= n-1; i++){
for (int j = 1; j <= n; j++){
xorshift64();
eb[i][j] = seed & ((1ULL << B) - 1);
}
}
// 你可以从这里开始编写你的代码
for (int i = 2; i <= n; i ++ ) f[1][i] = f[1][i - 1] + ea[1][i - 1];
for (int i = 2; i <= n; i ++ ) f[i][1] = f[i - 1][1] + eb[i - 1][1];
for (int i = n - 1; i >= 1; i -- ) f2[n][i] = f2[n][i + 1] + ea[n][i];
for (int i = n - 1; i >= 1; i -- ) f2[i][n] = f2[i + 1][n] + eb[i][n];
for (int i = 2; i <= n; i ++ )
for (int j = 2; j <= n; j ++ )
f[i][j] = min(f[i - 1][j] + eb[i - 1][j], f[i][j - 1] + ea[i][j - 1]);
for (int i = n - 1; i >= 1; i -- )
for (int j = n - 1; j >= 1; j -- )
f2[i][j] = min(f2[i][j + 1] + ea[i][j], f2[i + 1][j] + eb[i][j]);
for (int i = 0; i <= n + 1; i ++ )
for (int j = 0; j <= n + 1; j ++ )
{
if (i < 1 || j < 1 || i > n || j >= n) ea[i][j] = inf;
if (i < 1 || j < 1 || i >= n || j > n) eb[i][j] = inf;
}
for (int j = 1; j <= n; j ++ )
for (int i = n; i >= 1; i -- )
ea[i][j] = min(ea[i + 1][j], f[i][j] + ea[i][j] + f2[i][j + 1]);
for (int i = 1; i <= n; i ++ )
for (int j = n; j >= 1; j -- )
eb[i][j] = min(eb[i][j + 1], f[i][j] + eb[i][j] + f2[i + 1][j]);
while (q -- )
{
int x, y, xx, yy;
cin >> x >> y >> xx >> yy;
if (x == xx) cout << min(ea[x + 1][y], eb[x - 1][y + 1]) << "\n";
else cout << min(eb[x][y + 1], ea[x + 1][y - 1]) << "\n";
}
return 0;
}
如果没有删边的话,那这道题就是一道简单的动规题目,转移方程就是
但是有删边。考虑到一定经过一条边
下一步就是在 dp,所以先把周围都清除成无穷大。(这里有一个坑点就是一开始 RE,所以前面的定义要改)
这里的含义变了(重中之重!),设
接下来的难点是如何求答案。先看去掉
可以发现,想要绕过断掉的边,就只能在这两条边上不断向下/右绕,取最小值即可。
如果是断掉竖着的边那就整个反过来就行了,主要看代码。
感觉好像表达的并不是很通畅(?)如果有更多的意见和方法希望能在评论区留言,我也很希望能更深入理解这道题目。