A - Salty Fish FZU - 2253

咸鱼翻身算法
本文介绍了一种解决咸鱼翻身问题的高效算法,旨在通过翻转特定区间内的状态,实现咸鱼(01串中的1)数量的最大化。文章详细解释了算法思路,并提供了一个具体的示例和代码实现。

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

给定一些长度为n的01串,要求经过一次区间内的翻转后得到1
的最大数量。
转换一下思路,既然要经过翻转一个区间得到更多的1,我们先
假设该区间为[L,R],那么既然要得到更多的1,那么我们可以假设这个最大数量的1的个数为Max个,于是这个Max应该由2种1
组成,第一种1,原来01串中已经包含了得1,例如案例1中,一个长度为5的01串,10010,还未经过翻转的串,一共有2个1,
Max中就有可能有这种1,第二种1,经过一个区间[L,R]翻转后,
0变成1,而得到的1,自然[L,R]区间内,可能还会包括一些1,这些1就会变成0,那么这些1,自然是第一种1,这说明1的个数减少了,于是既然我们要经过这样的一次操作让1的数目变得最多,显然就是翻转区间[L,R]后,让增多的1的数目达到最多,然而这个过程中也很有可能会减少1,那么我们就是要记录,对于一个区间,我所能得到的最大的增多的1的个数,最后的结果就是,原来的1加上最后经过翻转后得到的1的个数。然而问题在于如何确定这个区间呢,显然我们是要通过所能增多的1的个数来确定该区间的,那么我们就可以对每一个可能的区间来进行记录所能增多的1的个数,然而,可能的区间会有n^2个,要记录1的个数还需要不断遍历,显然最后时间复杂度会变成O(n^3),我们再想一下确定这个区间可行的特征,即这个区间可以使得1的个数增多,而不是减少,那么对于这个01串,10010,因为第一个为1,显然我们不能够把第一个数放到这个区间里去,因为这会让我们的1的个数减少,意思就是,必定L > 1,(这里假设第一个数下标为1,第n个数下标为n),再检查第二个数0,显然我们可以把0添加进去这样的区间,因为这个时候的区间能够让我们的1增多,然后再加入第三个0,这时候我们确定了区间[2,3],那么遇到第四个数1的时候该怎么办呢,显然这个时候把它加入进去会让原来可增加的1的个数减少,但是,我们可以再想想,即使它加入进去,1的个数也还是会是增多的,因为我们之前已经加入了2个0了,这个时候加入1就相当于是这个区间里面只有1个0了,然而我们不能抛弃这个区间,为什么呢,因为它并没有让我们的1的个数减少,相反,如果我们在这个1后面能够找到更多的0的话,就又可以让最后我们得到的1的个数增多了,可这里也会有一个疑问,就是如果我们在这个1后面找不到更多的0呢,反而是找到更多的1怎么办呢,这个时候我们就可以想想了,既然担心这种情况,索性定义一个变量每一次添加一个元素入区间后记录一下经过区间翻转后所能增多的1的个数,同样的,如果我们发现当前的区间让1的个数减少了,我们当然就不取这个区间了,便重新开始去确定区间,例如这样的01串:1001111000。接着再给出代码:

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <set>
#include <map>
#include <list>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
int main()
{
    int n, x;
    while (scanf("%d", &n) != EOF){
        int Max = -INF, sum = 0, c = 0;//用c记录当前区间可使最后1增多的个数
        for (int i = 1; i <= n; i++) {
            scanf("%d", &x);//x只能为0或1
            if (x == 1)//用sum记录一下原来的串的1的个数
                sum++, c--;//x为1的话,该区间使得最后可增多的1的个数减少了
            else//x为0的话,该区间使得最后可增多的1的个数增多了
                c++;
            Max = max(Max, c);//取一个最大的可增多的1的个数为Max
            if (c < 0)//如果说该区间只会使得增多的1的个数减少,那么显然我们可以舍弃这个区间了
                c = 0;
            /*注意这里的Max可以为负数1,因为翻转是必须的,而如果01串全为1的话
              必定1的个数只会减少
            */  
        }
        printf("%d\n", sum + Max);//最终的1的个数必定由初始1的个数加上最后翻转增多的1,当然Max可能为-1
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值