(C卷,100分)- 小华地图寻宝(Java & JS & Python & C)

题目描述

小华按照地图去寻宝,地图上被划分成 m 行和 n 列的方格,横纵坐标范围分别是 [0, n-1] 和 [0, m-1]。

在横坐标和纵坐标的数位之和不大于 k 的方格中存在黄金(每个方格中仅存在一克黄金),但横坐标和纵坐标之和大于 k 的方格存在危险不可进入。小华从入口 (0,0) 进入,任何时候只能向左,右,上,下四个方向移动一格。

请问小华最多能获得多少克黄金?

输入描述

坐标取值范围如下:

  • 0 ≤ m ≤ 50
  • 0 ≤ n ≤ 50

k 的取值范围如下:

  • 0 ≤ k ≤ 100

输入中包含3个字数,分别是m, n, k

输出描述

输出小华最多能获得多少克黄金

用例

输入 40 40 18
输出 1484
说明
输入 5 4 7
输出 20
说明

题目解析

本题题目描述有些歧义

在横坐标和纵坐标的数位之和不大于 k 的方格中存在黄金(每个方格中仅存在一克黄金)

但横坐标和纵坐标之和大于 k 的方格存在危险不可进入

这两句话有点矛盾,所谓横坐标和纵坐标的数位之和,比如:

  • 横坐标是10,那么横坐标的数位和 = 1 + 0 = 1
  • 纵坐标是21,那么纵坐标的数位和 = 2 + 1 = 3
  • 横坐标和纵坐标的数位之和 = 1 + 3 = 4

但是横坐标和纵坐标之和

  • 横坐标是10,纵坐标是21,横坐标和纵坐标之和 = 10 + 21 = 31

和考友确认后,上面“横坐标和纵坐标之和”其实也是数位之和,即

横坐标和纵坐标数位之和大于 k 的方格存在危险不可进入

因此,本题其实就是图的遍历问题,可以使用深度优先搜索DFS,或者广度优先搜索BFS处理。

从(0,0)点开始,然后进行深搜或者广搜,其上下左右四个新位置,新位置是否可以进入的条件是:

  • 新位置不越界
  • 新位置未被访问过
  • 新位置的横坐标、纵坐标的数位之和 <= k

每进入一个位置,则获得黄金1克。

JS算法源码

深度优先搜索
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void (async function () {
  const [m, n, k] = (await readline()).split(" ").map(Number);

  // 记录题解
  let ans = 0;

  // 记录已访问过的位置,避免重复统计
  const visited = new Set();

  // 上下左右偏移量
  const offsets = [
    [-1, 0],
    [1, 0],
    [0, -1],
    [0, 1],
  ];

  // 数位和数组
  const digitSums = digitSum(Math.max(m, n));

  // 深度优先搜索遍历矩阵
  function dfs(x, y) {
    // 如果对应位置越界,或者已访问过,或者横坐标、纵坐标的数位和之和超过了k,则不能进入
    if (
      x < 0 ||
      x >= m ||
      y < 0 ||
      y >= n ||
      visited.has(x * n + y) ||
      digitSums[x] + digitSums[y] > k
    ) {
      return;
    }

    // 否则可以进入,且获得黄金
    visited.add(x * n + y);
    ans++;

    // 继续遍历上、下、左、右方向上的新位置继续深搜
    for (let [offsetX, offsetY] of offsets) {
      const newX = x + offsetX;
      const newY = y + offsetY;

      dfs(newX, newY);
    }
  }

  dfs(0, 0);

  console.log(ans);
})();

// 该方法用于求解 0 ~ maxSize - 1 各个数对应的数位和,提前计算好,避免后期重复计算某个数的数位和
function digitSum(maxSize) {
  // digitSums数组的索引是原始数,值是原始数对应的数位和
  const res = new Array(maxSize).fill(0);

  for (let i = 0; i < maxSize; i++) {
    let num = i;
    while (num > 0) {
      res[i] += num % 10;
      num = Math.floor(num / 10);
    }
  }

  return res;
}
广度优先搜索
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void (async function () {
  const [m, n, k] = (await readline()).split(" ").map(Number);

  // 记录已访问过的位置,避免重复统计
  const visited = new Set();

  // 上下左右偏移量
  const offsets = [
    [-1, 0],
    [1, 0],
    [0, -1],
    [0, 1],
  ];

  // 数位和数组
  const digitSums = digitSum(Math.max(m, n));

  function bfs() {
    const queue = [];
    queue.push(0);

    // 由于k>=0,因此出发点(0,0)肯定可以访问,且有黄金
    let ans = 1;
    visited.add(0);

    while (queue.length > 0) {
      const pos = queue.shift();

      const y = pos % n;
      const x = (pos - y) / n;

      // 遍历当前位置的上下左右四个方向的新位置
      for (let [offsetX, offsetY] of offsets) {
        const newX = x + offsetX;
        const newY = y + offsetY;

        // 新位置越界,则无法访问
        if (newX < 0 || newX >= m || newY < 0 || newY >= n) continue;

        // 新位置的横坐标、纵坐标数位和之和超过k,则无法访问
        if (digitSums[newX] + digitSums[newY] > k) continue;

        // 新位置已访问过,则不能再访问
        const newPos = newX * n + newY;
        if (visited.has(newPos)) continue;

        // 否则,可以进入新位置,且获得黄金
        ans++;
        visited.add(newPos);
        queue.push(newPos);
      }
    }

    return ans;
  }

  if (m == 0 || n == 0) {
    console.log(0);
  } else {
    console.log(bfs());
  }
})();

