ACM 粗心永远AC不了系列——Demo Day|三维DP问题,机器人寻路,hiho一下第150周


题目来源:https://hihocoder.com/contest/hiho150/problem/1

时间限制:10000ms

单点时限: 1000ms
内存限制: 256MB

描述

You work as an intern at a robotics startup. Today is your company's demo day. During the demo your company's robot will be put in a maze and without any information about the maze, it should be able to find a way out.

The maze consists of N * M grids. Each grid is either empty(represented by '.') or blocked by an obstacle(represented by 'b'). The robot will be release at the top left corner and the exit is at the bottom right corner.

Unfortunately some sensors on the robot go crazy just before the demo starts. As a result, the robot can only repeats two operations alternatively: keep moving to the right until it can't and keep moving to the bottom until it can't. At the beginning, the robot keeps moving to the right.

rrrrbb..            
...r....     ====> The robot route with broken sensors is marked by 'r'. 
...rrb..
...bb...

While the FTEs(full-time employees) are busy working on the sensors, you try to save the demo day by rearranging the maze in such a way that even with the broken sensors the robot can reach the exit successfully. You can change a grid from empty to blocked and vice versa. So as not to arouse suspision, you want to change as few grids as possible. What is the mininum number?

输入

Line 1: N, M.

Line 2-N+1: the N * M maze.


For 20% of the data, N * M <= 16.

For 50% of the data, 1 <= N, M <= 8.

For 100% of the data, 1<= N, M <= 100.

输出

The minimum number of grids to be changed.

样例输入
4 8
....bb..
........
.....b..
...bb...
样例输出
1

hiho一下第150周《Demo Day》题目分析

作为一名身经百战的选手,相信这种“从左上到右下,只能向左和向下”的题目大家一定见得多了。这题一眼看去就散发出浓浓的DP气息。

第一个想法就是用f[i][j]表示从左上角到达(i, j)这个位置最少需要改变几个格子。不过仔细一想会发现由于这题限制只能在“撞墙”的时候换方向,所以不知道当前方向的话,是没有办法转移的。

于是我们可以把方向也加入状态中:用f[i][j][d]表示从左上角到达(i, j)这个位置,并且当前移动方向是d(d有两个取值,向上或向下)最少需要改变几个格子。

之后我们需要检视一下有没有后效性。也就是说如果一个格子(是否有障碍)会影响从左上角到(i, j),那么这个格子会不会还影响到从(i, j)到右下角? 简单分析一下,我们就会发现并不会有这样的情况出现。

最后我们要写出转移方程。假设我们现在要求f[i][j][右]的值,显然f[i][j][右]只可能从f[i-1][j][右]、f[i-1][j][下]、f[i][j-1][右]和f[i][j-1][下]转移而来。

我们以f[i-1][j][右]为例。我们到绿色格子(i-1, j)时方向是向右,为了到蓝色格子(i, j)是方向也是向右,我们需要两个黄色格子(i-1,j+1)和(i+1,j)是有障碍物的,同时(i, j)不能有障碍物。

所以此时f[i][j][右] = f[i-1][j][右] + (g[i][j] != 空) + (g[i-1][j+1] != 障碍物) + (g[i+1][j] != 障碍物)。(真是1,假是0)

当然f[i][j][右]还可能从f[i-1][j][下]、f[i][j-1][右]和f[i][j-1][下]转移而来。最终f[i][j][右]是取这四种情况的最小值。

总复杂度是O(NM)的。

注意

这里值得注意的有两个问题,是我在编码时候犯的两个错误:

      1.一个格子既可以添加障碍物,也可以移去障碍物,随意关于移去障碍物的处理,我们要判断g[i][j]是否有障碍物,有的话执行移去操作,即:

dp[i][j][1]=dp[i][j][0]=g[i][j]?1:0;
    2.关于dp[0][0][0]=0,dp[0][0][1]=1;即是机器人初始化是向右的,所以你原点向下就要增加一个消耗
    3.注意边界判断,所以定义了一个MAXN

通过代码
import java.util.Scanner;
import static java.lang.Math.min;

/**
 * @Description :   《Demo Day》,机器人寻路,hiho一下第150周 https://hihocoder.com/contest/hiho150/problem/1
 * Created with IntelliJ IDEA.
 * Created by The_Sam on 2017/5/11 22:52
 */
public class Main {
    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        int N, M, dp[][][];
        boolean g[][];
        int MAXN = 0x3f3f;
        while (cin.hasNext()) {
            N = cin.nextInt();
            M = cin.nextInt();
            g = new boolean[N][M];
            dp = new int[N][M][2];//0右1下
            for (int i = 0; i < N; i++) {
                String sb = cin.next();
                for (int j = 0; j < M; j++) {
                    g[i][j] = sb.charAt(j) != '.';
                }
            }
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < M; j++) {
                    //此处有障碍物,则需要一处这个障碍物
                    dp[i][j][1]=dp[i][j][0]=g[i][j]?1:0;
                    //第一块初始化
                    if (i == 0 && j == 0&& !g[i][j+1]) {//第[i][j+1]块有没有阻塞,刚开始向下的方向要不要++
                        dp[i][j][1]++;
                        continue;
                    }
                    //获取上一步 last+上一步位置+上一步方向+结果方向
                    int lastLR0 = j > 0 ? dp[i][j - 1][0] : MAXN;
                    int lastLR1 = lastLR0;
                    int lastLD0 = j > 0 ? dp[i][j - 1][1] : MAXN;
                    int lastLD1 = lastLD0;
                    int lastUR0 = i > 0 ? dp[i - 1][j][0] : MAXN;
                    int lastUR1 = lastUR0;
                    int lastUD0 = i > 0 ? dp[i - 1][j][1] : MAXN;
                    int lastUD1 = lastUD0;
                    //      j-1| j  |j+1
                    // i-1  @    U   #1
                    //  i   L    T   #2
                    // i+1  #3   #4

                    // #1判断第1种情况判断
                    if (i > 0 && j < M - 1 && !g[i - 1][j + 1]) {
                        lastUR0++;
                        lastUR1++;
                    }
                    // #2判断第2种情况判断
                    if (j < M - 1 && !g[i][j + 1]) {
                        lastLR1++;
                        lastLD1++;
                    }
                    // #3判断第3种情况判断
                    if (i < N - 1 && j > 0 && !g[i + 1][j - 1]) {
                        lastLD0++;
                        lastLD1++;
                    }
                    // #4判断第3种情况判断
                    if (i < N - 1 && !g[i + 1][j]) {
                        lastUR0++;
                        lastUD0++;
                    }
                    dp[i][j][0] += min(min(lastLR0, lastLD0), min(lastUR0, lastUD0));
                    dp[i][j][1] += min(min(lastLR1, lastLD1), min(lastUR1, lastUD1));
                }
            }
            System.out.println(dp[N - 1][M - 1][0]);
        }
    }
}
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值