保卫方案

题目描述

战争游戏的至关重要环节就要到来了,这次的结果将决定王国的生死存亡,小B负责首都的防卫工作。首都位于一个四面环山的盆地中,周围的n个小山构成一个环,作为预警措施,小B计划在每个小山上设置一个观察哨,日夜不停的瞭望周围发生的情况。 一旦发生外地入侵事件,山顶上的岗哨将点燃烽烟,若两个岗哨所在的山峰之间没有更高的山峰遮挡且两者之间有相连通路,则岗哨可以观察到另一个山峰上的烽烟是否点燃。由于小山处于环上,任意两个小山之间存在两个不同的连接通路。满足上述不遮挡的条件下,一座山峰上岗哨点燃的烽烟至少可以通过一条通路被另一端观察到。对于任意相邻的岗哨,一端的岗哨一定可以发现一端点燃的烽烟。 小B设计的这种保卫方案的一个重要特性是能够观测到对方烽烟的岗哨对的数量,她希望你能够帮她解决这个问题。

输入描述:

输入中有多组测试数据,每一组测试数据的第一行为一个整数n(3<=n<=10^6),为首都周围的小山数量,第二行为n个整数,依次表示为小山的高度h(1<=h<=10^9).

输出描述:

对每组测试数据,在单独的一行中输出能相互观察到的岗哨的对数。

示例1

输入

复制

5
1 2 4 5 3

输出

复制

7

思路:
对于每一座山,找左右两边第一个高于它的山,它作为配对中第二高的山,虽然思路不难,但要考虑相等的情况,较为麻烦。时间复杂度O(n)。有left[n],right[n]记录左右两边第一个高于它的下标。每一座山的结果,可以基于求得的前一座山。

import java.util.Scanner;
public class Main{
    public static void main(String[] arg){
        Scanner sc=new Scanner(System.in);
        long cnt=0;
        while(sc.hasNext()){
            int n=sc.nextInt();
            int[] arr=new int[n];
            for(int i=0;i<arr.length;i++){
                arr[i]=sc.nextInt();
            }   
            int[] l=new int[n];
            int[] r=new int[n];
            for(int i=0;i<n;i++){
                l[i]=i-1;
                r[i]=i+1;
                if(i==0) l[i]=n-1;
                if(i==n-1) r[i]=0;
            }
            int[] count=new int[n];
            //寻找左边的最大值
            for(int i=0;i<n;i++){
                int p=l[i];
                while(p!=i&&arr[p]<=arr[i]){
                    if(arr[i]==arr[p])count[p]++;
                    if(l[p]==p||(arr[p]==arr[i]&&arr[l[p]]>arr[i])){//左边第一个
                        cnt+=count[p];
                    }
                    if(l[p]==p){
                        break;
                    }
                    p=l[p];
                }
                if(p!=i&&arr[p]!=arr[i]) cnt++;
                l[i]=p;
            }
            //寻找右边的最大值
            for(int i=n-1;i>=0;i--){
                int p=r[i];
                while(p!=i&&arr[p]<=arr[i]){
                    if(r[p]==p) break;
                    p=r[p];
                }
                if(p!=i&&p!=l[i]&&arr[p]!=arr[i]) cnt++;
                r[i]=p;
            }
        }
        System.out.println(cnt);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值