// 该方法用于求解 0 ~ maxSize - 1 各个数对应的数位和,提前计算好,避免后期重复计算某个数的数位和
function digitSum(maxSize) {
  // digitSums数组的索引是原始数,值是原始数对应的数位和
  const res = new Array(maxSize).fill(0);

  for (let i = 0; i < maxSize; i++) {
    let num = i;
    while (num > 0) {
      res[i] += num % 10;
      num = Math.floor(num / 10);
    }
  }

  return res;
}

Java算法源码

深度优先搜索
import java.util.HashSet;
import java.util.Scanner;

public class Main {
  static int m;
  static int n;
  static int k;

  // 记录题解
  static int ans = 0;

  // 记录已访问过的位置,避免重复统计
  static HashSet<Integer> visited = new HashSet<>();

  // 上下左右偏移量
  static int[][] offsets = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

  // 数位和数组
  static int[] digitSums;

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    m = sc.nextInt();
    n = sc.nextInt();
    k = sc.nextInt();

    digitSum(Math.max(m, n));

    dfs(0, 0);

    System.out.println(ans);
  }

  // 深度优先搜索遍历矩阵
  public static void dfs(int x, int y) {
    // 如果对应位置越界,或者已访问过,或者横坐标、纵坐标的数位和之和超过了k,则不能进入
    if (x < 0
        || x >= m
        || y < 0
        || y >= n
        || visited.contains(x * n + y)
        || digitSums[x] + digitSums[y] > k) return;

    // 否则可以进入,且获得黄金
    visited.add(x * n + y);
    ans++;

    // 继续遍历上、下、左、右方向上的新位置继续深搜
    for (int[] offset : offsets) {
      int newX = x + offset[0];
      int newY = y + offset[1];

      dfs(newX, newY);
    }
  }

  // 该方法用于求解 0 ~ maxSize - 1 各个数对应的数位和,提前计算好,避免后期重复计算某个数的数位和
  public static void digitSum(int maxSize) {
    // digitSums数组的索引是原始数,值是原始数对应的数位和
    digitSums = new int[maxSize];

    for (int i = 0; i < maxSize; i++) {
      int num = i;
      while (num > 0) {
        digitSums[i] += num % 10;
        num /= 10;
      }
    }
  }
}
广度优先搜索
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Scanner;

public class Main {
  static int m;
  static int n;
  static int k;

  // 记录已访问过的位置,避免重复统计
  static HashSet<Integer> visited = new HashSet<>();

  // 上下左右偏移量
  static int[][] offsets = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

  // 数位和数组
  static int[] digitSums;

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    m = sc.nextInt();
    n = sc.nextInt();
    k = sc.nextInt();

    digitSum(Math.max(m, n));

    if (m == 0 || n == 0) {
      System.out.println(0);
    } else {
      System.out.println(bfs());
    }
  }

  public static int bfs() {
    LinkedList<Integer> queue = new LinkedList<>();
    queue.add(0);

    // 由于k>=0,因此出发点(0,0)肯定可以访问,且有黄金
    int ans = 1;
    visited.add(0);

    while (queue.size() > 0) {
      int pos = queue.removeFirst();

      int x = pos / n;
      int y = pos % n;

      // 遍历当前位置的上下左右四个方向的新位置
      for (int[] offset : offsets) {
        int newX = x + offset[0];
        int newY = y + offset[1];

        // 新位置越界,则无法访问
        if (newX < 0 || newX >= m || newY < 0 || newY >= n) continue;

        // 新位置的横坐标、纵坐标数位和之和超过k,则无法访问
        if (digitSums[newX] + digitSums[newY] > k) continue;

        // 新位置已访问过,则不能再访问
        int newPos = newX * n + newY;
        if (visited.contains(newPos)) continue;

        // 否则,可以进入新位置,且获得黄金
        ans++;
        visited.add(newPos);
        queue.addLast(newPos);
      }
    }

    return ans;
  }

  // 该方法用于求解 0 ~ maxSize - 1 各个数对应的数位和,提前计算好,避免后期重复计算某个数的数位和
  public static void digitSum(int maxSize) {
    // digitSums数组的索引是原始数,值是原始数对应的数位和
    digitSums = new int[maxSize];

    for (int i = 0; i < maxSize; i++) {
      int num = i;
      while (num > 0) {
        digitSums[i] += num % 10;
        num /= 10;
      }
    }
  }
}

