牛客--最优屏障(栈+前缀后缀模拟)

在这里插入图片描述
思路:由于屏障作用后使得隔离两侧的哨兵彼此不能看到另一侧,即只能在同一侧内观察,故要试图找出出每个隔离位置前面及后面的防御力,由于屏障的隔离性,选择用一个类似前缀数组,一个类似后缀数组来分别存从1-i每个位置往前看和往后看的防御力值,由于上述式子的限定,其实对于每一个位置,在看的一座较高山后面的较矮山都看不到了,由于从头到目标的前一个位置都要求一个高度递减的单调性,故采用栈结构来存每座山,遍历时每次遇到比当前山矮的山都pop掉,因为该矮山对之后的山根本就没有贡献,这样求一次前缀,一次后缀。最后维护的答案是pre(n)(或suf(n))-(pre(i-1)+suf(i)),即之前的总值减掉屏蔽后的值,取max即可。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
#define maxn 50005
int a[maxn],pre[maxn],suf[maxn];
stack<int>st;

int main()
{
	int i,j;
	int n,m;
	int t;
	int k=0;
	scanf("%d",&t);
	while(t--)
	{
		memset(pre,0,sizeof(pre));
		memset(suf,0,sizeof(suf));
		while(!st.empty())
		{
			st.pop();
		}
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
		}
		for(i=1;i<=n;i++)
		{
			int now=0;
			pre[i]=pre[i-1];//计算的是往前看时能到达的最大防御力前缀和;
			while(!st.empty()&&st.top()<a[i])//计数同时更新山的位置,因为高度低的之前的那些山在目前这座山的屏蔽下都不能被之后的山观察到;
			{
				now++;//可行解+1;
				st.pop(); 
			 } 
			 if(!st.empty())
			 {
			 	pre[i]+=(now+1);//因为只是计算了比它矮的山,当前高山还没有记录,所以要+1; 
			 }
			 else
			 {
			 	pre[i]+=now;
			 } 
			 st.push(a[i]);
		}
		while(!st.empty())
		{
			st.pop();//之后要计算往后看的,要把栈先清空; 
		}
		//同理,每次计算后缀;
		for(i=n;i>=1;i--)
		{
			suf[i]=suf[i+1];
			int now=0;
			while(!st.empty()&&st.top()<a[i])
			{
				st.pop();
				now++;
			}
			if(!st.empty())
			{
				suf[i]+=(now+1);
			}
			else
			{
				suf[i]+=now;
			}
			st.push(a[i]);
		 } 
		 //之后就遍历,每个i的位置之前放置屏障取最大值;
		 int maxx=-1;
		 int maxpos;//记录位置;
		 //这里记录当前减小值的式子为:pre[n](即所有防御力的和)-(pre[i-1]+suf[i])(即加完屏障后,屏障前的只能往前看,屏障后的只能往后看);
		 //两者之差即为减小值;  
		 for(i=1;i<=n;i++)
		 {
		 	if((pre[n]-(pre[i-1]+suf[i]))>maxx)
		 	{
		 		maxx=pre[n]-(pre[i-1]+suf[i]);
		 		maxpos=i;
			 }
		 } 
		 printf("Case #%d: %d %d\n",++k,maxpos,maxx);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值