《算法竞赛·快冲300题》每日一题:“凹”

算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。


” ,链接: http://oj.ecustacm.cn/problem.php?id=1845

题目描述

【题目描述】 给定一个长度为n的数组a,请求出存在多少个二元组<i,j>满足“凹”的性质:
1、1≤i<j≤n;
2、对于所有的i<k<j均满足a[i]>a[k],a[j]>a[k]。
注意,当j=i+1时,k不存在,也满足上述约束。
【输入格式】 第一行为正整数n,n≤1000000。
接下来n行,每行一个a[i],1≤a[i]≤n,a[i]不互相同。
【输出格式】 输出一个数字表示答案。
【输入样例】

样例13
2
1
3

样例26
1
3
2
6
4
5 

【输出样例】

样例13

样例27 

题解

   首先模拟检查“凹”,了解执行的过程。以“3 1 2 6”为例,其中的“凹”有:“3 1 2”和“3 1 2 6”;还有j = i + 1的“3 1”、“1 2”、“2 6”。一共5个。
   像“3 1 2”和“3 1 2 6”这样的“凹”,需要检查连续的3个以上的数字。
   例如“3 1 2”,从“3”开始,下一个应该比“3”小,例如“1”,再后面的数字比“1”大,才能形成“凹”。
   再例如“3 1 2 6”,前面的“3 1 2”已经是“凹”了,最后的“6”也会形成新的“凹”,条件是这个“6”必须比中间的“1 2”大才行。
   总结上述过程是:先检查“3”;再检查“1”符合“凹”;再检查“2”,比前面的“1”大,符合“凹”;再检查“6”,比前面的“2”大,符合“凹”。
   以上是检查一个“凹”的两头,还有一种是“嵌套”。一旦遇到比前面小的数字,那么以这个数字为头,可能形成新的“凹”。例如“6 4 2 8”,其中的“6 4 2 8”是“凹”,二内部的“4 2 8”也是凹。如果学过递归、栈,就会发现这是嵌套,所以本题用栈来做很合适。
   以“3 1 2 6”为例,用栈模拟找“凹”。当新的数比栈内的数小,就进栈;如果比栈内的数大,就出栈,并记录找到了一个“凹”。
在这里插入图片描述

   然而,上述讨论隐含了一个前提,就是“凹”的尾部比头大,所以只能找到“头<尾”的“凹”。如何找“头>尾”的“凹”?只需反过来用栈执行一遍就行了。
【笔记】 栈的应用 。

C++代码

#include<bits/stdc++.h>
using namespace std;
int a[1000010];
stack<int>s;
int ans;
int main(){
    int n;  scanf("%d", &n);
    for(int i = 1; i <= n; i++)  scanf("%d", &a[i]);
    //从头到尾
    for(int i = 1; i <= n; i++){
        while(!s.empty() && s.top() < a[i])
            ans++, s.pop();
        s.push(a[i]);
    }
    while(!s.empty()) //清空栈s
        s.pop();
    //从尾到头
    for(int i = n; i >= 1; i--){
        while(!s.empty() && s.top() < a[i])
            ans++, s.pop();
        s.push(a[i]);
    }
    cout<<ans<<endl;
    return 0;
}

Java代码

import java.util.Scanner;
import java.util.Stack;
 
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) 
            a[i] = sc.nextInt();        
        sc.close(); 
        Stack<Integer> s = new Stack<Integer>();
        int ans = 0;
         // 从头到尾
        for (int i = 0; i < n; i++) {
            while (!s.empty() && s.peek() < a[i]) {
                ans++;
                s.pop();
            }
            s.push(a[i]);
        }
         s.clear();
         // 从尾到头
        for (int i = n - 1; i >= 0; i--) {
            while (!s.empty() && s.peek() < a[i]) {
                ans++;
                s.pop();
            }
            s.push(a[i]);
        }
        System.out.println(ans);
    }
}

Python代码

   注意两点:用setrecursionlimit扩栈,用readline加快读入速度。

import sys
sys.setrecursionlimit(1000000)
input = sys.stdin.readline           #加这句后读入会快些
a = []
s = []
ans = 0
n = int(input())
for _ in range(n):  a.append(int(input()))
# 从头到尾
for i in range(n):
    while len(s) > 0 and s[-1] < a[i]:
        ans += 1
        s.pop()
    s.append(a[i])

s.clear()
# 从尾到头
for i in range(n-1, -1, -1):
    while len(s) > 0 and s[-1] < a[i]:
        ans += 1
        s.pop()
    s.append(a[i])
print(ans)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗勇军

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值