题解 CF688A 【Opponents】

题目传送门

好久没写题解了,今天正好有空(主要是在家里待的都要发霉了 ),就写篇水题题解


(第一版)题解部分

言归正传。这道题其实可以看做是一道最大子段和的模板题,对于这道题,我的思路是:先定义一个数组a,用来标记小白是否获胜,,并全部初始化为100-100(为什么?先设个悬念),然后读入时查找每一个字符串里是否有00,如果有,就在a数组中相应位置标上11,其余保持原状。接着暴力求最小子段和,因为要求连胜场数,所以前面的初始化就派上用场了:
因为如果进行一般的求最大子段和,00会被纳入其子段中,而如果把100-100纳入进去,肯定不是最优解,在比较求maxmax时会被舍弃,所以不会对结果造成影响。
至于为什么要定义100-100,是因为d<100d < 100,考虑最坏情况,若11个数组里有999911,剩下的100-100也能起到很好的作用来使其被舍弃。
求完之后,直接输出结果就是了。
Code:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e2 + 10;  //个人习惯,不喜勿喷

int a[maxn], ans;   //定义
int main ()
{
    int n, d;
    cin >> n >> d;
    for(int i = 0;i < d; ++i)
        a[i] = -100;  //初始化

    for(int i = 0;i < d; ++i){
        string s;
        cin >> s;
        for(int j = 0;j < n; ++j)
            if(s[j] == '0')  
            {
                a[i] = 1;    //在a数组里标记小白是否获胜
                break;   //降低时间复杂度(虽然也加快不了多少)
            }
    }

    for(int i = 0;i < d; ++i){
        int sum = 0;
        for(int j = i;j < d; ++j){
            sum += a[j];
            ans = max(ans,sum);  
        }
    }      //暴力求子段和

    cout << ans << '\n';  //ans能被直接输出是因为我在a数组里标记的数是1,所以子段和就是连胜场数
    return 0;
 } 

你以为这篇题解结束了?NONONO


(第二版)如何求最大子段和

这里给刚接触动态规划的OIers讲一下如何求最大子段和
1.暴力算法
两层循环,分别枚举头和尾,时间复杂度为O(n2)O(n^2)
核心代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e3 + 10;

int a[maxn], n, ans;
int main ()
{
    cin >> n;
    for (int i = 0;i < n; i++)
        cin >> a[i];

    for (int i = 0; i < n; i++) {
        int sum = 0;
        for (int j = i; j < n; j++) {
            sum += a[j];
            ans = max(ans, sum);
        }
    }

    cout << ans << '\n';
    return 0; 
}

但这种算法的时间复杂度太高,用这种方法无法AC全部题目,于是我们便研究出了动态规划的算法。
2.动态规划算法(划重点)
算法思路:
(1):对于全是非正数的序列,只要求序列当中的最大值就行了(用脚趾头想都知道
(2):对于一个有正数的序列,那我们要:
<1>:考虑以每一个点结尾的最大子段和,第i个点记作f[i]f[i]
<2>:显而易见,这个点的前缀和是非负的,因为如果有任何一个前缀是负的,那么就不要那个负前缀了呗,减去那个负前缀肯定更优啊。所以如果f[i1]+a[i]<0f[i-1]+a[i]<0,都负了就别选了,这时什么都不选反而更好,最大值应该是00,否则,就把a[i]a[i]选上。
<3>:计算时只要把它从头到尾扫一遍,答案就是所有f[i]f[i]的最大值。
注:时间复杂度是线性,即O(n)O(n)算法。
核心代码:

ans = -inf;
for (int i = 1; i <= n; i++) {
    cin >> a[i];
    ans = max(ans, a[i]);
}
if (ans <= 0) {
    cout << ans << endl;
} else {
    for (int i = 1; i <= n; i++) {
        if (f[i - 1] + a[i] >= 0) {
            f[i] = f[i - 1] + a[i];
        }
        ans = max(ans, f[i]);
    }
    cout << ans << endl;
}

对于这个代码,我们可以再优化一下空间。
从上面的分析过程中可知,每个f[i]=a[i]f[i]=a[i],否则f[i]=f[i1]+a[i]f[i]=f[i-1]+a[i],所以代码可优化为:

for (int i = 1; i <= n; i++) {
    if (sum >= 0) {
        sum += a[i];
    } else {
        sum = a[i];
    }
    ans = max(ans, sum);
}

END

posted @ 2020-03-20 21:49  进击的蒟蒻  阅读(166)  评论(0)    收藏  举报