下面是我试图用记忆化搜索求最短路的错误算法
Acwing 844走迷宫
0能走,1,不能走。左上到右下,求最短路
#include<bits/stdc++.h>
using namespace std;
int d[104][104];
int a[104][104];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int h[104][104];
int n, m;
int f(int x, int y)
{
h[x][y]++;
if(x==n&&y==m) return 0;
if(d[x][y]) return d[x][y];
int mn=0x3f3f3f3f;
for(int i=0;i<4;i++)
{
int xx=x+dx[i];
int yy=y+dy[i];
if(h[xx][yy]==0&&a[xx][yy]==0&&xx>=1&&xx<=n&&yy>=1&&yy<=m)
{
mn=min(mn,f(xx,yy));
}
}
return d[x][y]=mn+1;
}
signed main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> a[i][j];
cout << f(1, 1) << endl;
return 0;
}
随便一组数据就能hack
10 10
0 0 0 0 0 0 1 0 0 1
0 0 1 0 0 1 0 0 0 1
0 0 1 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 1 1 0 0 0 0 1 0 0
0 0 0 0 0 0 1 0 0 1
0 0 0 1 1 1 0 1 1 1
0 0 0 0 0 0 0 0 0 1
0 0 0 0 1 0 1 0 0 0
0 0 0 0 1 0 1 1 1 0
32 31 30 29 28 1061109568 0 1061109568 1061109568 0
1061109568 1061109568 0 26 27 0 1061109568 1061109568 1061109568 0
1061109568 1061109568 0 25 24 0 1061109568 1061109568 1061109568 1061109568
1061109568 1061109568 1061109568 1061109568 23 22 21 1061109568 1061109568 1061109568
0 0 0 17 18 19 20 0 1061109568 1061109568
13 14 15 16 1061109568 1061109568 0 1061109568 1061109568 0
12 11 10 0 0 0 1061109568 0 0 0
1061109568 1061109568 9 8 7 6 5 4 3 0
1061109568 1061109568 1061109568 1061109568 0 1061109568 0 1061109568 2 1
1061109568 1061109568 1061109568 1061109568 0 1061109568 0 0 0 0
上面这个是d数组,注意第一行 29 28 27 26那里,显然兜圈了。
我们原本的想法很好,对于某个(x,y)点,找没遍历的且相邻的点P到终点的最小值,然后+1更新这个点。
但是这么做的正确的前提是:对于经过P和(x,y)的路径,找最优解时只需考虑某个(x,y)走到P,而不需考虑是P走到(x,y).
但是显然这个最短路的问题两种情况都要考虑,P走到(x,y)的情况上面的算法没考虑到。
(如果想改一点代码考虑到这种情况,可能会导致A需要B的值,B需要A的值的情况)
而对于同样是图论的滑雪问题(找最长相邻下降序列)
由于考虑一个数的时候只需与之相邻的,且相邻两个数的大小关系决定了这两个点在路径中的先后顺序,所以比如你考虑到20这个数时,fun(20)可能是fun(19)+1;而考虑到19时,fun(19)不可能是fun(20)+1。
这个特点是最短路问题没有的。
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
总结:
所以不管是dp,还是这种记忆化搜索,我们都讲究一个顺序。这也是所谓的状态转移。
对于上面的朴素的最短路问题我们是可以用bfs的,bfs也是一种顺序。
其实我们是通过状态转移来设计的遍历的顺序。