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

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值