RQNOJ 342 最不听话的机器人:网格dp
题目链接:https://www.rqnoj.cn/problem/342
题意:
DD 有一个不太听话的机器人,这个机器人总是会有自己的想法,而不会完全遵守 DD 给它的指令。
现在 DD 在试图命令机器人走迷宫。迷宫是一个 N*N 个格子组成的区域,格子自左上角到右下角从 (1,1) 到 (N,N) 编号。第 i 行、第 j 列的格子编号为 (i,j)。迷宫中的某些区域是障碍物,机器人不能移动到那里。
DD 给了机器人 M 条指令,指令的类型包括“前进一步”“后退一步”“左转九十度”“右转九十度”。但问题是机器人并不能完全遵守这些指令,因为如果机器人完全遵守这些指令,它可能会走到障碍物的格子里或者走到迷宫外面去,那样就会有危险。机器人希望从这个指令序列里面去掉一些,然后执行剩下的指令时,可以保证整个过程中都不会有危险。
机器人虽然不太听话,但它并不想惹恼了 DD,否则 DD 可能会把它拆掉的。所以机器人希望去掉的指令尽量少。
迷宫的大小是 N*N,指令共有 M 条,机器人初始时的位置是 (X0,Y0)。机器人初始时面朝的方向是上方。
那么,机器人最少需要去掉多少条指令才能保证不会有危险呢?
题解:
表示状态:
dp[i][x][y][d] = min num of deleted orders
i:考虑到第i条指令
x,y:当前位置
d:当前方向
找出答案:
min legal dp[m][x][y][d]
如何转移:
now: dp[i][x][y][d]
dp[i+1][x][y][d] = min dp[i][x][y][d] + 1 (不执行)
dp[i+1][nx][ny][nd] = min dp[i][x][y][d] (执行)
nx,ny,nd为执行第i条指令后的位置和方向。
边界条件:
dp[0][x0][y0][UP] = 0
ohters = INF
(UP为方向向上的编号)
注:空间限制,要把第一维[MAX_M]变为[2]。
小技巧:
如果压维的时候前后数据间有影响,则可以开一个vis数组。
更新dp[i&1][x][y][d]时,将vis[i&1][x][y][d] = i,意为当前dp的位置是在i的时候更新的。
每次用到dp[i&1][x][y][d]时,判断一下相应的vis。
如果vis[i&1][x][y][d] = i,则返回dp值,否则返回初始值INF(dp[i][x][y][d]这个状态还没被更新过)。
AC Code:
1 // state expression: 2 // dp[i][x][y][d] = min num of deleted orders 3 // i: considering ith order 4 // x,y: present pos 5 // d: present direction 6 // 7 // find the answer: 8 // min legal dp[m][x][y][d] 9 // 10 // transferring: 11 // now: dp[i][x][y][d] 12 // dp[i+1][x][y][d] = min dp[i][x][y][d] + 1 13 // dp[i+1][nx][ny][nd] = min dp[i][x][y][d] 14 // 15 // bound: 16 // dp[0][x0][y0][UP] = 0 17 // ohters = INF 18 #include <iostream> 19 #include <stdio.h> 20 #include <string.h> 21 #define MAX_N 105 22 #define MAX_M 1005 23 #define MAX_D 5 24 #define INF 10000000 25 26 using namespace std; 27 28 const int dx[]={-1,0,1,0}; 29 const int dy[]={0,1,0,-1}; 30 31 int n,m,x0,y0; 32 int ans; 33 int c[MAX_M]; 34 int dp[2][MAX_N][MAX_N][MAX_D]; 35 int vis[2][MAX_N][MAX_N][MAX_M]; 36 char a[MAX_N][MAX_N]; 37 38 void read() 39 { 40 cin>>n>>m>>x0>>y0; 41 for(int i=1;i<=n;i++) 42 { 43 for(int j=1;j<=n;j++) 44 { 45 cin>>a[i][j]; 46 } 47 } 48 string s; 49 for(int i=0;i<m;i++) 50 { 51 cin>>s; 52 if(s=="FORWARD") c[i]=0; 53 if(s=="BACK") c[i]=1; 54 if(s=="LEFT") c[i]=2; 55 if(s=="RIGHT") c[i]=3; 56 } 57 } 58 59 void cal_pos(int &x,int &y,int &d,int c) 60 { 61 if(c==0) 62 { 63 x+=dx[d]; 64 y+=dy[d]; 65 return; 66 } 67 if(c==1) 68 { 69 x-=dx[d]; 70 y-=dy[d]; 71 return; 72 } 73 if(c==2) 74 { 75 d=(d+3)%4; 76 return; 77 } 78 if(c==3) 79 { 80 d=(d+1)%4; 81 return; 82 } 83 } 84 85 inline bool is_legal(int x,int y) 86 { 87 return x>0 && x<=n && y>0 && y<=n && a[x][y]!='*'; 88 } 89 90 void solve() 91 { 92 memset(dp,0x3f,sizeof(dp)); 93 memset(vis,-1,sizeof(vis)); 94 dp[0][x0][y0][0]=0; 95 vis[0][x0][y0][0]=0; 96 for(int i=0;i<m;i++) 97 { 98 for(int x=1;x<=n;x++) 99 { 100 for(int y=1;y<=n;y++) 101 { 102 if(a[x][y]!='*') 103 { 104 for(int d=0;d<4;d++) 105 { 106 if(vis[i&1][x][y][d]==i) 107 { 108 int temp; 109 if(vis[(i+1)&1][x][y][d]!=i+1) temp=INF; 110 else temp=dp[(i+1)&1][x][y][d]; 111 dp[(i+1)&1][x][y][d]=min(temp,dp[i&1][x][y][d]+1); 112 vis[(i+1)&1][x][y][d]=i+1; 113 int nx=x,ny=y,nd=d; 114 cal_pos(nx,ny,nd,c[i]); 115 if(is_legal(nx,ny)) 116 { 117 if(vis[(i+1)&1][nx][ny][nd]!=i+1) temp=INF; 118 else temp=dp[(i+1)&1][nx][ny][nd]; 119 dp[(i+1)&1][nx][ny][nd]=min(temp,dp[i&1][x][y][d]); 120 vis[(i+1)&1][nx][ny][nd]=i+1; 121 } 122 } 123 } 124 } 125 } 126 } 127 } 128 ans=m; 129 for(int x=1;x<=n;x++) 130 { 131 for(int y=1;y<=n;y++) 132 { 133 if(a[x][y]!='*') 134 { 135 for(int d=0;d<4;d++) 136 { 137 if(vis[m&1][x][y][d]==m) 138 { 139 ans=min(ans,dp[m&1][x][y][d]); 140 } 141 } 142 } 143 } 144 } 145 } 146 147 void print() 148 { 149 cout<<ans<<endl; 150 } 151 152 int main() 153 { 154 read(); 155 solve(); 156 print(); 157 }