Codeforces Round #605 (Div. 3) 题解

传送门

A. Three Friends

题意

给三个数 \(a,b,c\),每个数都可以 \(+1,-1\) 或不变,问对每一个数操作至多一次后 \(|a'-b'|+|a'-c'|+|b'-c'|\) 的最小值。

思路

暴力枚举每个数操作后的值,然后记录最小结果就行了

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int T,a[5];

int main(){
    scanf("%d",&T);
    while(T--){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        LL res=0x3f3f3f3f3f3f3f;
        for(int i=-1;i<=1;i++)
            for(int j=-1;j<=1;j++)
                for(int k=-1;k<=1;k++){
                    int x=a+i,y=b+j,z=c+k;
                    res=min(res,1ll*abs(x-y)+abs(x-z)+abs(y-z));
                }
        printf("%lld\n",res);
    }
    return 0;
}

B. Snow Walking Robot

题意

给一个字符串描述路径,要求从中删除尽量少的字符,使得从 \((0,0)\) 沿着按照删改后的字符串走,可以走回 \((0,0)\),并且除了 \((0,0)\) 不会经过一个点两次

思路

统计'U'、'R'、'D'、'L'的个数,记作 \(U,R,D,L\)。从 \((0,0)\) 出发,要走回 \((0,0)\) 的话,那么显然 \(U=D,R=L\),那么首先需要使 \(U=D=min(U,D),R=L=min(R,L)\),然后按照先上,再右,再下,再左转一圈就可以走完。此时要注意,如果删改后 \(U=D=0\) 或者 \(R=L=0\),此时当然就只能走出一步然后马上回来,因为在这种情况下,只能原路返回,如果走多了,沿途的点也会被经过两次了,所以这种情况必须要特判掉。

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
int T;
char s[MAXN];

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%s",s+1);
        int n=strlen(s+1);
        int U=0,R=0,D=0,L=0;
        for(int i=1;i<=n;i++)
            if(s[i]=='U') U++;
            else if(s[i]=='R') R++;
            else if(s[i]=='D') D++;
            else if(s[i]=='L') L++;
        U=D=min(U,D);
        R=L=min(L,R);
        if(!U&&!D&&R&&L) L=R=1;
        if(!L&&!R&&U&&D) U=D=1;
        printf("%d\n",U+D+L+R);
        for(int i=1;i<=U;i++) printf("U");
        for(int i=1;i<=R;i++) printf("R");
        for(int i=1;i<=D;i++) printf("D");
        for(int i=1;i<=L;i++) printf("L");
        printf("\n");
    }
    return 0;
}

C. Yet Another Broken Keyboard

题意

给一个字符串 \(s\)\(k\) 个字母,问 \(s\) 的只包含这 \(k\) 种字母的子串个数。

思路

假设 \(s[i,j]\) 合法,那么它的子串一定合法,那么就会对答案产生贡献 \(\frac{(j-i+1)(j-i+2)}{2}\)
所以选出每一段合法区间,然后累加到答案上去就行了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=2e5+10;
int n,k,can[300];
char s[MAXN],temp[10];

int main(){
    scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    for(int i=1,x;i<=k;i++){
        scanf("%s",temp);
        can[temp[0]]=1;
    }
    LL ans=0;
    int j=0;
    for(int i=1;i<=n;i++){
        if(can[s[i]]) j++;
        else{
            ans+=1ll*j*(j+1)/2;
            j=0;
        }
    }
    ans+=1ll*j*(j+1)/2;
    printf("%lld\n",ans);
    return 0;
}

D. Remove One Element

题意

给一个序列,你可以从种删去至多一个数,问最长连续上升子序列的长度。

思路

