leetcode刷题笔记-LCP31-dp+思维

题目链接:https://leetcode-cn.com/problems/Db3wC1/

题目大意

给出一个 N ∗ M ∗ T N*M*T NMT的图,有障碍物和道路,一共有T个时刻,每个时刻图会变成对应时刻的样子,每个时刻可以往相邻的格子走一步或停留,问是否能在T时刻内从左上方走到右下方
可以选择一个障碍物消除一个时刻,以及选择一个位置的障碍物永久消除

题解

永久消除说明可能多次经过该点,与停留在该点等价
d p [ t ] [ x ] [ y ] [ p 1 ] [ p 2 ] 表 示 t 时 刻 , 在 位 置 ( x , y ) dp[t][x][y][p1][p2]表示t时刻,在位置(x,y) dp[t][x][y][p1][p2]t,(x,y)
p 1 , p 2 表 示 两 个 消 除 是 否 已 经 使 用 p1,p2表示两个消除是否已经使用 p1,p2使
时间从后往前倒着走
初始值 d p [ T − 1 ] [ N − 1 ] [ M − 1 ] [ 0 ] [ 0 ] = 1 , 其 余 为 0 dp[T-1][N-1][M-1][0][0]=1,其余为0 dp[T1][N1][M1][0][0]=1,0
转移方程
( x , y ) − > ( p x , p y ) (x,y)->(px,py) (x,y)>(px,py)
( p x , p y ) = ′ . ′ : d p [ t ] [ p x ] [ p y ] [ p 1 ] [ p 2 ] ∣ = d p [ t + 1 ] [ p x ] [ p y ] [ p 1 ] [ p 2 ] (px,py)='.':dp[t][px][py][p1][p2]|=dp[t+1][px][py][p1][p2] (px,py)=.:dp[t][px][py][p1][p2]=dp[t+1][px][py][p1][p2]
( p x , p y ) = ′ # ′ : (px,py)='\#': (px,py)=#:
   ( p 1 = 0 ) : d p [ t ] [ p x ] [ p y ] [ 1 ] [ p 2 ] ∣ = d p [ t + 1 ] [ x ] [ y ] [ p 1 ] [ p 2 ] \ \ (p1=0):dp[t][px][py][1][p2]|=dp[t+1][x][y][p1][p2]   (p1=0):dp[t][px][py][1][p2]=dp[t+1][x][y][p1][p2]
   ( p 2 = 0 ) : d p [ t ] [ p x ] [ p y ] [ p 1 ] [ 1 ] ∣ = d p [ t + k ] [ x ] [ y ] [ p 1 ] [ p 2 ] ( 1 ≤ k ≤ n − t − 1 ) \ \ (p2=0):dp[t][px][py][p1][1]|=dp[t+k][x][y][p1][p2](1\leq k\leq n-t-1)   (p2=0):dp[t][px][py][p1][1]=dp[t+k][x][y][p1][p2](1knt1)
发现永久消除转移的时候时间复杂度过高,但可以后缀和处理
s d [ t ] [ x ] [ y ] [ p 1 ] = m a x t ≤ k ≤ T − 1 ( d p [ k ] [ x ] [ y ] [ p 1 ] [ 0 ] ) sd[t][x][y][p1]=max_{t\leq k\leq T-1}(dp[k][x][y][p1][0]) sd[t][x][y][p1]=maxtkT1(dp[k][x][y][p1][0])
则转移为
   ( p 2 = 0 ) : d p [ t ] [ p x ] [ p y ] [ p 1 ] [ 1 ] ∣ = s d [ t + 1 ] [ x ] [ y ] [ p 1 ] \ \ (p2=0):dp[t][px][py][p1][1]|=sd[t+1][x][y][p1]   (p2=0):dp[t][px][py][p1][1]=sd[t+1][x][y][p1]
复杂度压制到 O ( T N M ) O(TNM) O(TNM)
进一步可以压位dp和sd,以节省空间

代码

#define ms(a) memset(a, 0, sizeof(a))
bool dp[101][50][50][2][2];
bool sd[101][50][50][2];
int dx[5] = {0, 1, -1, 0, 0};
int dy[5] = {0, 0, 0, 1, -1};
int T, n, m;
class Solution
{
public:
    bool escapeMaze(vector<vector<string>> &maze)
    {
        ms(dp);
        ms(sd);
        T = maze.size();
        n = maze[0].size();
        m = maze[0][0].length();
        dp[T - 1][n - 1][m - 1][0][0] = sd[T - 1][n - 1][m - 1][0] = true;
        for (int t = T - 2; t >= 0; t--)
        {
            for (int x = 0; x < n; x++)
                for (int y = 0; y < m; y++)
                    for (int p1 = 0; p1 < 2; p1++)
                        for (int p2 = 0; p2 < 2; p2++)
                        {
                            for (int i = 0; i < 5; i++)
                            {
                                int px = x + dx[i], py = y + dy[i];
                                if (px >= 0 && px < n && py >= 0 && py < m)
                                {
                                    if (maze[t][px][py] == '.')
                                    {
                                        dp[t][px][py][p1][p2] |= dp[t + 1][x][y][p1][p2];
                                    }
                                    else
                                    {
                                        if (!p1)
                                            dp[t][px][py][1][p2] |= dp[t + 1][x][y][p1][p2];
                                        if (!p2)
                                            dp[t][px][py][p1][1] |= sd[t + 1][x][y][p1];
                                    }
                                }
                            }
                            // if (dp[t][x][y][p1][p2])
                            // {
                            //     int xxx = 0;
                            // }
                        }
            for (int x = 0; x < n; x++)
                for (int y = 0; y < m; y++)
                    for (int p1 = 0; p1 < 2; p1++)
                    {
                        sd[t][x][y][p1] = sd[t + 1][x][y][p1] | dp[t][x][y][p1][0];
                        // if (dp[t][x][y][p1][0])
                        // {
                        //     int xxx = 0;
                        // }
                    }
        }
        bool ans = false;
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < 2; j++)
                if (dp[0][0][0][i][j])
                    ans = true;
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值