1. 程式人生 > >「日常訓練」Alternative Thinking(Codeforces Round #334 Div.2 C)

「日常訓練」Alternative Thinking(Codeforces Round #334 Div.2 C)

題意與分析 (CodeForces - 603A)

這題真的做的我頭疼的不得了,各種構造樣例去分析性質。。。
題意是這樣的:給出01字串。可以在這個字串中選擇一個起點和一個終點使得這個連續區間內所有的位取反。求經過處理後最多會得到多少次01變換(可以不連續)。
首先求原串的最長01長度,這個太簡單了,然後才是重頭戲:精彩的構造樣例分類討論環節——如何增加01的最長串?
我們考慮一下反轉串的幾種情況:1、反轉單個連續0/1串的內部;2、反轉兩個部分連續0/1串與中間的內容(內部無連續串)3、反轉兩個部分連續0/1串與中間的內容(內部有連續串)4、反轉連續01串(不是0/1串!)
第一種情況是最好考慮的,不論你是1000..0001還是0111...11110,裡面不論怎麼反轉,最後對答案的貢獻也多一個01/10,也就是2。這種多的貢獻僅僅出現在0/1串長度大於等於三的情況。
第二種情況稍微複雜一些:先考慮0開頭的情況,也就是00..0{101010101}1..11或者00..0{101010101}0..00。這種情況下最好的處理是連著前後一個字元一起反轉:00..1{010101010}0..11於是長度喜多2。這種情況下,前面跟著一個串(長度大於等於2即可)就產生1的貢獻,後面跟著一個就產生一個貢獻。1開頭的同理。
第三種情況建立在2的基礎上:如果反轉串中間也有連續會怎樣?什麼時期都不會發生,因為它作出的貢獻僅僅相當於一個單個字元

。於是答案同2。
第四種情況比較有趣了:我對原答案做反轉,沒有連續的01會怎樣?那你不管怎麼樣,最優答案總是原先的最長長度。

綜合以上四種情況,我們可以得到這個結論:
如果整個串裡面的00/11串個數\(\le 2\),那麼答案(即原先長度)加上這個串的個數;否則答案最多+2。情況1此時已經被包括在內了,因為長度為3的連續串一定有兩個00/11串。

程式碼

/* 
 * Filename: cfr334d2c.cpp
 * Date: 2018-11-05
 */

#include <bits/stdc++.h>

#define INF 0x3f3f3f3f
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
#define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
#define per(i,a,b) for(repType i=(a); i>=(b); --i)
#define ZERO(x) memset(x, 0, sizeof(x))
#define MS(x,y) memset(x, y, sizeof(x))
#define ALL(x) (x).begin(), (x).end()

#define QUICKIO                  \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)

using namespace std;
using pi=pair<int,int>;
using repType=int;
using ll=long long;
using ld=long double;
using ull=unsigned long long;

int dp[100005][2];

int
main()
{
    int n; cin>>n;
    bool b[100005];
    string str; cin>>str;
    int maxlen=0,nowlen=0;
    bool last;
    int cnt=0;
    rep(i,0,n-1)
    {
        b[i+1]=str[i]=='0';
        if(i==0)
        {
            dp[i+1][0]=str[i]=='0';
            dp[i+1][1]=str[i]=='1';
            last=b[i+1];
            nowlen=1;
        }
        else
        {
            if(last==b[i+1])
            {
                nowlen++;
                maxlen=max(nowlen,maxlen);
            }
            else
            {
                cnt+=nowlen-1;
                nowlen=1;
                last=b[i+1];
            }
            if(str[i]=='0')
            {
                dp[i+1][1]=dp[i][1];
                dp[i+1][0]=dp[i][1]+1;
            }
            else
            {
                dp[i+1][1]=dp[i][0]+1;
                dp[i+1][0]=dp[i][0];
            }
        }
    }
    if(nowlen!=1) cnt+=nowlen-1;
    int ans=0;
    rep(i,1,n)
        ans=max(ans,max(dp[i][0],dp[i][1]));
/*
    rep(i,1,n)
        cout<<dp[i][0]<<" ";
    cout<<endl;
    rep(i,1,n)
        cout<<dp[i][1]<<" ";
    cout<<endl;
*/
    cout<<ans+min(2,cnt)<<endl;
    return 0;
}