hihoCoder1290. Demo Day

题目来源: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;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值