\(f[i][0/1]\) 为以第 \(i\) 位结尾,未删除数/已删除数能得到的最长连续上升子序列的长度。
首先 \(f[i][0]\) 这个状态很好做,就是普通的最长连续上升子序列的长度:
\[ f[i][0]=f[i-1][0]+1\ (a[i]>a[i-1])\\ f[i][0]=1\ (a[i]\le a[i-1]) \]
\(f[i][1]\) 这个状态也不难,显然有两种情况会出现这种状态:
1.把 \(a[i]\) 接到以 \(a[i-1]\) 结尾且已删除数的最长上升连续子序列后。
2.把 \(a[i]\) 接到以 \(a[i-2]\) 结尾且未删除数的最长上升连续子序列后,显然这种情况是将 \(a[i-1]\) 删去了。
\[ f[i][1]=f[i-1][1]+1\ (a[i]>a[i-1])\\ f[i][1]=f[i-2][0]+1\ (a[i]>a[i-2])\\ f[i][1]=1 (任何情况下) \]

#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
int n,a[MAXN],f[MAXN][2];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int ans=0;
    for(int i=1;i<=n;i++){
        f[i][0]=f[i][1]=1;
        if(a[i]>a[i-1]){
            f[i][0]=f[i-1][0]+1;
            f[i][1]=f[i-1][1]+1;
        } 
        if(a[i]>a[i-2]) f[i][1]=max(f[i][1],f[i-2][0]+1);
        ans=max(ans,max(f[i][0],f[i][1]));
    }
    printf("%d\n",ans);
    return 0;
}

E. Nearest Opposite Parity

题意

