寒假第一天:贪心1

1.第一题:Problem - B1 - Codeforces:Painting the Array I

        题意:输入一个长为n(1 <= n <= 1e5) 的序列ai,你要维护两个栈 B 和 C,初始都为空。 现在顺次扫描序列 ,对于每个ai:有两个操作:

1. 要么将其压入 B 中

2. 要么将其压入 C 中

        问: seg(B)+seg(C)的最小值是什么? 其中 seg(D)表示把序列 D 中相邻 且 相同的项合并后所剩的元素个数,比如  seg{1,1,2,3,4}=seg{1,2,3,4}=4.

        我们肯定是希望每个数放进栈时,栈顶都会有一个相同的元素,也就是当每个数选择要放入的栈时,最优先选择有相同元素的栈顶的栈放入,我们能证明每次都这么做是最优的。                 (证明:若最优解不是这样做,考虑除了我们正在进行的操作,前面全部操作与最优解一致的策略:最优解选择了放入无相同元素的一个,我们选择放入有相同元素的一个,以后我们的每次操作与最优解一致,最优解最多能凭借该操作消去一个,而我们肯定能消去一个。)

        接下来考虑其他的情况:如果栈顶没有相同的元素怎么办?可以随便放吗,应该不可以,为什么?想了一下:我们每次放时,肯定希望被盖住的栈顶元素在以后的序列中不再出现,如果能够做到这点,我们这种策略肯定有效。到此我们发现了一种思路:每次放入元素时,考虑两个栈的栈顶元素在后面序列中的出现情况。

        

        这是因为,对于   两个栈顶元素都与要放元素不相同    的情况,无论放入哪个栈,在本次情况下总和都会加一,只有对未来贡献最好的放法,才是最优的。而放入每个栈的不同在于:遮住了哪个未来       最难出现      的元素,留住了哪个未来     最易出现      的元素。接下来我们就要考虑,怎么说明,评定与计算   最易出现(或最难出现)?

        很容易想到与出现次数联系在一起。不过我们要稍微严谨一些,此时再想想,比较每个数的出现次数可以吗?如果不可以,为什么不可以,能构造出反例吗?如果可以,怎么证明?

        我思考了一下,感觉还是有点复杂。原因是对于后面直到栈B栈顶元素(设为b)和栈C栈顶元素(c)之前的元素该放哪还是未知的:会不会把原本多的栈顶遮住,还是一直选择少的栈顶?

        既然如此,我们改善一下情况(先用已知的东西来代替未知的东西,考虑最简单的情况):设我们现在要放的元素为a,B栈顶b,C栈顶c,设后面出现的次数b要多于c,考虑后面的序列为 c , m1 , c , m2,  c , m3 , b, b, b ,b. (m1 , m2 , m3互不相同)

        有图:

        可以发现,当m1,m2,m3,...的数量逐渐增大时,这种方法会逐渐失去它的优越性。也就是说,这种方法是行不通的。

        为什么呢?思考过后得出答案:因为后面的序列会采用同样的方式,其中本可以消去的相同元素却没有消去,而换来的代价只是让b多消去了一个。此时在观察最优解,在这种情况下的最优解应该是把a放到B栈中接下来把所有的c放入C栈,m1,m2,m3放入B栈,四个b放哪里都行。

        

        我们可以看到,在最优解中,考虑了局部最优的方法,不是让最多的b放一起,而是让最先出现的c放在了一起。我们或许可以得到启发。

        我们接下来考虑比较栈顶元素在以后序列出现时间的先后。我们直接来看一下流程:对于放B栈的情况:(a,c)表示B栈放a,C栈放c。即(a,c)与(b,a)的比较。在b,c第一次出现之前,两者无差别。对于在b,c出现后的序列,两者也没有区别(因为后面再也没有b,c出现,对于b,c都无法消去)。我们只需要分析b,c出现的序列即可。我们假设b比c先出现,如果b,c都能消去,那么对于两种情况都需要从a到b(或c)第一次出现的序列全部放入C(或B)栈,由于b所在位置更靠前,因此b元素更易消去,并且b消去后对于后面的序列可自由放置。

        对于两个元素都不相同的情况讨论完毕了,接下来讨论两个元素都不相同的情况,思考一下可以得到解决方案:随便放。因为无论放入哪个栈得到的栈顶元素是相同的,不会因为未来的序列放置情况的不同而不同。

        总结一下:两个元素相同时:随便放。

                          两个元素不同时:(1)若有一个栈顶与当前元素相同:放入相同的

                                                       (2)若没有一个元素与当前元素相同:放入优先出现的

        接下来是代码:

        

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],b[N],k[N],at=-1,bt=-1,ai=0,bi=0;
int fr[N],ne[N];

int main(){
    int n;
    scanf("%d",&n);
    memset(fr,-1,sizeof fr);
    for(int i=1;i<=n;i++){
        scanf("%d",&k[i]);
        if(fr[k[i]]==-1)    fr[k[i]]=i,ne[i]=n+1;
        else{
            ne[fr[k[i]]]=i;
            ne[i]=n+1;
            fr[k[i]]=i;
        }
    }
    // for(int i=1;i<=n;i++)
    //     printf("%d:%d\n",i,ne[i]);
    
    for(int i=1;i<=n;i++){
        // printf("%d--%d\n",ai,bi);
        int t=k[i];
        if(at==-1 || bt==-1){
            if(at==-1){
                a[++at]=t;
                ai=i;
                continue;
            }
            if(t==a[at]){
                ai=i;
                continue;
            }
            if(bt==-1 && t!=a[at]){
                b[++bt]=t;
                bi=i;
                continue;
            }
        }
        if(a[at]==b[bt]){
            if(t!=a[at])    a[++at]=t;
            ai=i;
            continue;
        }
        if(a[at]!=b[bt]){
            if(a[at]==t){
                ai=i;
                continue;
            }
            if(b[bt]==t){
                bi=i;
                continue;
            }
            if(ne[ai]>ne[bi]){
                a[++at]=t;
                ai=i;
                continue;
            }
            else{
                b[++bt]=t;
                bi=i;
                continue;
            }
        }
    }
    // for(int i=0;i<=at;i++)
    //     printf("%d  ",a[i]);
    // printf("\n");
    // for(int i=0;i<=bt;i++)
    //     printf("%d  ",b[i]);
    // printf("\n");
    printf("%d",at+bt+2);
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值