• 子序列-反转区间求最长不下降子序列


    题目描述

    小Z有一个01序列A=(A1,A2,A3,…,An)。他可以进行一次操作:选择一个区间L,R将其反转。
    例如,对于序列A=(1,0,0,1,1),当L=2,R=4时,原序列将变为(1,1,0,0,1)。
    小Z希望:通过这一次反转操作,使得这个序列的最长不下降子序列的长度尽可能的大,并想让你输出这个最大值。
    一个序列的不下降子序列定义为:对于一个序列(p1,p2,…,pk)满足1≤p1<p2<…<pk≤n(n≤819200)且Ap1≤Ap2≤…≤Apk。则序列(Ap1,Ap2,…,Apk)为不下降子序列,其中k为这个不下降子序列的长度。

    输入

    一行一个01字符串,表示序列A

    输出

    一行一个正整数,表示最长不下降子序列长度的最大值。

    样例输入

    00111001101
    

    样例输出

    10
    提示
    一种最优策略为:反转区间[3,7],数列将变为(0,0,0,0,1,1,1,1,1,0,1) ,其最长不下降子序列的长度为10。

    在一开始的时候以为反转某一段区间之后,只会影响反转的这一块中0或1的数量与前一段或后一段的0或1的数量
    然后统计出来0或1的块的大小,只反转1…10…0这样的区间,其实是错的
    答案会影响四段
    wrong_code:
    记录反转了这一块后前面0的个数与后面1的数量思路其实是错的

    int n,k;
    int a[maxn];
    struct node{
        int id;
        int ct;
    }b[maxn];
    int main()
    {
        string s;
        cin >> s;
        n = s.size();
        s = "#" + s;
        int cnt = 0;
        int t = 0;
        int bak1 = 0;
        for(int i=1;i<=n;i++){
            ++ t;
            if(i == n || s[i] != s[i+1]){
                b[++cnt].id = s[i] - '0';
                b[cnt].ct = t;
                t = 0;
                if(s[i] == '1') bak1 += b[cnt].ct;
            }
        }
        /*for(int i=1;i<=cnt;i++){
            cout << b[i].id<<' '<<b[i].ct<<endl;
        }*/
        /// debug(bak1);
        if(cnt == 1 || cnt == 2){
            cout << n <<endl;
            return 0;
        }
         
        int pre0 = 0;
        int ans = 0;
        b[0].id = 0,b[0].ct=0;
        for(int i=1;i<cnt;i++){
            if(b[i-1].id == 0) pre0 += b[i-1].ct;
            else bak1 -= b[i-1].ct;
            ans = max(ans,bak1 + pre0);
            if(b[i].id == 1 && b[i+1].id == 0){
                ans = max(ans,pre0 + b[i+1].ct + bak1);
                /*debug(pre0);
                debug(b[i+1].ct);
                debug(bak1);*/
                // cout << pre0 + b[i+1].ct + bak1 <<endl;
            }
        }
        cout << ans <<endl;
        return 0;
    }
     
    /**************************************************************
        Problem: 13769
        Language: C++
        Result: 答案错误
    ****************************************************************/
    

    ac_code:

    int main()
    {
        string s;
        cin >> s;
        n = s.size();
        s = "#" + s;
        int t1,t2,t3,res;
        t1 = t2 = t3 = res = 0;
        for(int i=1;i<=n;i++){
            if(s[i] == '0'){
                ++ t1;
                t2 = max(t1,t2);
                t3 = max(t2,t3 + 1);
                res = max(res,t3);
            }else{
                t2 = max(t1,t2 + 1);
                t3 = max(t2,t3);
                res = max(t3,res + 1);
            }
        }
        cout << res <<endl;
        return 0;
    }
     
    /**************************************************************
        Problem: 13769
        Language: C++
        Result: 正确
        Time:40 ms
        Memory:15588 kb
    ****************************************************************/
    

    追更:
    其实这道题和acwing上面,这道题是一样的
    在这里插入图片描述
    应该考虑答案影响的四段

    最优质一定是在00000111110000011111
    这种情况下的
    我们用s1记录00000的最长长度
    用s2 记录0000011111的最长长度
    用s3 记录000001111100000的最长长度
    用s4 记录00000111110000011111的最长长度
    最终答案就是s3,s4中最大值
    O(n)
    该题code:

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    int main()
    {
        int n; cin >> n;
        int s1 = 0,s2 = 0,s3 = 0,s4 = 0;
        for(int i=1;i<=n;i++){
            int x;scanf("%d",&x);
            if(x == 1){
                ++ s1;
                s3 = max(s2 + 1,s3 + 1);
            }else{
                s2 = max(s1 + 1,s2 + 1);
                s4 = max(s3 + 1,s4 + 1);
            }
        }
        cout << max(s3,s4) <<endl;
        return 0;
    }
    
  • 相关阅读:
    基于Flask开发web微信
    爬取实例
    scrapy框架学习之路
    scripy
    wtforms
    由testcase数据之分析
    无用之flask学习
    无用之flask
    无用之学matplotlib,numpy,pandas
    jsp_1
  • 原文地址:https://www.cnblogs.com/PushyTao/p/15101036.html
Copyright © 2020-2023  润新知