Python算法源码

深度优先搜索
import sys

# 设置递归深度
sys.setrecursionlimit(5000)

# 输入获取
m, n, k = map(int, input().split())

# 全局变量
ans = 0  # 记录题解
visited = set()  # 记录已访问过的位置,避免重复统计
offsets = ((-1, 0), (1, 0), (0, -1), (0, 1))  # 上下左右偏移量

digitSums = [0] * (max(m, n))  # digitSums数组的索引是原始数,值是原始数对应的数位和
for i in range(len(digitSums)):
    num = i
    while num > 0:
        digitSums[i] += num % 10
        num //= 10


# 深度优先搜索遍历矩阵
def dfs(x, y):
    # 如果对应位置越界,则不能进入
    if x < 0 or x >= m or y < 0 or y >= n:
        return

    #  如果对应位置已横坐标、纵坐标的数位和之和超过了k,则不能进入
    if digitSums[x] + digitSums[y] > k:
        return

    pos = x * n + y

    # 如果对应位置已访问过,则不能进入
    if pos in visited:
        return

    # 否则可以进入
    visited.add(pos)

    # 且获得黄金
    global ans
    ans += 1

    # 继续遍历上、下、左、右方向上的新位置继续深搜
    for offsetX, offsetY in offsets:
        newX = x + offsetX
        newY = y + offsetY
        dfs(newX, newY)


# 算法入口
dfs(0, 0)
print(ans)
广度优先搜索
# 输入获取
m, n, k = map(int, input().split())

# 全局变量
visited = set()  # 记录已访问过的位置,避免重复统计
offsets = ((-1, 0), (1, 0), (0, -1), (0, 1))  # 上下左右偏移量

digitSums = [0] * (max(m, n))  # digitSums数组的索引是原始数,值是原始数对应的数位和
for i in range(len(digitSums)):
    num = i
    while num > 0:
        digitSums[i] += num % 10
        num //= 10


# 广度优先搜索
def bfs():
    queue = [0]

    # 由于k>=0,因此出发点(0,0)肯定可以访问,且有黄金
    ans = 1
    visited.add(0)

    while len(queue) > 0:
        pos = queue.pop(0)

        x = pos // n
        y = pos % n

        # 遍历当前位置的上下左右四个方向的新位置
        for offsetX, offsetY in offsets:
            newX = x + offsetX
            newY = y + offsetY

            # 新位置越界,则无法访问
            if newX < 0 or newX >= m or newY < 0 or newY >= n:
                continue

            #  新位置的横坐标、纵坐标数位和之和超过k,则无法访问
            if digitSums[newX] + digitSums[newY] > k:
                continue

            # 新位置已访问过,则不能再访问
            newPos = newX * n + newY
            if newPos in visited:
                continue

            # 否则可以进入
            ans += 1
            visited.add(newPos)
            queue.append(newPos)

    return ans


# 算法入口
if m == 0 or n == 0:
    print("0")
else:
    print(bfs())

C算法源码

深度优先搜索
#include <stdio.h>
#include <stdlib.h>

#define MAX(a, b) ((a) > (b) ? (a) : (b))

int m, n, k;

// 记录题解
int ans = 0;

// 记录已访问过的位置,避免重复统计
int *visited;

// 上下左右偏移量
int offsets[4][2] = {{-1, 0},
                     {1,  0},
                     {0,  -1},
                     {0,  1}};

// 数位和数组
int *digitSums;

void dfs(int x, int y) {
    // 如果对应位置越界,则不能进入
    if (x < 0 || x >= m || y < 0 || y >= n) {
        return;
    }

    // 如果对应位置已横坐标、纵坐标的数位和之和超过了k,则不能进入
    if (digitSums[x] + digitSums[y] > k) {
        return;
    }

    // 如果对应位置已访问过,则不能进入
    int pos = x * n + y;
    if (visited[pos]) {
        return;
    }

    // 否则可以进入
    visited[pos] = 1;
    // 且获得黄金
    ans += 1;

    // 继续遍历上、下、左、右方向上的新位置继续深搜
    for (int i = 0; i < 4; i++) {
        int newX = x + offsets[i][0];
        int newY = y + offsets[i][1];
        dfs(newX, newY);
    }
}

