前言
这道题是2019ICPC南京的一道铜牌题,是一道很明显的记忆化搜索题目。但是这道题的一些优化技巧很值得我们去学习。
题意
给定一个 n ∗ m n∗m n∗m的数字矩阵,从任意一个位置开始走,每次只能上下左右走一格,而且下一个位置必须比当前位置的数字大1,能继续往后走就不能停止,求总共有多少条长度大于等于4的路径。
数据范围
1
≤
n
,
m
≤
1000
1 \leq n,m \leq 1000
1≤n,m≤1000
−
1
0
9
≤
a
i
,
j
≤
1
0
9
-10^9 \leq a_{i,j} \leq 10^9
−109≤ai,j≤109
思路
其实这道题与之前滑雪那道题非常的相似,虽然这道题增添了长度的限制,但是还是可以采用记忆化搜索的思考方式。记忆化搜索可以转化为在一个DAG上按照拓扑序做dp,这道题也一样。
f
(
i
,
j
,
k
)
f(i,j,k)
f(i,j,k)表示的是以
(
i
,
j
)
(i,j)
(i,j)为终点,长度为
k
k
k的路径条数。因为只要长度大于等于4就符合要求,所以长度大于等于4的就可以归为一类,也就是
1
≤
k
≤
4
1 \leq k \leq 4
1≤k≤4。
另外设两个数组in[i][j]和out[i][j],记录每个点的入度和出度,入度为0的点为路径的起点,出度为0的点为路径的终点。
对图做拓扑序dp,状态转移方程大致可以写成这样,
f
(
t
x
,
t
y
,
k
)
=
(
f
(
t
x
,
t
y
,
k
)
+
f
(
x
,
y
,
k
−
1
)
)
%
m
o
d
f(tx,ty,k) = (f(tx,ty,k) + f(x,y,k-1))\%mod
f(tx,ty,k)=(f(tx,ty,k)+f(x,y,k−1))%mod。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
typedef pair<int,int> pii;
int n, m;
int g[N][N], in[N][N], out[N][N], f[N][N][5];
int dx[4] = {0,1,0,-1}, dy[4] = {1,0,-1,0};
void topsort()
{
queue<pii> que;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!in[i][j])
{
que.push({i,j});
f[i][j][1] = 1;
}
while(que.size())
{
auto t = que.front();
que.pop();
int x = t.first, y = t.second;
for(int i=0;i<4;i++)
{
int tx = x + dx[i], ty = y + dy[i];
if(tx<1||tx>n||ty<1||ty>m) continue;
if(g[tx][ty]==g[x][y]+1)
{
f[tx][ty][2] = (f[tx][ty][2] + f[x][y][1]) % mod;
f[tx][ty][3] = (f[tx][ty][3] + f[x][y][2]) % mod;
f[tx][ty][4] = (f[tx][ty][4] + f[x][y][3] + f[x][y][4]) % mod;
if(--in[tx][ty]==0) que.push({tx,ty});
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&g[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<4;k++)
{
int tx = i + dx[k], ty = j + dy[k];
if(tx<1||tx>n||ty<1||ty>m) continue;
if(g[tx][ty]==g[i][j]+1) out[i][j] ++;
if(g[tx][ty]==g[i][j]-1) in[i][j] ++;
}
int res = 0;
topsort();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!out[i][j])
res = (res + f[i][j][4]) % mod;
printf("%d\n",res);
return 0;
}