最优屏障

题目链接:https://ac.nowcoder.com/acm/problem/14666

链接:https://ac.nowcoder.com/acm/problem/14666
来源:牛客网

题目描述

M国的地势高低不平,现给出一个数组代表此国家某纬度上均匀分布的N座山的海拔高度H[i](任意两座山高度不同),已知每座山的山顶上都有一座哨塔,若两个哨兵分别位于第i、j(i<j)座山上,当且仅当两人所在的山比两人之间所有的山都高时,这两个哨兵可以相互监视,M国的防守能力大小为相互监视的哨兵对数。H国早已对M国虎视眈眈,H国的皇帝希望黑魔法师们可以在M国的某两座山之间放置一块巨大的屏障,M国的哨兵不可通过该屏障互相监视。皇帝想让你告诉他最优的屏障放置位置,你是皇帝手下最信任的军师,现在需要你帮助皇帝计算最优的屏障放置位置和最大的防守力减少量。

输入描述:

第一行包含一个正整数T(T≤20)。
对于每组数据,第一行包含一个正整数n(2≤n≤50000)。
接下来n个不同的正整数,H1,H2,H3,…,Hn(0≤Hi≤109)分别代表横截面上每座山的海拔高度。
(读入数据比较大,建议使用scanf而不要使用cin读入)
对于60%的数据,n≤500
对于80%的数据,n≤5000
对于100%的数据,n≤50000

输出描述:

每组数据输出一行形如“Case #N: X C”,N代表当前是第N组数据(从1开始),X代表屏障放置在第X座山前可使M国的防守能力下降最多, 此时减少量为C。若有多种方案使得减少量为C,那么输出最小的X对应的方案。
示例1

输入

复制
2
3
2 1 3
5
4 5 2 6 3

输出

复制
Case #1: 2 2
Case #2: 3 2
思路:很少用栈,这两天才开始刷与栈相关的题目,这是第二道吧,这道题并不会做,惭愧。
好了,说思路。 题目是说我们把一块巨大的屏障放进去,大家想想,是不是放进去之后原来的一整个哨塔就变成了以屏障为中心,左右两个独立的区间哨塔。 是的,这是解决这道题
的关键。 如果我们一开始知道所有哨塔的防御力,然后又知道屏障两边两个独立的区间哨塔的屏障,总的减去这两个,剩下的不就是放了这个屏障之后消去的防御力吗
没错,是的。 问题是我们怎么快速求出来总防御力,和插入屏障之后两个单独区间的防御力。
下面介绍几个用到的数组:(数组下标从1开始)
v1[i]:1-i所有的防御力总和(往前看)
v2[i]:i-N所有的防御力总和(往后看)
有了这两个数组之后,假设我们在位置x前放置屏障,那么此时得到的防御力总和就是 v1[x-1]+v2[x] (仔细想想)
所以减少的防御力就是v1[N]-(v1[x-1]+v2[x] (v1[N]=v2[1]=总防御力)
知道这个之后自己理解代码:
#include<iostream>
#include<cstdio>
#include<stack>
#include<cstring>
using namespace std;
const int maxn=50000+5;
int a[maxn];//输入
int v1[maxn],v2[maxn];//v1存储从后往前看的总防御力 v2存储从前往后看的总防御力
stack<int>s;
int main()
{
    int T;
    int ca=1;
    scanf("%d",&T);
    while(T--)
    {
        memset(v1,0,sizeof(v1));
        memset(v2,0,sizeof(v2));
        while(!s.empty()) s.pop();
        int N;
        scanf("%d",&N);
        for(int i=1;i<=N;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=N;i++)//从后往前看
        {
            int tmp=0;
            v1[i]=v1[i-1];
            while(!s.empty()&&s.top()<a[i])
            {
                tmp++;
                s.pop();
            }
            if(!s.empty()) v1[i]+=tmp+1;//证明还有一个能和自己监视
            else v1[i]+=tmp;
            s.push(a[i]);
        }
//        for(int i=1;i<=N;i++) cout<<v1[i]<<" ";cout<<endl;
        while(!s.empty()) s.pop();
        for(int i=N;i>=1;i--)//从前往后看
        {
            v2[i]=v2[i+1];
            int tmp=0 ;
            while(!s.empty()&&s.top()<a[i])
            {
                tmp++;
                s.pop();
            }
            if(!s.empty()) v2[i]+=tmp+1;
            else v2[i]+=tmp;
            s.push(a[i]);
        }
        int ans=-1,id;
        for(int i=1;i<=N;i++)
        {
            int x=v1[N]-(v1[i]+v2[i+1]);
            if(x>ans)
            {
                ans=x;id=i+1;
            }
        }
        printf("Case #%d: %d %d\n",ca++,id,ans);
    }

    return 0;
}

 

posted @ 2019-07-12 13:08  执||念  阅读(327)  评论(0编辑  收藏  举报