題目連結: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 }