《算法竞赛·快冲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)
相关推荐
sweetheart7-711 小时前
LeetCode17. 电话号码的字母组合(2024秋季每日一题 59)
算法·深度优先·力扣·dfs
£suPerpanda2 天前
牛客周赛 Round65 补题DEF
开发语言·数据结构·c++·算法·深度优先·动态规划·图论
飞滕人生TYF2 天前
判断图中是否存在环
算法·深度优先
姜西西_3 天前
穷举vs暴搜vs深搜vs回溯vs剪枝 算法专题
算法·深度优先·剪枝
c沫栀3 天前
E-小H学历史(牛客练习赛131)
c++·算法·深度优先·图论
新手程序员大猫5 天前
洛谷(DFS)-P2089 烤鸡详解
算法·深度优先
梅见十柒7 天前
数据结构与算法分析:你真的理解图算法吗——深度优先搜索(代码详解+万字长文)
c语言·c++·经验分享·笔记·算法·深度优先·图搜索算法
玉树临风ives7 天前
2024 CSP-J 题解
c++·算法·深度优先·动态规划
姜西西_8 天前
递归 算法专题
算法·深度优先
X BoHang9 天前
2022 NOIP 题解
算法·深度优先·图论