码迷,mamicode.com
首页 > 其他好文 > 详细

胜者为王(贪心)

时间:2019-08-05 20:28:14      阅读:140      评论:0      收藏:0      [点我收藏+]

标签:define   its   合并   最大   double   扩展   reg   png   git   

题面

技术图片

技术图片

技术图片



$ solution: $

似乎是燕山楠学长的一道原创题?那还是将题目完整的放上来吧!

(下文中蓝色表示题目中不会被筛掉的点,红色表示会被筛掉的点)首先我们知道如果存在两个l较小的蓝色点中间有一个较大的红色点,我们的 $ ans $ 长度一定不能超过蓝色点围成区间的长度,否则我们筛不掉这个红色点(两边较小的蓝色点会被先筛掉)。我们假定对于某一个红色点,它最近的两个比它小的蓝色点为其左右限制,然后我们知道每一个红色点都存在这样的左右限制(边界也算一对限制)。因为 $ x $ 不能超过这个限制长度,我们可以知道题目所求即为最小的那一对限制

正解: (请时刻牢记我们所求为最小的那一对限制)我们先搞清楚两个关键性质:

第一个性质:我们其实可以发现如果有一段连续红色点,其中真正可以求得最小限制的只有最大的那个红色点,因为这一段点连续,我们向两边扩展寻找较小蓝色点时最高的那个红色点找的左右限制一定最近,而且这一对限制是必要的,没有这一对限制这个最高的红色点就不能被删除。

第二个性质:我们还可以发现如果有两个红色点中间只有蓝色点,如果这些蓝色点比两端的红点都大,它们就无法作为限制。然后这两个红色点中只有较大的那一个可以扩展为最小限制,因为它们中间的点都不是边界限制,于是它们都会向外面扩展,(同第一个性质)向外面扩展时大的那个红色点找的左右限制一定最近!且这对限制必要!

然后我们发现如果我们先去找红色点的右边限制,我们建一个指针初始为第一个红色点,然后出发向右找,会碰到四种情况:1. 如果碰到一个比它小的蓝色点就终止,将右边限制长度计入当前指针指向的点。2. 如果碰到一个比它大的蓝色点就计入右边限制长度+1。3. 如果碰到比它小的红色点或着比它大的红色点,一定可以转换为上面两种性质,我们将指针移到两个中较大的红色点,然后继续往右找即可(注意如果指针转到右边那个点,限制长度要变成初值!因为找的是右边!)。注意最后跑完还要回收,有一个点可能没有找到右边界!(考场没写70pts QAQ)

然后我们再同理跑一遍左边的即可,合并时只有左右边界都确定的点可以被计入答案!注意初值和清零操作!



$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<bitset>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define rg register int

using namespace std;

int n,ans;
int a[10000005];
int b[10000005];
bool s[10000005];
bool l[10000005];
bool r[10000005];

inline int qr(){
    register char ch; register bool sign=0;
    while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
    rg res=ch^48;
    while(isdigit(ch=getchar()))res=res*10+(ch^48);
    if(sign)return -res; else return res;
}

int main(){
    freopen("robber.in","r",stdin);
    freopen("robber.out","w",stdout);
    n=qr(); ans=n; //ans初值就是最长
    for(rg i=1;i<=n;++i) a[i]=qr();
    for(rg i=1;i<=n;++i) s[i]=qr();
    rg v=0,res=1;
    for(rg i=1;i<=n;++i){
        if(s[i]==0) {if(a[i]>a[v])v=i,res=1;} //也是一个会被筛的点,判断是否更优
        else{
            if(a[i]>a[v])++res; //不会被筛且比它要大,计入贡献
            else l[v]=1,b[v]=res,v=0,res=1; //找到了比它更小的就更新
        }
    } l[v]=1,b[v]=res,v=0,res=1; //最后回收,考场忘记加了只有70分 Σ( ° △ °|||)︴
    // 还要倒着来一遍
    for(rg i=n;i>=1;--i){
        if(s[i]==0) {if(a[i]>a[v])v=i,res=1;} //同上,这个点更高一定更优
        else{
            if(a[i]>a[v])++res;
            else r[v]=1,b[v]+=res,v=0,res=1; //找到了比它更小的就更新
        }
    } r[v]=1,b[v]+=res,v=0,res=1;//最后回收
    for(rg i=1;i<=n;++i)
        if(l[i]&&r[i])ans=min(ans,b[i]-1); //只有左右限制都在才会更新答案
    printf("%d\n",ans);
    return 0;
}

胜者为王(贪心)

标签:define   its   合并   最大   double   扩展   reg   png   git   

原文地址:https://www.cnblogs.com/812-xiao-wen/p/11305239.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!