保卫方案(京东2017笔试题)

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

输入描述: 
输入中有多组测试数据,每一组测试数据的第一行为一个整数n(3<=n<=10^6),为首都周围的小山数量,第二行为n个整数,依次表示为小山的高度h(1<=h<=10^9). 
输出描述: 
对每组测试数据,在单独的一行中输出能相互观察到的岗哨的对数。 
示例1 
输入


1 2 4 5 3 
输出

7

解题思路:

把这一组数围成一个环,判断两个点(A、B)是否能够通信的方法是,这两点之间的点是否都比这两个点小(比A小且比B小)。看了别人的解法试着分析一下吧。。。 

我看了其他的人的想法,这一题有点像 
leetcode No84. Largest Rectangle in Histogram

我们可以用单调递减栈来做,以题中的例子为例 1 2 4 5 3

先考虑没有连续重复元素的情况,接着说有重复元素的情况 
先循环移位,让最大值5作为第一个元素(why?),那么这个时候,数组为 5 3 1 2 4。

设结果为res=0; 
由于栈是单调递归栈,那么5,3,1 依次入栈。此时栈顶元素是1。接下来要入栈的是2。2大于栈顶元素1,那么栈顶元素1的左边(3),右边(2)。那么1是可以看到3和2的,有两对。res=2

1出栈,此时栈顶元素为3,2小于3。所以2进栈。此时栈顶元素为2。接下来要4要进栈,4大于栈顶元素2。那么栈顶元素2可以看到左边(3)右边(4)。res=2+2=4。2出栈

同理,3出栈,4进栈。res=4+2=6。 
此时,栈里面的元素只有4和5。作为一对。res=6+1=7。

有重复元素的情况下,假设 3,1(连续n个1) ,4。这n个1都能看到3和4有2*n个,而且这个n个1是可以互相看到对方,所以有Cn2个。所以对数为 n*2+n*(n-1)/2。两种元素的情况下 1(t1个1)、2(t2个2),对数为t1*t2+t1*(t1-1)/2+t2*(t2-1)/2。

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <stack>
#include <math.h>
#define N 10006
using namespace std;


int main() {
	int n, maxVal = -5;
	stack<pair<int, int> > s; 
	long long res = 0;
	scanf("%d", &n);
	vector<int> v(n, 0);
	for (int i = 0; i < n; i++) {
		cin >> v[i];
		maxVal = max(maxVal, v[i]);
	}
	
	vector<int>::iterator it = v.begin();
	while (*it != maxVal) {
		int temp = *it;
		it = v.erase(it);
		v.push_back(temp);
	}

	s.push(pair<int, int>(v[0], 1));
	for (int i = 1; i < n; i++) {
		if (v[i] == s.top().first) {
			s.top().second++;
		} else if (v[i] < s.top().first) {
			s.push(pair<int, int>(v[i], 1));
		} else {
			while (v[i] > s.top().first) {
				long long t = s.top().second;
				res += t * (t - 1) / 2 + 2 * t;
				s.pop();
			}
			if (v[i] == s.top().first) { 			//做了出栈操作后,继续判断 
				s.top().second++; 
			} else {
				s.push(pair<int, int>(v[i], 1));
			}
		}
	}
	if (s.size() > 2) {
		while (s.size() > 1) {
			long long t = s.top().second;
			res += t * (t - 1) / 2 + 2 * t;
			s.pop();
		}
		long long t = s.top().second;
            res += t * (t - 1) / 2;
            s.pop();
	} else if (s.size() == 2){
            long long a = s.top().second;
            s.pop();
            long long b = s.top().second;
            s.pop();
            res += b * (b-1) / 2 + a * (a-1) / 2 + a * b;
        } else {
            long long t = s.top().second;
            res += t * (t - 1) / 2;   
            s.pop(); 
        }
        cout << res << endl;
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值