int main() {
    scanf("%d %d %d", &m, &n, &k);

    // digitSums数组的索引是原始数,值是原始数对应的数位和
    int maxSize = MAX(m, n);
    digitSums = (int *) calloc(maxSize, sizeof(int));
    for (int i = 0; i < maxSize; i++) {
        int num = i;
        while (num > 0) {
            digitSums[i] += num % 10;
            num /= 10;
        }
    }

    // visited数组记录已访问过的位置,避免重复统计
    visited = (int *) calloc(m * n, sizeof(int));

    dfs(0, 0);
    printf("%dn", ans);

    return 0;
}
广度优先搜索
#include <stdio.h>
#include <stdlib.h>

#define MAX(a, b) ((a) > (b) ? (a) : (b))

typedef struct ListNode {
    int ele;
    struct ListNode *next;
} ListNode;

typedef struct LinkedList {
    int size;
    ListNode *head;
    ListNode *tail;
} LinkedList;

LinkedList *new_LinkedList() {
    LinkedList *link = (LinkedList *) malloc(sizeof(LinkedList));
    link->size = 0;
    link->head = NULL;
    link->tail = NULL;
    return link;
}

void addLast_LinkedList(LinkedList *link, int ele) {
    ListNode *node = (ListNode *) malloc(sizeof(ListNode));
    node->ele = ele;
    node->next = NULL;

    if (link->size == 0) {
        link->head = node;
        link->tail = node;
    } else {
        link->tail->next = node;
        link->tail = node;
    }

    link->size++;
}

int removeFirst_LinkedList(LinkedList *link) {
    if (link->size == 0) exit(-1);

    ListNode *removed = link->head;

    if (link->size == 1) {
        link->head = NULL;
        link->tail = NULL;
    } else {
        link->head = link->head->next;
    }

    link->size--;

    int res = removed->ele;

    free(removed);

    return res;
}

int m, n, k;

// 记录已访问过的位置,避免重复统计
int *visited;

// 上下左右偏移量
int offsets[4][2] = {{-1, 0},
                     {1,  0},
                     {0,  -1},
                     {0,  1}};

// 数位和数组
int *digitSums;

int bfs() {
    LinkedList *queue = new_LinkedList();
    addLast_LinkedList(queue, 0);

    // 由于k>=0,因此出发点(0,0)肯定可以访问,且有黄金
    int ans = 1;
    visited[0] = 1;

    while (queue->size > 0) {
        int pos = removeFirst_LinkedList(queue);

        int x = pos / n;
        int y = pos % n;

        // 遍历当前位置的上下左右四个方向的新位置
        for (int i = 0; i < 4; i++) {
            int newX = x + offsets[i][0];
            int newY = y + offsets[i][1];

            // 新位置越界,则无法访问
            if (newX < 0 || newX >= m || newY < 0 || newY >= n) continue;

            // 新位置的横坐标、纵坐标数位和之和超过k,则无法访问
            if (digitSums[newX] + digitSums[newY] > k) continue;

            // 新位置已访问过,则不能再访问
            int newPos = newX * n + newY;
            if (visited[newPos]) continue;

            // 否则,可以进入新位置,且获得黄金
            ans++;
            visited[newPos] = 1;
            addLast_LinkedList(queue, newPos);
        }
    }

    return ans;
}

int main() {
    scanf("%d %d %d", &m, &n, &k);

    // digitSums数组的索引是原始数,值是原始数对应的数位和
    int maxSize = MAX(m, n);
    digitSums = (int *) calloc(maxSize, sizeof(int));
    for (int i = 0; i < maxSize; i++) {
        int num = i;
        while (num > 0) {
            digitSums[i] += num % 10;
            num /= 10;
        }
    }

    // visited数组记录已访问过的位置,避免重复统计
    visited = (int *) calloc(m * n, sizeof(int));

    if(m == 0 || n == 0) {
        puts("0");
    } else {
        printf("%dn", bfs());
    }

    return 0;
}

免责声明:

1、IT资源小站为非营利性网站,全站所有资料仅供网友个人学习使用,禁止商用
2、本站所有文档、视频、书籍等资料均由网友分享,本站只负责收集不承担任何技术及版权问题
3、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除下载链接并致以最深的歉意
4、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
5、一经注册为本站会员,一律视为同意网站规定,本站管理员及版主有权禁止违规用户
6、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和IT资源小站的同意
7、IT资源小站管理员和版主有权不事先通知发贴者而删除本文

0

评论0

站点公告

没有账号?注册  忘记密码?