农田最大产出评估
题意
有 个连续地块,每个地块有一个肥力指数。选取任意一段连续的地块,这段地块的"产出系数"等于段内最小肥力指数乘以地块数量。求所有连续子段中最大的产出系数。
思路
先想想暴力怎么做:枚举所有区间 ,找区间最小值再乘以长度。这是
的,
到
会超时。
换个角度想——如果我们固定"谁是区间里的最小值"呢?
假设第 个地块的肥力
是某个区间的最小值,那这个区间最远能往左延伸到哪里?往右延伸到哪里?答案就是:往左找到第一个比
小的位置
,往右找到第一个比
小的位置
,那以
为最小值的最宽区间就是
,宽度为
,产出系数就是
。
怎么高效求每个位置左边和右边第一个更小的元素?这正是单调栈的经典用法。
维护一个栈底到栈顶递增的单调栈:
- 从左往右扫一遍,求出每个位置左边第一个严格小于它的位置
。
- 从右往左扫一遍,求出每个位置右边第一个严格小于它的位置
。
最后遍历每个位置,取 的最大值即可。
复杂度
- 时间复杂度:
,每个元素最多入栈出栈各一次。
- 空间复杂度:
。
代码
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
scanf("%d",&n);
vector<int> a(n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
vector<int> left(n), right(n);
stack<int> st;
for(int i=0;i<n;i++){
while(!st.empty() && a[st.top()]>=a[i]) st.pop();
left[i] = st.empty()? -1 : st.top();
st.push(i);
}
while(!st.empty()) st.pop();
for(int i=n-1;i>=0;i--){
while(!st.empty() && a[st.top()]>=a[i]) st.pop();
right[i] = st.empty()? n : st.top();
st.push(i);
}
long long ans=0;
for(int i=0;i<n;i++){
long long w = right[i]-left[i]-1;
ans = max(ans, (long long)a[i]*w);
}
printf("%lld\n",ans);
return 0;
}
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = Integer.parseInt(br.readLine().trim());
}
int[] left = new int[n];
int[] right = new int[n];
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!st.isEmpty() && a[st.peek()] >= a[i]) st.pop();
left[i] = st.isEmpty() ? -1 : st.peek();
st.push(i);
}
st.clear();
for (int i = n - 1; i >= 0; i--) {
while (!st.isEmpty() && a[st.peek()] >= a[i]) st.pop();
right[i] = st.isEmpty() ? n : st.peek();
st.push(i);
}
long ans = 0;
for (int i = 0; i < n; i++) {
long w = right[i] - left[i] - 1;
ans = Math.max(ans, (long) a[i] * w);
}
System.out.println(ans);
}
}
import sys
input = sys.stdin.readline
def solve():
n = int(input())
a = [int(input()) for _ in range(n)]
left = [0] * n
right = [0] * n
st = []
for i in range(n):
while st and a[st[-1]] >= a[i]:
st.pop()
left[i] = st[-1] if st else -1
st.append(i)
st.clear()
for i in range(n - 1, -1, -1):
while st and a[st[-1]] >= a[i]:
st.pop()
right[i] = st[-1] if st else n
st.append(i)
ans = 0
for i in range(n):
w = right[i] - left[i] - 1
ans = max(ans, a[i] * w)
print(ans)
solve()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line.trim()));
rl.on('close', () => {
const n = parseInt(lines[0]);
const a = [];
for (let i = 1; i <= n; i++) a.push(parseInt(lines[i]));
const left = new Array(n);
const right = new Array(n);
const st = [];
for (let i = 0; i < n; i++) {
while (st.length > 0 && a[st[st.length - 1]] >= a[i]) st.pop();
left[i] = st.length === 0 ? -1 : st[st.length - 1];
st.push(i);
}
st.length = 0;
for (let i = n - 1; i >= 0; i--) {
while (st.length > 0 && a[st[st.length - 1]] >= a[i]) st.pop();
right[i] = st.length === 0 ? n : st[st.length - 1];
st.push(i);
}
let ans = 0;
for (let i = 0; i < n; i++) {
const w = right[i] - left[i] - 1;
ans = Math.max(ans, a[i] * w);
}
console.log(ans);
});

京公网安备 11010502036488号