• Education codeforces Round 96 D题 -string deletion (搬运自自己的洛谷博客)


    本题洛谷有搬运,题号CF1430D

    题意概括


    ------------
    给一个0/1串,每次选择删除一个字符,然后将整个串前面的一个连续0串或者连续1串(0串在前就删0串,1串在前就删1串)给删除,这两个删除算一次操作,求删空字符的时候最大操作数量是多少

    ------------
    分析

     



    先摆样例方便下面的说明
    下文中叙述时均定字符串的最左端是第一位,不按照字符串实际下标表示,即本文字符串的第一位是1。

    eg1: 10010011

    求最大操作数量,所以每次尽量删除更少的字符

    对于一个串比如[eg1],如果删除了某个单字符(eg1的第4位),当它在中间的时候,删掉它有可能会把左右两个串给合并,使得它们可以被一次性删除(删它产生1000011,消除了前缀1就是000011,然后下一次必定会把这一长串0给删掉),对以后造成不利影响。

    模拟一下: 10010011->000011->1(选某个1)或11(选某个0)->空 ---共3次

    当这个单字符是前缀的时候(比如eg1的第1位,删了它变成0010011,接着前缀消除变成10011,消了3位),如果删掉它,有可能它紧邻的后面存在一个连续串,那么删掉它就会有一个连续串直接被删除,一次性删掉了很多字符。

    模拟一下:10010011->10011->11->空 ----共3次

    综上我们不该直接删除单字符。那么先删除多字符中的字符呢?

    eg2 0001010

    对于前缀是多字符(eg2的前三位),只选择它自己中间的任意字符显然最优,这样它只会删掉自己,而不会多删任何一个字符。如果选择了它之外的字符,显然不但删除了它,还在它之外又多删除了一个,使得答案更劣。

    eg3 10010011(其实还是eg1)

    对于前缀是单字符(eg3的第一位),优先选择利用后面的长串(长串即长度大于等于2的串)删一个字符然后让这个单字符作为前缀被删掉;

    模拟过程: 10010011->010011->1011->01->空 . . .一共删了4次

    这样每次前缀为单字符的时候只删掉了两个字符,显然最优,否则就是我们在eg1中提到过的。

    当然,只剩下单字符的时候,只能一次删除两个了,删到最后就好。没的玩。

    具体实现

     


     

    我们的贪心只和块的长度有关,所以我们把字符串化作块集,用数组储存每个块的长度,然后按照上述贪心,如果前缀是单字符,用一个指针q(下标)来往后寻找大于2的块,没有的话就全都是单字符,一次减2长度来干掉他们并累计答案。如果前缀是多字符,更好办,直接删掉这个块,累计一次答案。

    代码如下

     


     

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int t;
     4 char s[200100];
     5 int bl[200100],blo;//第i块里有多少个数,有多少块
     6 int main()
     7 {
     8 cin>>t;
     9 for(register int j=1;j<=t;j++)
    10 {
    11 memset(bl,0,sizeof(bl));blo=0;
    12 int n;int q=1;int ans=0;
    13 scanf("%d",&n);scanf("%s",s);
    14 for(register int i=0;i<n;i++)
    15 {
    16 blo++;
    17 while(1)
    18 {
    19 bl[blo]++;
    20 if(s[i+1]!=s[i]) 
    21 break;
    22 i++;
    23 }
    24 }
    25 int flag=0;
    26 for(int i=1;i<=blo;i++)//现在已经可以用块来处理这件事情了 
    27 {
    28 if(flag)//只剩单块,每次两个 
    29 {
    30 ans++;i++;
    31 continue;
    32 }
    33 if(bl[i]>=2)//对于一个大于等于2的联通块,删除自己 
    34 {
    35 ans++;
    36 }
    37 else
    38 {
    39 while(q<i||(q<=blo&&bl[q]<2))q++;//后置指针如果没有跟上枚举让它先跟上枚举,然后往后面找大于等于2的块
    40 if(q>blo)//如果q直接飞了出去,说明后面没有大于2的块了,直接跳过前缀的两个单字符块 
    41 {
    42 flag=1;
    43 ans++;
    44 i++;
    45 }
    46 else{//如果存在大于等于2的块,这个块少1个 
    47 bl[q]--;
    48 ans++;
    49 }
    50 }
    51 }
    52 printf("%d
    ",ans);
    53 }
    54 return 0;
    55 }
    56 /*
    57 1
    58 6
    59 100100
    60 
    61 */
    View Code

     

     


     

     



    ps:
    **eg4 101100**

    ( 其实长串的定义也可以是长度大于2的串,比如说eg4,如果定义长串是大于2的串,现在没有大于2的串了,我们只好一个个(两个两个)删,显然可以删三次

    101100->1100->00->空

    如果说我们定义长串是大于等于2的话,会先选择第三位的1,然后删前缀1,得到0100,然后再删第三位的0,接着前缀0没了,剩下10,依然是一步删除了。

    101100->0100->10->空

    从上边我们发现,不管我们定义长串是大于还是大于等于2,删到它不是长串的时候(各自定义下的长串),再删它的时候(按照每次只看前缀再选择删除的贪心原则)最多也最少减掉两个字符,真的定义大于2的块是长串的话,手动模拟是没有问题的,只是如果定义大于2的块是长串的话,我的程序会认为有两个字符的块为一个字符,一次跳过两个块的时候其实删去三个字符,会使答案变少,所以在我的操作中,大于等于2的串是长串 )

    ------------

  • 相关阅读:
    Flutter 步骤进度组件
    程序员到底要不要学习框架、库和工具
    Flutter 吐血整理组件继承关系图
    超过百万的StackOverflow Flutter 问题
    Flutter 实现网易云音乐字幕
    Flutter 实现虎牙/斗鱼 弹幕效果
    Flutter AbsorbPointer 与 IgnorePointer的区别
    《Flutter 小技巧》一行禁用App,一行置灰App,致敬
    Python 任务自动化工具:nox 的配置与 API
    Flask 作者 Armin Ronacher:我不觉得有异步压力
  • 原文地址:https://www.cnblogs.com/mikuo/p/13816029.html
Copyright © 2020-2023  润新知