【学习笔记】CF1510J Japanese Game

文章详细解释了一种处理01串排列的算法,考虑在不改变0和1数量的前提下,如何通过移动0来满足特定条件。算法首先分析了0的数量(K),然后通过区间表示连续的1,并探讨了不同K值下的处理方式,最终给出了一种O(n)复杂度的解决方案,代码使用C++编写。
摘要由CSDN通过智能技术生成

写这篇题解单纯是因为考场上把这道题想错了,其实也没啥特别难的地方。

方便起见,下文用 01 01 01串来叙述。考虑假设没有被固定的 0 0 0的数目为 K K K,那么我们可以先把这 K K K 0 0 0放到最前面,这样每一个连续的 1 1 1可以表述为区间 [ l i , r i ] [l_i,r_i] [li,ri]。那么将这 K K K 0 0 0挪到最后面过后就变成了 [ l i , r i − K + 1 ] [l_i,r_i-K+1] [li,riK+1],如果长度不足 K K K就全部变成 0 0 0

想明白这个过程过后,这道题就非常简单了。注意到 K ∈ [ 0 , 3 ] K\in [0,3] K[0,3](因为实现一段连续的 0 0 0是容易的,只要 01 01 01交错就行,所以 K K K很大是不必要的),因此可以直接暴力枚举 K K K然后判断。

复杂度 O ( n ) O(n) O(n)

难得写了注释

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
using namespace std;
int n;
string s;
vector<int>vec;
vector<pair<int,int>>seq;
void check2(){
    int tot=0;
    for(int i=0;i<n;i++)tot+=(s[i]=='_');
    if(tot==n){
        cout<<0;
        exit(0);
    }
    if(s[0]=='_'||s[n-1]=='_')return;
    for(int i=1;i<n;i++){
        if(s[i]=='_'&&s[i-1]=='_')return;
    }
    for(auto x:seq){
        vec.pb(x.se-x.fi+1);
    }
    cout<<vec.size()<<"\n";
    for(auto x:vec){
        cout<<x<<"\n";
    }
    exit(0);
}
int check(int Mid){
    for(int i=0;i<Mid;i++){
        if(s[i]=='#'){
            return 0;
        }
    }
    vec.clear();
    //处理第一段的情况
    int tot=0;
    for(int i=0;i<n;i++){
        if(s[i]=='_'){
            tot++;
        }
        else{
            break;
        }
    }
    tot-=Mid;
    if(tot==1)return 0;
    if(tot>1&&tot%2==0){
        for(int i=1;i<=tot/2;i++)vec.pb(1);
    }
    if(tot>1&&tot%2==1){
        if(Mid==1)return 0;
        vec.pb(2);
        for(int i=1;i<=tot/2-1;i++)vec.pb(1);
    }
    //处理中间段的情况
    for(int i=1;i<seq.size();i++){
        int Len=seq[i].fi-seq[i-1].se-1;
        if(Len<=Mid){
            return 0;
        }
        vec.pb(seq[i-1].se-seq[i-1].fi+1+Mid);
        Len-=Mid;
        if(Len%2==1){
            for(int j=1;j<=Len/2;j++)vec.pb(1);
        }
        else if(Len>2&&Mid>=2){
            vec.pb(2);
            for(int j=1;j<=(Len-1)/2-1;j++)vec.pb(1);
        }
        else return 0;
    }
    //处理最后一段的情况
    tot=0;
    for(int i=n-1;i>=0;i--){
        if(s[i]=='_'){
            tot++;
        }
        else{
            break;
        }
    }
    if(tot<Mid){
        return 0;
    }
    vec.pb(seq.back().se-seq.back().fi+1+Mid);
    tot-=Mid;
    if(tot==1)return 0;
    if(tot>1&&tot%2==0){
        for(int i=1;i<=tot/2;i++)vec.pb(1);
    }
    if(tot>1&&tot%2==1){
        if(Mid==1)return 0;
        vec.pb(2);
        for(int i=1;i<=tot/2-1;i++)vec.pb(1);
    }
    return 1;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>s,n=s.size();
    for(int i=0;i<n;i++){
        if(s[i]=='#'){
            int j=i;while(j+1<n&&s[j+1]=='#')j++;
            seq.pb({i,j}),i=j;
        }
    }
    check2(); // 判断不移动的情况,或者全为0的情况
    for(int i=1;i<=3;i++){
        //fixed
        if(check(i)){
            cout<<vec.size()<<"\n";
            for(auto x:vec)cout<<x<<" ";
            return 0;
        }
    }
    cout<<-1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值