保卫方案(京东笔试题)

Question

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

输入描述

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

输出描述

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

思路

你要说这是单调栈板子题似乎有点看不起京东。总结一下,两座山A,B之间能通信的要求是,A,B之间的点都比这个点小,确实看到这很容易想到单调栈,构建一个单调递减栈,以1,2,4,5,3为例。

首先找到这些元素中的最大值,以最大值作为起点进行入栈操作,那么5,3,1依次入栈,接下来要入栈的是2,2大于1,所以1出栈,1的左边(3),右边(2)。那么1是可以看到2和3的,有两对。res += 2

4准备进栈,4大于2,所以2要出栈,2的左边(3),右边(4),res += 2。

同理,3也要出栈,res += 2。此时栈里面只有4和5作为一对,res += 1。

上述是一种特殊情况,没有重复元素时,如果有重复元素,假设数组是4,4,5,5,6,6,此时stack中存的是一个pair,pair中包含两个变量,当前存的值,以及该值的个数,比方说遍历数组,首先是下标为4的6先进栈,然后下标为5的6进栈,因为值相同,所以pair.times++,然后两个4进栈,此时的情况见下图
保卫方案(京东笔试题)_第1张图片
4要出栈,但是此时res要加多少呢?这其实是一个排列组合的问题res += C(4,2)+2*2,然后两个5都进来了,数组遍历结束,但是此时栈中还有值
保卫方案(京东笔试题)_第2张图片
数组遍历完了,但是栈中还有值,这种情况要特殊讨论

  • 栈中元素个数大于2,res += C(stack.peek().times,2) + 2*stack.pop().times
  • 栈中元素个数等于2
    • 如果栈底元素的times==1,则res += C(stack.peek().times,2)+stack.pop().times
    • 如果栈底元素的times !=1 ,则res += C(stack.peek().times,2)+2*stack.pop().times
  • 栈中元素小于2(就一个),res += C(stack.pop().times,2)

代码

package nowcoder;
import java.util.*;
public class Main {
	public static int nexIndex(int size,int i) {
		return i < (size - 1) ? (i + 1) : 0;
	}
	private static class Pair {
		public int value;
		public int times;
		Pair(int value) {
			this.value = value;
			this.times = 1;
		}
	}
	public static long getInternalSum(int k) {
		return k == 1L ? 0L : (long)k * (long)(k - 1) / 2L;
	}
	public static long communications(int[] arr) {
		if(arr == null || arr.length < 2)
			return 0;
		int size = arr.length;
		int maxIndex = 0;
		for(int i = 0;i < size;i++)
			maxIndex = arr[maxIndex] < arr[i] ? i : maxIndex;
		int value = arr[maxIndex];//最大值
		int index = nexIndex(size,maxIndex);//最大值的下一个
		long res = 0L;
		Stack<Pair> stack = new Stack<Pair>();
		stack.push(new Pair(value));
		while(index != maxIndex) {
			value = arr[index];
			while(!stack.isEmpty() && stack.peek().value < value) {
				int times = stack.pop().times;
				res += getInternalSum(times) + 2 * times;//C(times,2) + 2 * times
			}
			if(!stack.isEmpty() && stack.peek().value == value) 
				stack.peek().times++;
			else 
				stack.push(new Pair(value));
			index = nexIndex(size,index);
		}
		while(!stack.isEmpty()) {
			int times = stack.pop().times;
			res += getInternalSum(times);
			if(!stack.isEmpty()) {
				res += times;
				if(stack.size() > 1)
					res += times;
				else 
					res += stack.peek().times > 1 ? times : 0;
			}
		}
		return res;
	}
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		while(cin.hasNext()) {
			int n = cin.nextInt();
			int[] arr = new int[n];
			HashSet<Integer> tmp = new HashSet<Integer>();
			for(int i = 0;i < n;i++) {
				arr[i] = cin.nextInt();
				tmp.add(arr[i]);
			}
			if(tmp.size() == n)//没有重复元素,直接用结论
				System.out.println((long)2 * n - 3);
			else
				System.out.println(communications(arr));
		}
	}
}

你可能感兴趣的:(算法)