保卫方案,单调栈

保卫方案

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

思路:

利用单调栈,取出每个数左边和右边分别离他最大的数,然后计算这样的对数有多少个,加上两两之间的对数,就是总的对数。

首先,找到最大的数,并记录其下标,作为压入栈底的开始,这样可以保证这个栈在压入的过程中永不为空

其次,在压栈的过程中,如果当前要压入栈的元素 arr[i]  比栈顶元素小,那么继续压入,

但是如果arr[i]比栈顶元素大,那么弹出栈顶元素 即为 top, 那么 左边离他最近的数就是弹出后的栈的栈顶,右边离他最大的就是arr[i], 通过这种方式,就能 找到左右最近最大的值。

以上仅仅考虑不重复元素,即可解决,但是当出现重复元素的时候,需要额外考虑元素之间两两的配对,所以我们可以定义一个结构体,来保存这个数存入的次数,  当这个数被弹出的时候,可以直接计算一共有多少对,即 =  C_n_2 +  n + n  ,其中 n 表示这个数的重复次数。

C_n_2  是表示,从这n个数中,取出不重复的对为  C_n_2,而如果左边数还在,则还要+ n 个对,因为 这n个数字分别要和这个最大的数进行配对,所以有n个,而右边如果也存在比他大的数的话,也是同理。

其他的就是考虑最大的数,因为最大的数只能自己之间互相配对,为 C_n_2

代码:

#include<iostream>
#include<cstdlib>
#include<ctime>
#include<stack>
using namespace std;
struct node{
	int num;
	int count;
	node(int num){
		this->num = num;
		this->count = 1;
	}
};
typedef long long ll;
int N;
int arr[1000000+100];
stack<node> stk;

int get_next(int id,int len){
	return  id>=(len-1)?0:id+1;
}

void scanfArr(){
	scanf("%d",&N);
	for(int i=0;i<N;i++){
		scanf("%d",&arr[i]);
	}
}

void printfArr(){
	for(int i=0;i<N;i++){
		printf("%d ",arr[i]);
	}
	printf("\n");
}

ll getC_n_2(int n){
	return n== 1? 0: (ll)n *(ll)(n-1)/(ll)2;
}

int main(){
	scanfArr();
	
	int max_idx = 0;
	int max_num = arr[max_idx];
	for(int i=0;i<N;i++){
		if(arr[i]>max_num){
			max_num = arr[i];
			max_idx = i;
		}
	}
	
	int idx = get_next(max_idx,N);
	node a = node(arr[max_idx]);
	stk.push(a);
	ll sum = 0;
	while(idx != max_idx){
		node temp = stk.top();
		if(arr[idx]>temp.num){
			stk.pop();
			sum += getC_n_2(temp.count);
			sum +=temp.count;
			if(!stk.empty()) sum+= temp.count;
			
			if (!stk.empty() && arr[idx] == stk.top().num){
				stk.top().count++;
			}else{
				node new_ = node(arr[idx]);
				stk.push(new_);
			}
		}else if(arr[idx]<temp.num){
			node new_ = node(arr[idx]);
			stk.push(new_);
		}else if( arr[idx] == temp.num){
			stk.top().count++;	
		}
		idx = get_next(idx,N);
	}
	

	while(!stk.empty()){
		node temp = stk.top(); stk.pop();
		sum+= getC_n_2(temp.count);
		if(!stk.empty()){
			sum+= temp.count;
			node temp_no = stk.top(); 
			if(temp_no.num == max_num){
				sum+= temp_no.count>1? temp.count:0;
			}else {
				sum+= temp.count;
			}
		}
	}
	
	printf("%ld\n",sum);
	
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值