Digital Path(记忆化搜索)

前言

这道题是2019ICPC南京的一道铜牌题,是一道很明显的记忆化搜索题目。但是这道题的一些优化技巧很值得我们去学习。

题意

给定一个 n ∗ m n∗m nm的数字矩阵,从任意一个位置开始走,每次只能上下左右走一格,而且下一个位置必须比当前位置的数字大1,能继续往后走就不能停止,求总共有多少条长度大于等于4的路径。

数据范围

1 ≤ n , m ≤ 1000 1 \leq n,m \leq 1000 1n,m1000
− 1 0 9 ≤ a i , j ≤ 1 0 9 -10^9 \leq a_{i,j} \leq 10^9 109ai,j109

思路

其实这道题与之前滑雪那道题非常的相似,虽然这道题增添了长度的限制,但是还是可以采用记忆化搜索的思考方式。记忆化搜索可以转化为在一个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 1k4
另外设两个数组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,k1))%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;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值