近些年来,我国防沙治沙取得显著成果。某沙漠新种植N棵胡杨(编号1-N),排成一排。
一个月后,有M棵胡杨未能成活。
现可补种胡杨K棵,请问如何补种(只能补种,不能新种),可以得到最多的连续胡杨树?
N 总种植数量,1 <= N <= 100000
M 未成活胡杨数量,M 个空格分隔的数,按编号从小到大排列,1 <= M <= N
K 最多可以补种的数量,0 <= K <= M
5
2
2 4
1
3
说明 补种到2或4结果一样,最多的连续胡杨棵树都是3。
10
3
2 4 7
1
6
说明 补种第7棵树,最多连续胡杨树棵数位6(5,6,7,8,9,10)
解题思路:
初始化树木状态: 我们首先将树木的状态表示为一个长度为 n 的数组 trees,其中 1 表示树木存活,0 表示树木已经死亡。输入的
dead_indices 数组给出了死亡树木的位置(是 1-based 的,需要转化成 0-based)。
滑动窗口: 我们使用两个指针 left 和 right 来表示当前考虑的连续区间。right 指针从左到右遍历整个数组,left
指针表示当前区间的左边界。我们维护一个 zeros_count 来记录当前窗口内死树的数量。
窗口调整: 每当窗口中的死树数量超过了 k,我们就移动 left 指针来缩小窗口,直到死树的数量不超过
k。每次更新窗口时,我们记录当前窗口的长度,并更新最大长度 max_length。
返回结果: 遍历完整个数组后,max_length 就是我们能找到的最大长度的连续区域。
关键点:
滑动窗口算法: 本算法通过维护一个动态变化的窗口,能够在 O(n) 时间复杂度内找到最长的符合条件的区间。 边界条件处理:
在处理 dead_indices 时,注意数组是 1-based,而 Python 数组是 0-based,所以我们需要进行转换。
时间复杂度分析:
时间复杂度: 由于每个元素最多被 left 或 right 指针遍历一次,因此时间复杂度是 O(n),其中 n
是树木的数量。 空间复杂度: 我们使用了一个长度为 n 的数组来表示树木状态,因此空间复杂度是 O(n)。
def max_continuous_trees(n, m, dead_indices, k):
# 将死树的索引转化为集合,以便快速查找
dead_set = set(dead_indices)
# 初始化树木数组,默认所有树木都是活的(1表示活树)
trees = [1] * n
# 将死树位置置为0
for index in dead_indices:
trees[index - 1] = 0 # dead_indices是1-based,所以我们要减去1使其变为0-based
max_length = 0 # 记录最大连续区域的长度
left = 0 # 左指针
zeros_count = 0 # 记录当前窗口内的死树数量
# 遍历整个数组,right是右指针
for right in range(n):
if trees[right] == 0: # 如果当前树木是死树
zeros_count += 1 # 死树数量加1
# 如果当前窗口内的死树数量超过了k,调整左指针
while zeros_count > k:
if trees[left] == 0: # 如果左边界是死树
zeros_count -= 1 # 死树数量减1
left += 1 # 移动左指针,缩小窗口
# 更新最大连续区域长度
max_length = max(max_length, right - left + 1)
return max_length # 返回最大连续区间的长度
if __name__ == "__main__":
# 读取输入数据
n = int(input()) # 树木总数
m = int(input()) # 死树的数量
dead_indices = list(map(int, input().split())) # 死树的位置列表
k = int(input()) # 允许的最大死树数量
# 计算结果并输出
result = max_continuous_trees(n, m, dead_indices, k)
print(result)
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// 创建Scanner对象用于输入
Scanner sc = new Scanner(System.in);
// 输入数组的大小
int size = sc.nextInt();
// 输入最多允许的零的个数
int toRemove = sc.nextInt();
// 创建并初始化数组,所有元素初始为1
int[] arr = new int[size];
Arrays.fill(arr, 1);
// 输入并设置数组中要置为零的索引位置
for (int i = 0; i < toRemove; i++) {
int index = sc.nextInt() - 1; // 输入的索引是从1开始,减去1转为0基索引
arr[index] = 0; // 将该索引位置的元素置为零
}
// 输入最多允许的零的个数
int maxZeros = sc.nextInt();
// 调用findMaxSubarray函数计算结果并输出
System.out.println(findMaxSubarray(arr, maxZeros));
}
// findMaxSubarray方法用于找到符合条件的最长子数组
public static int findMaxSubarray(int[] arr, int maxZeros) {
// 左指针,初始化为0
int left = 0;
// 当前窗口中零的个数
int zeroCount = 0;
// 最大子数组的长度
int maxLength = 0;
// 右指针,遍历数组
for (int right = 0; right < arr.length; right++) {
// 如果当前元素为零,零的数量增加
if (arr[right] == 0) {
zeroCount++;
}
// 当零的个数超过maxZeros时,收缩窗口
while (zeroCount > maxZeros) {
// 如果左边的元素是零,减少零的数量
if (arr[left] == 0) {
zeroCount--;
}
// 移动左指针,缩小窗口
left++;
}
// 更新最大子数组的长度
maxLength = Math.max(maxLength, right - left + 1);
}
// 返回最大的子数组长度
return maxLength;
}
}
#include
#include
#include
#define MAX_TREES 10000 // 最大树木数量的定义
// 函数findMaxContinuous,用于找到最长的连续子数组,该子数组中最多有maxPlant个死树(即值为0的元素)
int findMaxContinuous(int trees[], int totalTrees, int maxPlant) {
int start = 0; // 左指针,表示子数组的起始位置
int deadIndices[MAX_TREES]; // 存储死树(值为0)的位置
int deadCount = 0; // 当前窗口中死树的个数
int maxLength = 0; // 最长子数组的长度
// 右指针,遍历整个树木数组
for (int end = 0; end < totalTrees; end++) {
if (trees[end] == 0) { // 如果当前树是死的(值为0)
deadIndices[deadCount++] = end; // 将当前死树的位置加入deadIndices数组
// 如果当前窗口中的死树数量超过了maxPlant,更新窗口
if (deadCount > maxPlant) {
// 计算并更新当前最长子数组的长度
maxLength = (end - start > maxLength) ? end - start : maxLength;
// 将左指针移到第一个死树的后面,即start跳到deadIndices[0] + 1的位置
start = deadIndices[0] + 1;
// 将deadIndices数组中的死树位置往前移动,移除掉最早的死树
for (int i = 1; i < deadCount; i++) {
deadIndices[i - 1] = deadIndices[i];
}
// 减少死树计数
deadCount--;
}
}
// 更新当前窗口的最大长度
maxLength = (end - start + 1 > maxLength) ? end - start + 1 : maxLength;
}
// 返回最终找到的最长合法子数组的长度
return maxLength;
}
int main() {
int totalTrees, deadTrees, maxPlant;
// 输入树木的总数和死树的数量
scanf("%d %d", &totalTrees, &deadTrees);
// 初始化一个数组treeLine,表示所有树初始为活树(值为1)
int treeLine[MAX_TREES];
for (int i = 0; i < totalTrees; i++) {
treeLine[i] = 1; // 所有树初始值为1,表示活树
}
// 输入死树的索引,并将对应位置的树标记为死树(值为0)
for (int i = 0; i < deadTrees; i++) {
int deadIndex;
scanf("%d", &deadIndex);
treeLine[deadIndex - 1] = 0; // 将该位置的树设为死树
}
// 输入最大允许的死树数量
scanf("%d", &maxPlant);
// 调用findMaxContinuous函数并输出结果
printf("%d\n", findMaxContinuous(treeLine, totalTrees, maxPlant));
return 0;
}
// 引入 readline 模块,用于从标准输入获取用户输入
const readline = require("readline");
// 创建一个 readline 接口,用于处理输入和输出
const rl = readline.createInterface({
input: process.stdin, // 标准输入
output: process.stdout, // 标准输出
});
// 用于存储输入的各行数据
const inputs = [];
// 监听输入的每一行数据
rl.on("line", (input) => {
// 将每行输入保存到 inputs 数组中
inputs.push(input);
// 当输入的行数达到4行时,表示输入已完成
if (inputs.length === 4) {
// 解析输入数据
const total = parseInt(inputs[0], 10); // 树的总数
const deadCount = parseInt(inputs[1], 10); // 死树的数量
const deadPositions = inputs[2].split(" ").map(Number); // 死树的位置数组
const maxReplant = parseInt(inputs[3], 10); // 最大可重新种植的死树数
// 调用 findMaxConsecutive 函数来计算结果
const result = findMaxConsecutive(total, deadPositions, maxReplant);
// 输出计算结果
console.log(result);
// 清空 inputs 数组,准备处理下一个输入(如果有的话)
inputs.length = 0;
}
});
// 函数 findMaxConsecutive 用于寻找最长的连续子数组,子数组中最多可以有 k 个死树
function findMaxConsecutive(n, deadTrees, k) {
let leftPtr = 0; // 左指针,表示窗口的起始位置
let replantCnt = 0; // 当前窗口内的死树数量
let maxConsec = 0; // 最大的合法子数组长度
let deadPos = new Set(deadTrees); // 使用 Set 存储死树的位置,便于快速查找
// 遍历数组,使用 rightPtr 作为右指针,表示当前窗口的结束位置
for (let rightPtr = 0; rightPtr < n; rightPtr++) {
// 如果当前右指针指向的树是死树,增加死树计数
if (deadPos.has(rightPtr + 1)) {
replantCnt++;
}
// 如果当前窗口内的死树数量超过了允许的最大数量 k,移动左指针收缩窗口
while (replantCnt > k) {
// 如果左指针指向的是死树,减少死树计数
if (deadPos.has(leftPtr + 1)) {
replantCnt--;
}
// 移动左指针,缩小窗口
leftPtr++;
}
// 更新最大子数组的长度
maxConsec = Math.max(maxConsec, rightPtr - leftPtr + 1);
}
// 返回最大连续合法子数组的长度
return maxConsec;
}
#include
#include
#include
#define MAX_TREES 10000 // 定义树木数组的最大长度
// 函数 findMaxContinuous 用于找出最多可以允许 maxPlant 个死树的最长连续子数组
int findMaxContinuous(int trees[], int totalTrees, int maxPlant) {
int start = 0; // 初始化左指针,表示窗口的起始位置
int deadIndices[MAX_TREES]; // 用于存储当前窗口中死树的位置
int deadCount = 0; // 当前窗口内死树的数量
int maxLength = 0; // 存储最大子数组的长度
// 遍历整个树木数组,右指针右移
for (int end = 0; end < totalTrees; end++) {
// 如果当前树是死树(值为0),将其索引记录在 deadIndices 数组中
if (trees[end] == 0) {
deadIndices[deadCount++] = end; // 增加死树计数并记录该死树的位置
// 如果当前窗口中的死树数量超过了 maxPlant,缩小窗口
if (deadCount > maxPlant) {
// 计算并更新当前最大合法子数组的长度
maxLength = (end - start > maxLength) ? end - start : maxLength;
// 将左指针移动到第一个死树的后面,即 start 移动到 deadIndices[0] + 1
start = deadIndices[0] + 1;
// 将 deadIndices 数组中已经处理过的死树位置移除,更新剩余死树的位置
for (int i = 1; i < deadCount; i++) {
deadIndices[i - 1] = deadIndices[i];
}
// 死树数量减少
deadCount--;
}
}
// 每次右指针移动时,更新最大子数组的长度
maxLength = (end - start + 1 > maxLength) ? end - start + 1 : maxLength;
}
// 返回最大子数组的长度
return maxLength;
}
int main() {
int totalTrees, deadTrees, maxPlant;
// 读取输入的总树木数量和死树的数量
scanf("%d %d", &totalTrees, &deadTrees);
// 初始化树木数组,所有树初始为活树(1)
int treeLine[MAX_TREES];
for (int i = 0; i < totalTrees; i++) {
treeLine[i] = 1; // 默认值为 1,表示活树
}
// 读取死树的索引,并将相应位置的树标记为死树(0)
for (int i = 0; i < deadTrees; i++) {
int deadIndex;
scanf("%d", &deadIndex);
treeLine[deadIndex - 1] = 0; // 注意输入的索引是从1开始,需要减1
}
// 读取最大允许的死树数量
scanf("%d", &maxPlant);
// 调用 findMaxContinuous 函数来计算最大合法子数组的长度,并输出结果
printf("%d\n", findMaxContinuous(treeLine, totalTrees, maxPlant));
return 0;
}