思路:由于屏障作用后使得隔离两侧的哨兵彼此不能看到另一侧,即只能在同一侧内观察,故要试图找出出每个隔离位置前面及后面的防御力,由于屏障的隔离性,选择用一个类似前缀数组,一个类似后缀数组来分别存从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;
}