解题思路
首先我们容易想到一个简单的动态规划方法:
用dp[t][x][y][s][fx][fy]表示在时间t时,s(0或1)表示临时消除法是否使用,以及永久消除法已经用在点(fx,fy),点(x,y)能否到达。用递推来计算出所有dp状态的结果。
但是,这个方法的时间复杂度位O(Tnm2nm5),其中5为转移复杂度,最坏情况下约为10^9,复杂度太高。
我们来考虑如何优化这个dp表达式,显然时间t,坐标(x,y),状态s都无法优化。我们把优化重点放到永久消除卡上。
分类讨论:
1,如果我们使用永久消除卡后,不再回到永久消除卡使用的点,那么永久消除卡就相当于一个临时消除卡。
2,如果我们使用永久消除卡后,再回到永久消除卡使用的点,那么相当于我一直呆在这个点没有移动。我们可以用一直呆在这个点,等价替换离开这个点再回到这个点的情况。
因此我们就不用再开两维来记录永久消除卡使用在哪里,我们只需要记录永久消除卡是否使用。在状态转移的时候,若本次使用了永久消除卡,则更新一直待在这个点的其余状态。
具体来说,我们用dp[t][x][y][s],表示在时间t,s为状态消除卡使用状态(二进制最低位表示临时消除卡是否使用,二进制次低位表示永久消除卡是否使用),点(x,y)能否到达。用我们上述方法进行转移即可。转移细节见代码。
代码
class Solution {
public:
bool escapeMaze(vector<vector<string>>& maze) {
int T=maze.size();
int dir[5][2]={1,0,-1,0,0,1,0,-1,0,0};
int n=maze[0].size();
int m=maze[0][0].size();
bool dp[110][55][55][5];
memset(dp,false,sizeof(dp));
dp[0][0][0][0]=true;
for(int t=0;t<T-1;t++)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
for(int s=0;s<=3;s++)
{
if(!dp[t][i][j][s])
continue;
for(int k=0;k<5;k++)
{
int x=i+dir[k][0];
int y=j+dir[k][1];
if(x<0||x>=n||y<0||y>=m)
continue;
if(maze[t+1][x][y]=='.')
{
dp[t+1][x][y][s]=true;
if(x==n-1&&y==m-1)
return true;
} else {
if((s&1)==0)
{
dp[t+1][x][y][s|1]=true;
if(x==n-1&&y==m-1)
return true;
}
if((s&2)==0)
{
if(x==n-1&&y==m-1)
return true;
for(int z=t+1;z<T;z++)
{
dp[z][x][y][s|2]=true;
}
}
}
}
}
}
}
}
return false;
}
};
作者:ma-lie
链接:https://leetcode-cn.com/problems/Db3wC1/solution/dong-tai-gui-hua-by-ma-lie-u5gc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:ma-lie
链接:https://leetcode-cn.com/problems/Db3wC1/solution/dong-tai-gui-hua-by-ma-lie-u5gc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。