题解 | #1005.0 vs 1# 2023杭电暑期多校8

1005.0 vs 1

博弈,模拟

题目大意

两人名为 0 0 0 1 1 1 ,在给定的 01 01 01 串上进行博弈
0 0 0 只能取走两端的一个 0 0 0 1 1 1 只能取走两端的一个 1 1 1 0 0 0 执先
先不能取的人判负,若取完则判平局

解题思路

模拟博弈过程,当前操作者 x x x 可以可以遵循以下策略:

  1. 两端不同,只能取 x x x 的一端,交替操作权
  2. 两端相同
    1. 两端都不是 x x x ,无法操作,失败
    2. 两端都是 x x x ,假设取了某端
      1. 这端的下一个数字是 x x x ,则两端都是 x x x ,对方无法操作,获胜
      2. 这端的下一个数字是 ! x !x !x ,则对方只能取这一端
    3. 如果离任一端最近的连续两个相同的数都为 x x x ,则根据上 2 2 2 一直取到 x x x 获胜
    4. 如果离两端最近的连续两个相同的数都为 ! x !x !x ,则不论选哪端,最终都会到达两端都为 ! x !x !x 的情况,失败
      1. 特判:如果整个串有且仅有 1 1 1 段连续两个相同的 ! x !x !x ,则从两端向中间将各会取掉一个,达成平局

可以结合代码注释理解这一过程

时间复杂度

O ( n ) O(n) O(n)

参考代码

参考代码为已AC代码主干,其中部分功能需读者自行实现

void solve_s(deque s,int now){
    ll n=s.size(),i,j;;
    if(s.empty()) {cout << "-1" << endl;return ;}//取完平局
    if(s.front()!=s.back()){//前后不同,无法自由选择,交替操作权
        if(s.front()==now){//取前
            s.pop_front();solve_s(s,(now?0:1));return ;
        }else{//取后
            s.pop_back(); solve_s(s,(now?0:1));return ;
        }
    }else{//前后相同
        if(s.front()!=now) {cout << (now?"0":"1") << endl;return ;}//两端不可取,失败
        for(i=0;ii;j--) if(s[j]==s[j-1]) break;//从后往前找最后一个连续
        if(s[j]==now) {cout << (now?"1":"0") << endl;return ;}//有己方连续,胜利
        if(i+1==j) {cout << "-1" << endl;return ;}//有且仅有一对对方连续,取完平局
        {cout << (now?"0":"1") << endl;return ;}//两对以上对方连续,失去主动权,判负
    }
}
void solve(){
    ll n;cin >> n;
    string ts;cin >> ts;
    deque s(n);FORLL(i,0,n-1) s[i]=ts[i]-'0';
    solve_s(s,0);
}

你可能感兴趣的:(2023杭电多校,c++)