题目描述
给定一个矩阵,包含 N * M 个整数,和一个包含 K 个整数的数组。
现在要求在这个矩阵中找一个宽度最小的子矩阵,要求子矩阵包含数组中所有的整数。
输入描述
第一行输入两个正整数 N,M,表示矩阵大小。
接下来 N 行 M 列表示矩阵内容。
下一行包含一个正整数 K。
下一行包含 K 个整数,表示所需包含的数组,K 个整数可能存在重复数字。
所有输入数据小于1000。
输出描述
输出包含一个整数,表示满足要求子矩阵的最小宽度,若找不到,输出-1。
用例
输入 | 2 5 1 2 2 3 1 2 3 2 3 2 3 1 2 3 |
---|---|
输出 | 2 |
说明 | 矩阵第0、3列包含了1,2,3,矩阵第3,4列包含了1,2,3 |
输入 | 2 5 1 2 2 3 1 1 3 2 3 4 3 1 1 4 |
---|---|
输出 | 5 |
说明 | 矩阵第1、2、3、4、5列包含了1、1、4 |
题目解析
1.首先,我们需要将给定的矩阵转换为一个二维数组,以便后续操作。
2.然后,我们需要找到包含所需整数的子矩阵。为了实现这一点,我们可以使用滑动窗口的方法。我们从矩阵的第一列开始,逐步向右移动窗口,直到找到一个包含所有所需整数的子矩阵。在每次移动窗口时,我们需要检查当前窗口是否包含所有所需整数。如果包含,则更新最小宽度;如果不包含,则继续向右移动窗口。
3.最后,输出满足要求的子矩阵的最小宽度。如果没有找到满足要求的子矩阵,则输出-1。
JS算法源码
const rl = require("readline").createInterface({ input: process.stdin });
const iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
(async function () {
const [n, m] = (await readline()).split(" ").map(Number);
const matrix = [];
for (let i = 0; i < n; i++) {
matrix.push((await readline()).split(" ").map(Number));
}
const k = parseInt(await readline());
const nums = (await readline()).split(" ").map(Number);
const cnts = new Array(1000).fill(0);
for (let num of nums) {
cnts[num]++;
}
function getResult() {
let total = k;
let minLen = Infinity;
let l = 0;
let r = 0;
while (r < m) {
for (let i = 0; i < n; i++) {
const num = matrix[i][r];
if (cnts[num]-- > 0) {
total--;
}
}
while (total == 0) {
minLen = Math.min(minLen, r - l + 1);
for (let i = 0; i < n; i++) {
const num = matrix[i][l];
if (cnts[num]++ >= 0) {
total++;
}
}
l++;
}
r++;
}
return minLen === Infinity ? -1 : minLen;
}
console.log(getResult());
})();
Java算法源码
import java.util.Scanner;
public class Main {
static int n;
static int m;
static int[][] matrix;
static int k;
static int[] cnts;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
matrix = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
matrix[i][j] = sc.nextInt();
}
}
k = sc.nextInt();
cnts = new int[1000];
for (int i = 0; i < k; i++) {
int num = sc.nextInt();
cnts[num]++;
}
System.out.println(getResult());
}
public static int getResult() {
int total = k;
int minLen = Integer.MAX_VALUE;
int l = 0;
int r = 0;
while (r < m) {
for (int i = 0; i < n; i++) {
int num = matrix[i][r];
if (cnts[num]-- > 0) {
total--;
}
}
while (total == 0) {
minLen = Math.min(minLen, r - l + 1);
for (int i = 0; i < n; i++) {
int num = matrix[i][l];
if (cnts[num]++ >= 0) {
total++;
}
}
l++;
}
r++;
}
if (minLen == Integer.MAX_VALUE) {
return -1;
} else {
return minLen;
}
}
}
Python算法源码
import sys
from collections import Counter
def count_numbers(nums):
return Counter(nums)
def get_result(matrix, k, nums):
total = k
min_len = sys.maxsize
left = 0
right = 0
while right < len(matrix[0]):
for row in matrix:
num_right = row[right]
if nums[num_right] > 0:
total -= 1
nums[num_right] -= 1
while total == 0:
min_len = min(min_len, right - left + 1)
for row in matrix:
num_left = row[left]
if nums[num_left] >= 0:
total += 1
nums[num_left] += 1
left += 1
right += 1
if min_len == sys.maxsize:
return -1
else:
return min_len
n, m = map(int, input().split())
matrix = [list(map(int, input().split())) for _ in range(n)]
k = int(input())
nums = list(map(int, input().split()))
counted_nums = count_numbers(nums)
result = get_result(matrix, k, counted_nums)
print(result)
C算法源码
#include <stdio.h>
#include <limits.h>
#include <math.h>
#define MAX_SIZE 1000
int n, m;
int matrix[MAX_SIZE][MAX_SIZE];
int k;
int cnts[MAX_SIZE] = {0};
int getResult() {
int total = k;
int minLen = INT_MAX;
int l = 0;
int r = 0;
while (r < m) {
for (int i = 0; i < n; i++) {
int num = matrix[i][r];
if (cnts[num]-- > 0) {
total--;
}
}
while (total == 0) {
minLen = (int) fmin(minLen, r - l + 1);
for (int i = 0; i < n; i++) {
int num = matrix[i][l];
if (cnts[num]++ >= 0) {
total++;
}
}
l++;
}
r++;
}
if (minLen == INT_MAX) {
return -1;
} else {
return minLen;
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &matrix[i][j]);
}
}
scanf("%d", &k);
for (int i = 0; i < k; i++) {
int num;
scanf("%d", &num);
cnts[num]++;
}
printf("%d\n", getResult());
return 0;
}
解题思路如下:
- 输入处理:
- 首先,程序读取矩阵的行数
n
和列数m
。 - 接着,程序会读取
n
行输入,每行包含m
个数字,用以构造矩阵matrix
。 - 然后,读取一个整数
k
,表示需要在子矩阵中找到的数字种类数。 - 最后,读取一系列数字,这些数字是需要在子矩阵中查找的数字。
- 统计数字:
- 使用
Counter
函数统计输入数字nums
中每个数字的出现次数,得到counted_nums
。
- 查找最小子矩阵:
- 初始化
total
为k
,表示当前子矩阵中还需要找到的数字种类数量。 - 初始化
min_len
为最大整数,用于记录找到的最小子矩阵的宽度。 - 使用双指针技巧(滑动窗口),
left
和right
分别指向子矩阵的左右边界。 - 遍历矩阵的每一列(通过
right
指针),在每列中检查每一行的数字,并更新total
和counted_nums
。 - 当
total
变为0时,表示当前子矩阵包含了所有需要找的数字种类,此时尝试收缩左边界(通过left
指针),并更新min_len
。 - 继续移动右边界,直到遍历完所有列。
- 输出结果:
- 如果
min_len
仍然是初始化的最大整数值,表示没有找到包含所有需要数字种类的子矩阵,返回-1。 - 否则,返回
min_len
,即包含所有需要数字种类的最小子矩阵的宽度。
需要注意的是,这个算法假设输入的矩阵matrix
的每一行都具有相同的长度,且nums
中的数字都是矩阵中可能出现的数字。算法的核心是使用滑动窗口技巧,在列方向上移动左右边界,以找到包含所有需要数字种类的最小子矩阵。
另外,程序中有一个小错误需要指出:在get_result
函数中,当移动左边界时,应该检查nums[num_left]
是否大于0,而不是大于等于0,因为当nums[num_left]
等于0时,表示该数字在当前子矩阵中的数量已经满足要求,不需要再增加。因此,应该将下面的代码:
python复制代码
if nums[num_left] >= 0: |
修改为:
python复制代码
if nums[num_left] > 0: |
这样修改后,程序将能更准确地找到包含所有需要数字种类的最小子矩阵。
不过,由于你的程序逻辑中,counted_nums
存储的是nums
中每个数字的出现次数,而nums
数组本身并未在get_result
函数中使用,所以上述错误在实际执行中可能并不会导致问题。但为了代码的准确性和可读性,建议进行相应的修改。
最后,你的程序是寻找一个子矩阵,该子矩阵在列方向上包含所有指定的数字至少一次,且子矩阵的列数最少。这是一个典型的滑动窗口问题,通过动态地调整子矩阵的左右边界来找到最优解。
作为互联网求职大人和大厂的专业老师,我有以下几点建议给准备参加校招的年轻人:
清晰定位自己的职业方向:在参加校招之前,首先要明确自己的兴趣和擅长的领域,以及未来希望从事的工作方向。这样,在校招过程中可以更有针对性地选择适合自己的岗位和公司。
充分准备简历和面试:简历是求职者的第一张名片,一定要认真制作,突出自己的优势和特点。同时,面试是求职过程中非常关键的一环,需要提前了解公司背景和岗位要求,做好充分的准备。
关注行业动态和技术趋势:互联网行业变化迅速,了解最新的行业动态和技术趋势对于求职者来说非常重要。这不仅可以帮助你更好地选择适合自己的岗位,还可以在面试中展示出你对行业的了解和热情。
积极参与实习和项目经验:在校期间,尽可能多地参与实习和项目经验,这不仅可以提升自己的实践能力,还能在校招时作为自己的亮点呈现。
注意网络素养和社交礼仪:在互联网行业,网络素养和社交礼仪尤为重要。在参加校招的过程中,无论是线上交流还是线下面试,都要注意自己的言行举止,给人留下良好的第一印象。
保持积极心态和耐心:校招过程可能会遇到各种挑战和困难,但要保持积极的心态和足够的耐心。相信自己的能力和潜力,坚持不懈地努力,最终一定会找到适合自己的工作机会。
提前了解公司文化和价值观:在参加校招之前,建议提前了解目标公司的文化和价值观,看看自己是否与之契合。这样,在面试过程中可以更加自信地表达自己对公司的认同和期望。
希望准备参加校招的年轻人们能够充分准备、积极面对挑战,找到自己满意的工作机会。加油!