您的位置:首页 > 其它

nyoj-306-走迷宫--二分法+dfs

2015-08-21 14:28 330 查看

nyoj-306-走迷宫–二分法+dfs

题目 走迷宫

限制

1000ms | 65535kb

描述

Dr.Kong设计的机器人卡多非常爱玩,它常常偷偷跑出实验室,在某个游乐场玩之不疲。这天卡多又跑出来了,在SJTL游乐场玩个不停,坐完碰碰车,又玩滑滑梯,这时卡多又走入一个迷宫。整个迷宫是用一个N * N的方阵给出,方阵中单元格中填充了一个整数,表示走到这个位置的难度。

这个迷宫可以向上走,向下走,向右走,向左走,但是不能穿越对角线。走迷宫的取胜规则很有意思,看谁能更快地找到一条路径,其路径上单元格最大难度值与最小难度值之差是最小的。当然了,或许这样的路径不是最短路径。

机器人卡多现在在迷宫的左上角(第一行,第一列)而出口在迷宫的右下角(第N行,第N列)。

卡多很聪明,很快就找到了这样的一条路径。你能找到吗?

输入

有多组测试数据,以EOF为输入结束的标志

第一行: N 表示迷宫是N*N方阵 (2≤ N≤ 100)

接下来有N行, 每一行包含N个整数,用来表示每个单元格中难度 (0≤任意难度≤120)。

输出

输出为一个整数,表示路径上最高难度与和最低难度的差。

样例输入

5
1 1 3 6 8
1 2 2 5 5
4 4 0 3 3
8 0 2 3 4
4 3 0 2 1


样例输出

2


分析

题意是要为仅能上、下、左、右行动的机器找出一条从左上角到右下角的路径,并且这条路径上的最大值和最小值之差要最小。

看到题目,先是用dfs直接实现,果不其然TLE。又是看了几遍题目后发现,难度的区间已给出:[0, 120],若用二分的话感觉会优化不少时间,但是应该也要几百ms。以防万一我去查了一下其他人的运行时间发现大都只有不到100ms,以为算法错了。浪费了好多时间和本来就不多的脑细胞,实在想不出其他较好的方法,就上网查了一下,发现竟然都是二分+dfs,于是敲码后提交,果然只有16ms…

可能本题的数据比较水,但还是应该“实践出真知”

二分法需要细心设计一下,附上代码:

/*
* nyoj. 306
* date: 2015.8.21
* 16sm 656kb
*/
#include <iostream>
#include <cstdio>
#include <memory.h>
#define max(a,b) a>b ? a : b
#define min(a,b) a<b ? a : b
using namespace std;
const int INF = 100000000;
const int MAXL = 110;
const int direction[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
int arr[MAXL][MAXL], visited[MAXL][MAXL];
int N
, tempmindif, tempmaxdif      // 当前根据二分法确定的最小值、最
, mindif, maxdif;             // 大值整个输入的最小值、最大值

bool inscale(int x, int y) {    //  判断点坐标(x, y)是否在当前确定的最大值、最小值之间
return arr[x][y] >= tempmindif
&& arr[x][y] <= tempmaxdif;
}

bool dfs(int x, int y) {
if (x == N && y == N) return true;

int nearx, neary;
for (int i = 0; i < 4; ++i) {
nearx = x + direction[i][0];
neary = y + direction[i][1];

if (!visited[nearx][neary] && inscale(nearx, neary)) {
visited[nearx][neary] = true;
if (dfs(nearx, neary))
return true;
}
}
return false;
}

bool findpath(int key) {
for (int i = mindif; i <= maxdif - key; ++i) {
tempmindif = i, tempmaxdif = i+key;
if (!inscale(1, 1) || !inscale(N, N))   // 如果起始点或终止点不在当前最小值、最大值之
continue;                           // 间,那这个当前最小值、最大值肯定是错误的

memset(visited, false, sizeof(visited));
visited[1][1] = true;
if (dfs(1, 1))
return true;
}
return false;
}

int main() {
memset(arr, -1, sizeof(arr));               //赋值成-1,可省去dfs时边界的判定
while (~scanf("%d", &N)) {
mindif = INF, maxdif = 0;
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
scanf("%d", *(arr+i)+j);
maxdif = max(maxdif, arr[i][j]);
mindif = min(mindif, arr[i][j]);
}
}

int bsmid, bsleft = 0, bsright = maxdif - mindif;
while (bsleft <= bsright) {             //二分法的设计需要仔细考虑
bsmid = (bsleft + bsright) >> 1;
if (findpath(bsmid))
bsright = bsmid - 1;
else
bsleft  = bsmid + 1;
}
printf("%d\n", bsleft);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  二分法