Educational Codeforces Round 128

Educational Codeforces Round 128

C. Binary String

题意:每次可以从一个01串的两头删除0或者1,求最后留在串中的0的数量和删除的1的数量最大值最小是多少
做法:我们枚举从一端删除1的数量,那么其实你可以发现随着另一端删除1的数量增加,答案会随着变小到最小然后再变大,就类似二次函数的极值,那么我们就只需要对每一个位置三分极值即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;

char s[200050];
int dp[200050];
int n;
vector<int>pos;

int check(int i,int j,int num,int zero){
    if(j>=pos.size()) return 10000000;
     int One=num+j;
    int O=zero-(n-i+1+dp[pos[j]]-1-One);
    int t=max(One,O);
    return t;
}

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif
    int T;
    cin>>T;
    while(T--){
        pos.clear();
        scanf("%s",s+1);
        n=strlen(s+1);
        int ans=1000000;
        int zero=0,one=0;
        int num=0;
        for (int i=1;i<=n;i++) {
            if(s[i]=='0') zero++;
            else {
                num++;
                dp[num]=i;
                pos.push_back(num);
            }
        }
        int r=n-zero;
        if(r==0 || zero==0){
            puts("0");
            continue;
        }
        ans=r;
        num=0;
        for (int i=n+1;i>=1;i--){
            if(s[i]=='1') num++;
            int l=0;
            r=pos.size()-1;
            while(l<=r){
               int mid=(l+r)>>1;
               int t1=check(i,mid,num,zero);
               int t2=check(i,mid+1,num,zero);
               ans=min(ans,t1);
               ans=min(ans,t2);
               if(t1>=t2){
                   l=mid+1;
               }else r=mid-1;
            }

            for (int j=max(0,l-5);j<=min((int)pos.size()-1,r+5);j++) ans=min(ans,check(i,j,num,zero));

            ans=min(ans,check(i,0,num,zero));
            ans=min(ans,check(i,pos.size()-1,num,zero));
        }
        printf("%d\n",ans);
    }
    return 0;
}

E. Moving Chips

题意:给一个两行的\(*和.\)的序列,最后要将所有的\(*\)都移动到一个位置,求最少需要的步数
做法:基本思路,我们可以枚举每一个\(*\)位置将所有的\(*\)转移到这个位置的代价,然后求出最小步数即可,那么问题就转化为怎么求将前面的所有\(*\)转移到每个位置的最小代价,这个dp推一下即可,

设pre[i][j]为1~ j - 1列的芯片都移动到(i,j)的最小次数,则分两种情况 (因为同一列都有芯片总要合并一起移动才最优):

1 ~ j - 1列的芯片都先移动到 (i, j - 1) ,然后再移动到(i, j)。
次数为前面的移动到(i, j - 1)的次数,加上(i^1, j - 1)移动到(i,j - 1)的次数,再加上一次
即为pre[i][j - 1] + 1+ (s[i^1][j - 1] == ‘*’)
1 ~ j - 1列的芯片都先移动到(i^1, j - 1) ,然后再移动到(i,j)
次数为pre[i^1][j - 1] + 1
同理可以计算suf[i][j]为j+1~n都移动到(i,j)的最小操作数。

那么(i,j)位置的答案要么是两个pre都在[i ^ 1,j],然后向上一步,或者两个都在(i,j),如果(i^1,j)有'*'再加上1,

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int maxn=2e5+10;

int pre[2][maxn],suf[2][maxn];
char s[2][maxn];

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif

    int T;
    cin>>T;
    while(T--){
        int n;
        scanf("%d",&n);
        scanf("%s",s[0]+1);
        scanf("%s",s[1]+1);
        int tou=1,wei=n;
        while(s[0][tou]=='.' && s[1][tou]=='.' && tou<n) tou++;
        while(s[0][wei]=='.' && s[1][wei]=='.' && wei>1) wei--;
        for (int i=0;i<=n;i++) pre[0][i]=pre[1][i]=suf[0][i]=suf[1][i]=0;
        for (int i=tou+1;i<=n;i++) for (int j=0;j<=1;j++) pre[j][i]=min(pre[j^1][i-1]+2,pre[j][i-1]+1+(s[j^1][i-1]=='*'));
        for (int i=wei-1;i>=1;i--) for (int j=0;j<=1;j++) suf[j][i]=min(suf[j^1][i+1]+2,suf[j][i+1]+1+(s[j^1][i+1]=='*'));
        int ans=1e9;
        for (int j=0;j<=1;j++){
            for (int i=1;i<=n;i++){
                if(s[j][i]=='.') continue;
                ans=min(ans,pre[j][i]+suf[j][i]+(s[j^1][i]=='*'));
                ans=min(ans,pre[j^1][i]+suf[j^1][i]+1);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
posted @ 2022-05-15 20:19  lmj_1  阅读(32)  评论(0)    收藏  举报