acm-(dp、思维、找规律)Codeforces Global Round 10 D. Omkar and Bed Wars

题面
传送门
先令 ′ L ′ 'L' L 0 0 0 ′ R ′ 'R' R 1 1 1,然后本题就可以无脑dp,设 d p [ i ] [ j ] [ k ] [ h ] [ w ] dp[i][j][k][h][w] dp[i][j][k][h][w]表示考虑到字符串第 i i i位, s [ 1 ] = j , s [ 2 ] = k s[1]=j,s[2]=k s[1]=j,s[2]=k s [ i − 1 ] = h , s [ i ] = w s[i-1]=h,s[i]=w s[i1]=h,s[i]=w的时候对应的最小改变字符数(使得前 i i i个字符合法,头尾不保证合法,在最后的时候对 n n n特判)。

方程有亿点点不好写,大概也就讨论了十几种情况吧,下面给出具体的代码:

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

const int maxn = 2e5+5;
const int inf = 2147483647;

char s[maxn],g[2];

int dp[maxn][2][2][2][2];//
int main(){
    int t;
    g[0]='L',g[1]='R';
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d%s",&n,s+1);
        for(int i=1;i<=n;++i){
            for(int j=0;j<2;++j){
                for(int k=0;k<2;++k){
                    for(int h=0;h<2;++h){
                        for(int w=0;w<2;++w){
                            dp[i][j][k][h][w]=inf/4;
                        }

                    }

                }
            }
        }
        for(int i=0;i<2;++i){
            for(int j=0;j<2;++j){
                dp[2][i][j][i][j]=(s[1]!=g[i])+(s[2]!=g[j]);
            }
        }
        for(int i=2;i<n-1;++i){
            //00
            for(int j=0;j<2;++j)for(int k=0;k<2;++k)dp[i+1][0][1][j][k]=min(dp[i+1][0][1][j][k],dp[i][0][0][j][k]+(s[i+1]!=g[1]));
            //01
            for(int j=0;j<2;++j)for(int k=0;k<2;++k){
                dp[i+1][1][0][j][k]=min(dp[i+1][1][0][j][k],dp[i][0][1][j][k]+(s[i+1]!=g[0]));
                dp[i+1][1][1][j][k]=min(dp[i+1][1][1][j][k],dp[i][0][1][j][k]+(s[i+1]!=g[1]));
            }
            //10
            for(int j=0;j<2;++j)for(int k=0;k<2;++k){
                dp[i+1][0][0][j][k]=min(dp[i+1][0][0][j][k],dp[i][1][0][j][k]+(s[i+1]!=g[0]));
                dp[i+1][0][1][j][k]=min(dp[i+1][0][1][j][k],dp[i][1][0][j][k]+(s[i+1]!=g[1]));
            }
            //11
            for(int j=0;j<2;++j)for(int k=0;k<2;++k){
                dp[i+1][1][0][j][k]=min(dp[i+1][1][0][j][k],dp[i][1][1][j][k]+(s[i+1]!=g[0]));
            }

        }
        //00
        dp[n][0][1][0][0]=min(dp[n][0][1][0][0],dp[n-1][0][0][0][0]+(s[n]!=g[1]));
        dp[n][0][1][0][1]=min(dp[n][0][1][0][1],dp[n-1][0][0][0][1]+(s[n]!=g[1]));
        dp[n][0][1][1][0]=min(dp[n][0][1][1][0],dp[n-1][0][0][1][0]+(s[n]!=g[1]));
        //01
        dp[n][1][0][0][1]=min(dp[n][1][0][0][1],dp[n-1][0][1][0][1]+(s[n]!=g[0]));
        dp[n][1][0][1][0]=min(dp[n][1][0][1][0],dp[n-1][0][1][1][0]+(s[n]!=g[0]));
        dp[n][1][0][1][1]=min(dp[n][1][0][1][1],dp[n-1][0][1][1][1]+(s[n]!=g[0]));
        dp[n][1][1][0][0]=min(dp[n][1][1][0][0],dp[n-1][0][1][0][0]+(s[n]!=g[1]));
        dp[n][1][1][0][1]=min(dp[n][1][1][0][1],dp[n-1][0][1][0][1]+(s[n]!=g[1]));
        //10
        dp[n][0][0][1][0]=min(dp[n][0][0][1][0],dp[n-1][1][0][1][0]+(s[n]!=g[0]));
        dp[n][0][0][1][1]=min(dp[n][0][0][1][1],dp[n-1][1][0][1][1]+(s[n]!=g[0]));
        dp[n][0][1][1][0]=min(dp[n][0][1][1][0],dp[n-1][1][0][1][0]+(s[n]!=g[1]));
        dp[n][0][1][0][0]=min(dp[n][0][1][0][0],dp[n-1][1][0][0][0]+(s[n]!=g[1]));
        dp[n][0][1][0][1]=min(dp[n][0][1][0][1],dp[n-1][1][0][0][1]+(s[n]!=g[1]));
        //11
        dp[n][1][0][0][1]=min(dp[n][1][0][0][1],dp[n-1][1][1][0][1]+(s[n]!=g[0]));
        dp[n][1][0][1][0]=min(dp[n][1][0][1][0],dp[n-1][1][1][1][0]+(s[n]!=g[0]));
        dp[n][1][0][1][1]=min(dp[n][1][0][1][1],dp[n-1][1][1][1][1]+(s[n]!=g[0]));
        int ans=inf;
        for(int i=0;i<2;++i)for(int j=0;j<2;++j)for(int k=0;k<2;++k)for(int w=0;w<2;++w)ans=min(ans,dp[n][i][j][k][w]);
        printf("%d\n",ans);
    }
}

那有没有更简单的方法呢,容易 发现其实只要序列中出现了连续的三个相同字母,那么就不合法,因此只要通过改变一些字母使得序列中没有三个连续的字母即可,注意到序列是环形的。

于是考虑贪心,如果所有字母都相同需要特判,答案是 ⌈ n 3 ⌉ \lceil \frac n3\rceil 3n,否则的话考虑所有连续的相同字母构成的段的长度假设是 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak,那么答案是 ∑ i = 1 k ⌊ a i 3 ⌋ \sum_{i=1}^k\lfloor\frac{a_i}3\rfloor i=1k3ai

输出答案即可。

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

const int maxn = 2e5+5;
const int inf = 2147483647;

char s[maxn<<1];
vector<int>g;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d%s",&n,s+1);
        bool diff=false;
        g.clear();
        for(int i=1;i<=n;++i){
            s[n+i]=s[i];
            if(i>1 && s[i]!=s[i-1])diff=true;
        }
        if(!diff){
            printf("%d\n",(n+2)/3);
            continue;
        }
        int cnt=0;
        g.push_back(0);
        for(int i=2;i<=n+1;++i){
            if(s[i]!=s[i-1]){
                for(int j=i;j<=i+n-1;++j){
                    if(j==i || j>i&&s[j]==s[j-1]){
                        g.back()++;
                    }else g.push_back(1);
                }
                break;
            }
        }
        int ans=0;
        for(int i=0;i<g.size();++i)ans+=g[i]/3;
        printf("%d\n",ans);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值