zoukankan      html  css  js  c++  java
  • FZU

    海边躺着一排咸鱼,一些有梦想的咸鱼成功翻身(然而没有什么卵用),一些则是继续当咸鱼。一个善良的渔夫想要帮这些咸鱼翻身,但是渔夫比较懒,所以只会从某只咸鱼开始,往一个方向,一只只咸鱼翻过去,翻转若干只后就转身离去,深藏功与名。更准确地说,渔夫会选择一个区间[L,R],改变区间内所有咸鱼的状态,至少翻转一只咸鱼。

    渔夫离开后想知道如果他采取最优策略,最多有多少只咸鱼成功翻身,但是咸鱼大概有十万条,所以这个问题就交给你了!

    Input
    包含多组测试数据。

    每组测试数据的第一行为正整数n,表示咸鱼的数量。

    第二行为长n的01串,0表示没有翻身,1表示成功翻身。

    n≤100000

    Output
    在渔夫的操作后,成功翻身咸鱼(即1)的最大数量。

    Sample Input
    5
    1 0 0 1 0
    3
    0 1 0
    Sample Output
    4
    2
    Hint
    对于第一个样例,翻转区间[2,3],序列变为1 1 1 1 0。

    对于第二个样例,翻转整个区间,序列变为1 0 1。

    分析:将连续整块的1和0压缩成一个数字,首先我们知道若对于一个块区间,如果全是1,那么肯定不能单独翻,如果一块区间全是0,那么可能可以全部翻过来,但是是否会和隔着的一块0区间一起翻过来的收获更大呢?也就是说,要如果翻,要么是一整块0区间翻,要么是010这样的区间翻,不会出现01或者10这样左边或右边是1的情况翻转。那么规整为单独的数字后,如果是0,说明翻过来肯定有这个区间长度的收获,如果是1,翻过来就有区间长度的减少。因此1的块收获为负数,0块收获为整数,对于一串有正有负的数列,我们选择连续的几个值求和使其价值最大,这就是最大子段和问题。

    最大子段和问题: 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。

    于是,我们利用动态规划的方法以O(n)的复杂度算出最优结果。

    如果当前取到的求和大于0,那么说明收获是正数,加上下一个数,即使下一个数可能是负数使收获减少,但难免再之后的数是更大的收获能把一段负值挽回。

    而当前值为0或负值时,这样的情况没有延续下去或者用后面的值挽回的必要,直接舍去前面这段区间,取一个为正数的开头作为新区间。因为之前已经说明了,我们不会有一个10这样的开头,1是负值,取10的翻转区间不如取一个0的区间作为端点。

    以这样的方式不断取值求和,其中一只记录这样区间取舍的最大值。即最终的ans。

    注意题目要求必须翻一条鱼,不能不翻,因此全部是1的时候,要减去一个1的个数输出。

    #include<stdio.h>
    #include<vector>
    using namespace std;
    const int maxn=1e5+10;
    int a[maxn];
    vector<int>q;
    int main()
    {
        int n;
        while(scanf("%d",&n)!=EOF)
        {
            q.clear();
            int tmp=-1,one=0;
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&a[i]);
                if(a[i])one++;
                if(tmp==-1)
                {
                    tmp=1;
                    continue;
                }
                if(i!=1&&a[i]!=a[i-1])
                {
                    if(a[i-1])tmp=-tmp;
                    q.push_back(tmp);
                    tmp=1;
                }
                else tmp++;
            }
            if(a[n])tmp=-tmp;
            q.push_back(tmp);
            if(n==one)///必须翻一次
            {
                printf("%d
    ",n-1);
                continue;
            }
            int ans=0;
            tmp=0;
            for(int i=0;i<q.size();i++)///最大子段和
            {
                if(tmp>0)tmp+=q[i];
                else tmp=q[i];
                if(tmp>ans)ans=tmp;
            }
            printf("%d
    ",one+ans);
        }
    }
    
  • 相关阅读:
    微信運動步數
    JS逐页转pdf文件为图片格式
    js学习笔记]PDF.js专题
    PDF轉圖片流並jquery顯示到頁面
    使用 pdf.js 在网页中加载 pdf 文件
    使用pdfobject.js实现在线浏览PDF
    Echarts的使用
    C# ffmpeg 视频处理
    C#文件/文件夾壓縮,解壓縮
    epplus插入圖片/鏈接
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11135751.html
Copyright © 2011-2022 走看看