给一个 \(n\) 个数的数列 \(a_1,a_2,...,a_n\),第 \(i\) 个数可以跳一次跳到第 \(i'=i-a_i(i-a_i\geq 1)\) 个数或第 \(i'=i+a_i(i+a_i\leq n)\) 个数上,问每个数最少跳几次,使得 \(a_i\)\(a_{i'}\) 的奇偶性不同。

思路

按理来说可以通过搜索来解决这道题的,但是直接搜索当然会超时,而我加上记忆化之后就 WA 了,所以我换了一种方法,建图之后一遍广搜可以解决。
说一下怎么建图
如果已知 \(ans_i\),那么如果 \(j+a_j=i\)\(a_j\%2=a_i%2\),那么 \(ans_j=ans_i+1\)
所以如果 \(a_i\%2=a_{i+a_i}\%2\),那么就可以建立一条 \(i+a_i->i\) 的有向边,
\(i-a_i\)\(i\) 同理
显然可以轻松的到 \(ans_i=1\) 的所有位置,那么可以从这些位置开始广搜即可

扫描二维码关注公众号,回复: 8173582 查看本文章

代码

#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int MAXN=2e5+10;
int n,a[MAXN],ans[MAXN];
int head[MAXN],to[MAXN*2],nxt[MAXN*2],tot=0;
queue<int> que;

void add(int u,int v){
    to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    memset(ans,0x3f,sizeof(ans));
    for(int i=1;i<=n;i++){
        if(i-a[i]>=1&&a[i-a[i]]%2==a[i]%2) add(i-a[i],i);
        if(i+a[i]<=n&&a[i+a[i]]%2==a[i]%2) add(i+a[i],i);
    }
    for(int i=1;i<=n;i++)
        if(i-a[i]>=1&&a[i-a[i]]%2!=a[i]%2||i+a[i]<=n&&a[i+a[i]]%2!=a[i]%2)
            ans[i]=1,que.push(i);
    while(!que.empty()){
        int u=que.front();que.pop();
        for(int i=head[u];i;i=nxt[i])
            if(ans[to[i]]>ans[u]+1){
                ans[to[i]]=ans[u]+1;
                que.push(to[i]);
            }
    }
    for(int i=1;i<=n;i++) printf("%d ",ans[i]==inf?-1:ans[i]);
    return 0;
}

F. Two Bracket Sequences

题意

给两个括号序列 \(s1,s2\),要求构造一个最短的规范的括号序列 \(ans\),且满足 \(s1,s2\)\(ans\) 的子序列。

思路

广搜
假定已经有了一个构造了序列 \((i,j,k)\)
\(i\)\(s1\) 的前 \(i\) 位是目前序列的子序列
\(j\)\(s2\) 的前 \(j\) 位是目前序列的子序列
\(k\)\(k=左括号-右括号\)
那么就需要从 \((0,0,0)\) 构造到 \((len_{s1},len_{s2},0)\)
如果将这个三元组看作是三维空间中的点的话,其实要求的就是从 \((0,0,0)\)\((len_{s1},len_{s2},0)\) 的最短路径。
因为这个从一个点到另一个点的距离就是 \(1\),那么求最短路可以直接广搜。
现在来看两个点之间的关系
已知括号序列 \((i,j,k)\)
1.如果向该序列添加一个 '(' (可以理解为点 \((i,j,k)\) 向另一个点走)
如果 \(s1[i+1]='(',s2[j+1]='('\),可以构成序列 \((i+1,j+1,k+1)\) (点 \((i,j,k)->(i+1,j+1,k+1)\))
如果 \(s1[i+1]='(',s2[j+1]\not='('\),可以构成序列 \((i+1,j,k+1)\) (点 \((i,j,k)->(i+1,j,k+1)\))
如果 \(s1[i+1]\not='(',s2[j+1]='('\),可以构成序列 \((i,j+1,k+1)\) (点 \((i,j,k)->(i,j+1,k+1)\))
2.如果向该序列添加一个 ')' (此时 \(k\geq 0\),因为要规范的括号序列,任意一个前缀中右括号都不能比左括号多)
如果 \(s1[i+1]=')',s2[j+1]=')'\),可以构成序列 \((i+1,j+1,k-1)\) (点 \((i,j,k)->(i+1,j+1,k-1)\))
如果 \(s1[i+1]=')',s2[j+1]\not=')'\),可以构成序列 \((i+1,j,k-1)\) (点 \((i,j,k)->(i+1,j,k-1)\))
如果 \(s1[i+1]\not=')',s2[j+1]=')'\),可以构成序列 \((i,j+1,k-1)\) (点 \((i,j,k)->(i,j+1,k-1)\))
那么以 \((0,0,0)\) 为起点,进行一次宽搜。
记录下宽搜的路径,从终点 \((len_{s1},len_{s2},0)\) 逆向构造出路径,就是答案了。

#include <bits/stdc++.h>
using namespace std;
char s1[210],s2[210];
int n,m;
struct  Node{
    int i,j,k;
}f[210][210][410];
queue<Node> que;

void bfs(){
    memset(f,-1,sizeof(f));
    f[0][0][0]=(Node){0,0,0};
    que.push((Node){0,0,0});
    while(!que.empty()){
        Node u=que.front();que.pop();
        int i=u.i,j=u.j,k=u.k,vi,vj,vk;
        vi=i+(s1[i+1]=='(');
        vj=j+(s2[j+1]=='(');
        vk=k+1;
        if(vk<=n+m&&f[vi][vj][vk].i==-1){
            f[vi][vj][vk]=u;
            que.push((Node){vi,vj,vk});
        }
        vi=i+(s1[i+1]==')');
        vj=j+(s2[j+1]==')');
        vk=k-1;
        if(vk>=0&&f[vi][vj][vk].i==-1){
            f[vi][vj][vk]=u;
            que.push((Node){vi,vj,vk});
        }
    }
}

int main(){
    scanf("%s%s",s1+1,s2+1);
    n=strlen(s1+1);
    m=strlen(s2+1);
    bfs();
    string ans;
    int i=n,j=m,k=0;
    while(i||j||k){
        Node u=f[i][j][k];
        if(k>u.k) ans="("+ans;
        else ans=")"+ans;
        i=u.i;j=u.j;k=u.k;
    }
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/BakaCirno/p/12034009.html