《算法竞赛·快冲300题》每日一题:“游泳”

算法竞赛·快冲300题 》将于2024年出版,是《算法竞赛》的辅助练习册。

所有题目放在自建的OJ New Online Judge

用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。

文章目录

" 游泳 " ,链接: http://oj.ecustacm.cn/problem.php?id=1753

题目描述

【题目描述】 游泳池可以等分为n行n列的小区域,每个区域的温度不同。

小明现在在要从游泳池的左上角(1, 1)游到右下角(n, n),小明只能向上下左右四个方向游,不能游出泳池。

而小明对温度十分敏感,他希望你帮他找一条最舒适的路径,使路径上的最高的水温和最低的水温差值最小。
【输入格式】 第一行输入一个正整数n。接下来n行,每行n个正整数,表示方阵每个区域的温度a[i][j]。所有数据保证随机。(1≤n≤100,1≤a[i][j]≤1000)。
【输出格式】

一行一个数表示最小差值。
【输入样例】

c 复制代码
4
1 3 10 8
1 4 10 8
1 1 1 1
1 5 8 8

【输出样例】

c 复制代码
7

题解

在图上找一条路径,如果是最短路径,用BFS最好;如果用DFS,会导致搜索大量路径,必须剪枝才能减少搜索。如果不是最短路径,用DFS更好编码更简单,注意也需要剪枝。

本题要求找一条温差最小的路径,如果简单地遍历出所有路径,再比较得到温差最小路径,肯定超时,因为总路径数量是 O ( 4 n ) O(4^n) O(4n)的。必须剪枝才能减少路径的搜索数量。

如何剪枝?如果已知最小温差,只要一边游一边检查当前路径上的最大温差,如果已经超过了允许的最小温差,就不用走下去了。但是最小温差不能预知,只能猜,最好的方法是使用二分法来猜这个最小温差。本题的解法是"DFS + 二分法"。 用"BFS+二分法"也行,请读者思考。
【重点】 DFS+二分法。

C++代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n;
int a[N][N];           //温度
bool vis[N][N];        //是否已经访问过
int dx[4] = {1,-1,0,0}, dy[4] = {0,0,1,-1};  //上下左右四个方向
void dfs(int x,int y,int t_max,int t_min){
    if(a[x][y] > t_max || a[x][y] < t_min) return;  //剪枝
    vis[x][y] = true;
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i];
        int ny = y + dy[i];
        if(!vis[nx][ny] && nx >= 1 && nx <= n && ny >= 1 && ny <= n)
            dfs(nx,ny,t_max,t_min);
    }
}
bool check(int x){
    for(int i = 1; i + x <= 1000; i++){
        memset(vis,0,sizeof vis);
        dfs(1,1,i + x,i);
        if(vis[n][n]) return 1;
    }
    return 0;
}
int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            scanf("%d",&a[i][j]);
    int left = 0,right = 1000;
    while(left+1 < right){
        int mid = (left + right)/2;
        if(check(mid)) right = mid;
        else left = mid;
    }
    printf("%d",right);
    return 0;
}

Java代码

java 复制代码
import java.util.*;

public class Main {
    static final int N = 110;
    static int n;
    static int[][] a = new int[N][N]; // 温度
    static boolean[][] vis = new boolean[N][N]; // 是否已经访问过
    static int[] dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1}; // 上下左右四个方向
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        n = scan.nextInt();
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                a[i][j] = scan.nextInt();
        int left = 0, right = 1000;
        while (left + 1 < right) {
            int mid = (left + right) / 2;
            if (check(mid))  right = mid;
            else             left = mid;
        }
        System.out.println(right);
        scan.close();
    }
    static boolean check(int x) {
        for (int i = 1; i + x <= 1000; i++) {
            memset(vis, false);
            dfs(1, 1, i + x, i);
            if (vis[n][n])  return true;
        }
        return false;
    }
    static void dfs(int x, int y, int t_max, int t_min) {
        if (a[x][y] > t_max || a[x][y] < t_min)  return; // 剪枝
        vis[x][y] = true;
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (!vis[nx][ny] && nx >= 1 && nx <= n && ny >= 1 && ny <= n)
                dfs(nx, ny, t_max, t_min);
        }
    }
    static void memset(boolean[][] arr, boolean val) {
        for (int i = 0; i < arr.length; i++)
            Arrays.fill(arr[i], val);
    }
}

Python代码

python 复制代码
import sys
sys.setrecursionlimit(1000000)
N = 110
n = int(input())
a = [[0]*N for _ in range(N)]  # 温度
vis = [[False]*(N) for _ in range(N)]  # 是否已经访问过
dx, dy = [1, -1, 0, 0], [0, 0, 1, -1]  # 上下左右四个方向
def dfs(x, y, t_max, t_min):
    if a[x][y] > t_max or a[x][y] < t_min:  return  # 剪枝
    vis[x][y] = True
    for i in range(4):
        nx = x + dx[i]
        ny = y + dy[i]
        if (not vis[nx][ny]) and 1 <= nx <= n and 1 <= ny <= n:
            dfs(nx, ny, t_max, t_min)
def check(x):
    for i in range(1, 1000 - x + 1):
        global vis
        vis = [[False]*N for _ in range(N)]
        dfs(1, 1, i + x, i)
        if vis[n][n]:  return True
    return False
for i in range(1,n+1):
    a[i] = [0] + list(map(int, input().split()))
print(a[n][n])
left, right = 0, 1000
while left + 1 < right:
    mid = (left + right) // 2
    if check(mid):  right = mid
    else:        left = mid
print(right)
相关推荐
鱼跃鹰飞2 小时前
Leetcode面试经典150题-130.被围绕的区域
java·算法·leetcode·面试·职场和发展·深度优先
山顶夕景19 小时前
【Leetcode152】分割回文串(回溯 | 递归)
算法·深度优先·回溯
WenGyyyL1 天前
力扣最热一百题——二叉树的直径
java·c++·算法·二叉树·深度优先
人才程序员2 天前
CSP-J 算法基础 广度优先搜索BFS
数据结构·c++·算法·深度优先·宽度优先·比赛·noi
joker_zh533 天前
DFS之剪枝与优化
深度优先·dfs的剪枝与优化
arin8763 天前
【组合】矩阵ksm+状压dp
算法·矩阵·深度优先
EllinY4 天前
CF 231 E Cactus 题解(仙人掌图上找环)
c++·笔记·算法·深度优先·图论
joker_zh534 天前
846. 树的重心
算法·深度优先·树的重心
我明天再来学Web渗透5 天前
【hot100-java】【对称二叉树】
java·开发语言·数据结构·算法·leetcode·链表·深度优先
MIMO. mimo8 天前
深度优先算法,广度优先算法,hill climbing,贪心搜索,A*算法,启发式搜索算法是什么,比起一般搜索法算法有什么区别
算法·深度优先·宽度优先