题目## 题目
解题思路
这是一个典型的动态规划问题:
- d p [ i ] [ j ] dp[i][j] dp[i][j] 表示到达位置 ( i , j ) (i,j) (i,j) 的最小路径和
- 每个位置只能从上方或左方到达
- 状态转移方程: d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + m a t r i x [ i ] [ j ] dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + matrix[i][j] dp[i][j]=min(dp[i−1][j],dp[i][j−1])+matrix[i][j]
- 需要特别处理第一行和第一列,因为它们只能从一个方向到达
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> matrix(n, vector<int>(m));
vector<vector<int>> dp(n, vector<int>(m));
// 读取矩阵
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
cin >> matrix[i][j];
}
}
// 初始化dp数组
dp[0][0] = matrix[0][0];
// 处理第一行
for(int j = 1; j < m; j++) {
dp[0][j] = dp[0][j-1] + matrix[0][j];
}
// 处理第一列
for(int i = 1; i < n; i++) {
dp[i][0] = dp[i-1][0] + matrix[i][0];
}
// 动态规划过程
for(int i = 1; i < n; i++) {
for(int j = 1; j < m; j++) {
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + matrix[i][j];
}
}
cout << dp[n-1][m-1] << endl;
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[][] matrix = new int[n][m];
int[][] dp = new int[n][m];
// 读取矩阵
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
matrix[i][j] = sc.nextInt();
}
}
// 初始化dp数组
dp[0][0] = matrix[0][0];
// 处理第一行
for(int j = 1; j < m; j++) {
dp[0][j] = dp[0][j-1] + matrix[0][j];
}
// 处理第一列
for(int i = 1; i < n; i++) {
dp[i][0] = dp[i-1][0] + matrix[i][0];
}
// 动态规划过程
for(int i = 1; i < n; i++) {
for(int j = 1; j < m; j++) {
dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + matrix[i][j];
}
}
System.out.println(dp[n-1][m-1]);
sc.close();
}
}
n, m = map(int, input().split())
matrix = []
for _ in range(n):
matrix.append(list(map(int, input().split())))
dp = [[0] * m for _ in range(n)]
dp[0][0] = matrix[0][0]
# 处理第一行
for j in range(1, m):
dp[0][j] = dp[0][j-1] + matrix[0][j]
# 处理第一列
for i in range(1, n):
dp[i][0] = dp[i-1][0] + matrix[i][0]
# 动态规划过程
for i in range(1, n):
for j in range(1, m):
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + matrix[i][j]
print(dp[n-1][m-1])
算法及复杂度
- 算法:动态规划
- 时间复杂度: O ( n m ) \mathcal{O}(nm) O(nm)
- 空间复杂度: O ( n m ) \mathcal{O}(nm) O(nm)
关键点说明:
- 使用二维 d p dp dp 数组记录到达每个位置的最小路径和
- 需要特别处理第一行和第一列的初始化
- 对于其他位置,取上方和左方的最小值加上当前位置的值
- 最终 d p dp dp 数组的右下角即为所求的最小路径和
注意:这里使用了 O ( n m ) \mathcal{O}(nm) O(nm) 的空间复杂度,如果需要优化空间复杂度,可以使用滚动数组将空间复杂度降至 O ( m ) \mathcal{O}(m) O(m)。
解题思路
一、问题分析
-
题目要求
- 从数组中选择数字
- 任意两数差值不超过 k k k
- 求最多可选数字个数
-
关键点
- 差值限制意味着选择的数字要在一个范围内
- 排序后可以简化问题
-
基本思路
- 先对数组排序
- 使用滑动窗口
- 窗口内的数字差值都不超过 k k k
- 维护最大窗口大小
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
vector<int> nums(n);
for(int i = 0; i < n; i++) {
cin >> nums[i];
}
// 排序
sort(nums.begin(), nums.end());
// 滑动窗口
int maxCount = 1;
int left = 0;
for(int right = 1; right < n; right++) {
// 如果当前窗口的最大差值超过k,移动左指针
while(left < right && nums[right] - nums[left] > k) {
left++;
}
maxCount = max(maxCount, right - left + 1);
}
cout << maxCount << endl;
return 0;
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int k = sc.nextInt();
int[] nums = new int[n];
for(int i = 0; i < n; i++) {
nums[i] = sc.nextInt();
}
// 排序
Arrays.sort(nums);
// 滑动窗口
int maxCount = 1;
int left = 0;
for(int right = 1; right < n; right++) {
// 如果当前窗口的最大差值超过k,移动左指针
while(left < right && nums[right] - nums[left] > k) {
left++;
}
maxCount = Math.max(maxCount, right - left + 1);
}
System.out.println(maxCount);
}
}
n, k = map(int, input().split())
nums = list(map(int, input().split()))
# 排序
nums.sort()
# 滑动窗口
max_count = 1
left = 0
for right in range(1, n):
# 如果当前窗口的最大差值超过k,移动左指针
while left < right and nums[right] - nums[left] > k:
left += 1
max_count = max(max_count, right - left + 1)
print(max_count)
算法及复杂度分析
算法:滑动窗口,双指针
时间复杂度
- 排序: O ( n log n ) \mathcal{O}(n\log n) O(nlogn)
- 滑动窗口: O ( n ) \mathcal{O}(n) O(n)
- 总体: O ( n log n ) \mathcal{O}(n\log n) O(nlogn)
空间复杂度
- O ( n ) \mathcal{O}(n) O(n),输入的数组。
关键点说明
-
排序的必要性
- 排序后可以保证窗口内的数字连续
- 只需要比较窗口两端的差值
-
滑动窗口维护
- 右指针不断向右移动
- 左指针在差值超过 k k k 时移动
- 记录最大窗口大小
-
边界条件处理
- 数组长度为 1 1 1 的情况
- 所有数字都可以选择的情况