Codeforces Round #809 (Div. 2), problem: (C) Qpwoeirut And The City

一.题意 

给你n个排成一排的建筑,每个建筑有一个高度指标h[i],我们定义当i>=2 && i<=n-1的时候的时候,若h[i]>=h[i-1]&&h[i]>=h[i+1]  那么这个建筑物是酷炫的!

现在给到你砖瓦可以在任何一个建筑上加盖楼层,但是不能将已有的楼层拆卸。

你的目的是使用最少的砖瓦,使得酷炫建筑数量最大化。

二.思路

最下面有个总结,觉得下面的解释太长的小伙伴可以直接看下面的总结,如果有不理解的地方可以再看我的详尽解释。

分情况来讨论

①如果n是奇数的话,那2~n-1的一个建筑一定是一个高低高低高低…………高的交错排序,因为如果一共有n个建筑,最终一定会有(n-1+2)/2(上取整公式 (n+m-1)/m)个酷炫建筑,所以一定最终是个这样的排列,自己可以画一画看一看(我自己有一个毛病就是老是空想,然后也许想的是错误的,但是没有自己拿笔写一写画一画,就觉得是正确的,犯了“我以为”的错误)那么这种情况只需要枚举一变看看需要多少个砖瓦就可以变成这种排列了。

②如果n是偶数的话,如果一共有n个建筑,最终一定会有n/2(上取整公式)个酷炫建筑。

【为了文章简洁性 这里 把酷炫建筑简称为 高 不酷炫建筑简称为 低】

而这些酷炫建筑一定是间隔出现的 ,经过初步思考,我觉得可以是高低高低……高低或者是低高低高……低高两种方案,但是又看了一下样例的这个:

6
1 10 1 1 10 1

发现可以出现 1次 两高中间间隔两个低的情况 这样也是满足有n/2个酷炫建筑的。

但是这种情况开头第2个建筑必定为高,也就是酷炫建筑,并且我们可以发现若第i个建筑是高,第i+1和i+2个建筑都是低的话,其实建筑的排列节奏是从高低高低……高低第2个建筑为高之后交替排列  在i+2处换成了低高低高……低高(第2个建筑为低之后交替排列),所以我们利用前缀和的思想分别记下来 前i个 建筑利用  低高低高……低高 节奏和 高低高低……高低 节奏的一个所有砖块数量,然后在每个i假设出现第i个建筑是高,第i+1和i+2个建筑都是低(节奏转变)这一情况,进行砖块数目的一个计算,计算公式是ans1[i]+ans2[n-1]-ans2[i+1],其中ans1[i]是把前i个建筑变成高低高低……高低一个排列形式所用的砖块数,ans2[i]是把前i个建筑变成低高低高……低高一个排列形式所用的砖块数,那么ans2[n-1]-ans2[i+1]就是把i+1个建筑之后的建筑变成低高低高……低高一个排列形式所用的砖块数,因此ans1[i]和ans2[n-1]-ans2[i+1]加起来就是整个的i假设出现第i个建筑是高,第i+1和i+2个建筑都是低(节奏转变)一个砖瓦数。

因此这种情况的话就枚举一个转折点就行啦!

总结一下:

两大情况: 

1.n为奇数  要达到酷炫建筑最大化的一个排列形式固定,直接枚举算ans。

2.n为偶数,三种情况:

①第2个建筑为低,那么排列形式也是固定了,直接可以得到ans,也就是前缀和ans2[n-1]。

第2个建筑为高,第n-1个建筑为低,那么排列形式也是固定了,直接可以得到ans,也就是前缀和ans1[n-2]。

③第2个建筑为高,第n-1个建筑为高,那么这之间一定有一个转折点i使得第i个建筑是高,第i+1和i+2个建筑都是低,这时候会进行一个从高低高低……高低第2个建筑为高之后交替排列  在i+2处换成了低高低高……低高(第2个建筑为低之后交替排列)一个转换,因此我们利用前缀和数组,分别记下分别利用这两种节奏在做到第i个建筑的时候的一个所用砖瓦数,然后再枚举这个转折点就行。

看到这里快要结束啦,很开心你可以点进来,如果文章对你有帮助的话,可以顺手点一下下面的小手手👍吗?你的支持是对我创作最大的鼓励 !

三.代码


#include <iostream>
#include <cstring>
#include <algorithm>
#include<map>
using namespace std;
map<int,int> st,cnt; 
const int N = 2e5+10;
typedef long long LL;
int n;
int a[N];
LL ans1[N],ans2[N];

void solve()
{
    
    cin>>n;
    for(int i=1;i<=n;i++)
    cin>>a[i];
    for(int i=1;i<=n+2;i++)
    {
        ans1[i]=0;
        ans2[i]=0;
    }
    LL ans=0;
    if(n&1) //n为奇数
    {
        for(int i=2;i<=n-1;i+=2)
        {
            if(a[i]<=a[i-1]||a[i]<=a[i+1])
            {
                ans+=max(a[i-1],a[i+1])-a[i]+1;
            }
        }
        
    }
    else//n为偶数
    {
        ans=2e14;//一定不要忘记初始化ans为数据最大值!
        for(int i=2;i<=n-1;i+=2)//这里的i代表把第i个变为高,所以间隔取数
        {
            if(a[i]<=a[i-1]||a[i]<=a[i+1])
            {
                ans1[i]+=max(a[i-1],a[i+1])-a[i]+1;//高低高低节奏下前i个使用的砖瓦数
            }
            if(i>=4) ans1[i]+=ans1[i-2];
        }
        for(int i=3;i<=n-1;i+=2)//这里的i代表把第i个变为高,所以间隔取数
        {
            if(a[i]<=a[i-1]||a[i]<=a[i+1])
            {
                ans2[i]+=max(a[i-1],a[i+1])-a[i]+1;//低高低高节奏下前i个使用的砖瓦数
            }
            if(i>=3) ans2[i]+=ans2[i-2];
        }
        for(int i=2;i<=n-4;i+=2)
        {
            ans=min(ans,ans1[i]+ans2[n-1]-ans2[i+1]);情况③的最小值
        }
        ans=min(ans,min(ans1[n-2],ans2[n-1]));//情况①②③取最小值
    }
    cout<<ans<<endl;
    
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        solve();
    }
}
  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值