题目来源:http://hihocoder.com/problemset/problem/1290
题目大意是,给一个 N*M 的迷宫,输入给出每个位置是否有障碍(用b表示),把机器人放在左上角(0,0)的位置,目标是到达右下角(n-1, m-1)。机器人只能向右或向下行动,碰到障碍就改变方向,初始方向向右,问最少需要改变几个位置的障碍(没有b的可以增加b,有b的可以去掉b)。
本题很容易想到用动态规划的方法去做,大概思路是,到达当前位置maze[i][j],只能从两个地方移动得到:maze[i-1][j]或maze[i][j-1],取两者的较小即可。dp表需要记录到达每个位置所改变的障碍物,需要记录到达每个位置的方向。
表的数据结构如下:struct DP{
int cnt, orient;//改变障碍物计数, 方向(0:either, 1:right, 2:down)
};
对dp[i-1][j](上侧点)进行讨论,如果dp[i-1][j].orient == 1,则需要知道maze[i-1][j+1]是否存在障碍;如果dp[i-1][j].orient == 0 or 2,则 route[0] = dp[i-1][j].cnt.
对dp[i][j-1](左侧点)进行讨论,如果dp[i-1][j].orient== 2,则需要知道maze[i+1][j-1]是否存在障碍;如果dp[i][j-1].orient == 0 or 1,则route[1] = dp[i][j-1].cnt.
dp[i][j].cnt = min(route[0], route[1]),并设置到达dp[i][j]的方向,最后需要查看maze[i][j]是否存在障碍,如果有,dp[i][j].cnt++.
这里要当心数组越界问题,需要初始化dp[0][0] ~ dp[0][m-1]和dp[1][0] ~ dp[n-1][0]的值,以及一个小技巧,在最右侧和最底端加上障碍,具体做法是在申请maze数组空间的时候,申请(N+1)*(M+1)的空间并将这些位置初始化为true(有障碍)。给出AC代码:#include <bits/stdc++.h>
#define REP(i, n) for (int i=0; i<int(n); ++i)
using namespace std;
struct DP{
int cnt, orient;//改变障碍物计数,方向(0:either, 1:right, 2:down)
}*dp;
int main()
{
int n, m;
cin >> n >> m;
bool **maze = new bool*[n+1];
DP **dp = new DP*[n];
REP(i, n+1) maze[i] = new bool[m+1];//(n+1)*(m+1) maze
REP(i, n) dp[i] = new DP[m];
char c;
REP(i, n) {
getchar();
REP(j, m) {
scanf("%c", &c);
maze[i][j] = (c=='.') ? false : true;
}
}
//initialize
dp[0][0].cnt = maze[0][0] ? 1 : 0;
dp[0][0].orient = 1;//right
for(int j = 1; j < m; j++){
dp[0][j].orient = 1;
dp[0][j].cnt = maze[0][j] ? dp[0][j-1].cnt+1 :dp[0][j-1].cnt;
}
dp[1][0].cnt = maze[0][1] ? dp[0][0].cnt+maze[1][0] :dp[0][0].cnt+maze[1][0]+1;
dp[1][0].orient = 2;
for(int i = 2; i < n; i++){
dp[i][0].orient = 2;
dp[i][0].cnt = maze[i][0] ? dp[i-1][0].cnt+1 :dp[i-1][0].cnt;
}
REP(i, n+1) maze[i][m] = true;//最右端和最下端添加阻挡
REP(i, m+1) maze[n][i] = true;
for(int i = 1; i < n; i++)//dp
{
for(int j = 1; j < m; j++)
{
//dp[i][j] look for dp[i-1][j] and dp[i][j-1]
int tmp[2];
switch(dp[i-1][j].orient)
{
case 1://right, 如果有阻挡,肯定向下运动;如果没有阻挡,添加阻挡
tmp[0] = maze[i-1][j+1] ? dp[i-1][j].cnt :dp[i-1][j].cnt+1;break;
case 0://either or down
case 2:tmp[0] = dp[i-1][j].cnt;break;
}
switch(dp[i][j-1].orient)
{
case 2://down
tmp[1] = maze[i+1][j-1] ? dp[i][j-1].cnt : dp[i][j-1].cnt+1;break;
case 0://either or right
case 1:tmp[1] = dp[i][j-1].cnt;break;
}
if(tmp[0] == tmp[1])
dp[i][j].orient = 0;
else if(tmp[0] < tmp[1])
dp[i][j].orient = 2;
else
dp[i][j].orient = 1;
dp[i][j].cnt = min(tmp[0], tmp[1]);
if(maze[i][j]) dp[i][j].cnt++;
}
}
cout<<dp[n-1][m-1].cnt;
return 0;
}