Codeforces Round #605 (Div. 3)

A. Three Friends

题目链接

题目大意

给你三个数(a,b,c),每个数可以选择向左,向右或者原地不动,求(min left(left|a-b ight|+left|a-c ight|+left|b-c ight| ight)的值)

解题思路

  1. 先按定义求出答案,如果三个数都想等的话,就直接输出0,如果有两个数连续的话就减去(2),否则减去(4)
  2. 依然先按定义求出答案,如果小于4,输出0,否则减去4输出。

AC代码1

#include <bits/stdc++.h>
const int maxn = 1e5+100;
typedef long long ll;
using namespace std;
int q;
ll a[4];
int main()
{
    // freopen("data.txt","r",stdin);
    cin>>q;
    while(q--)
    {
        for(int i=0;i<3;i++)
        {
            cin>>a[i];
        }
        sort(a,a+3);
        ll res = a[1]-a[0]+a[2]-a[0]+a[2]-a[1];
        if(a[0]==a[1]&&a[1]==a[2]){
 
        }
        if(a[0]==a[1]&&a[1]+1==a[2]){
            res-=2;
        }else if(a[1]==a[2]&&a[0]+1==a[1]){
            res-=2;
        }else{
            res-=4;
        }
        res = max(1LL*0,res);
        cout<<res<<endl;
    }
}

AC代码2

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int main()
{
    int q;
    cin>>q;
    while(q--){
        int a,b,c;
        cin>>a>>b>>c;
        int res = abs(a-b)+abs(b-c)+abs(a-c);
        res = (res>4)?res-4:0;
        cout<<res<<endl;
    }
}

B. Snow Walking Robot

题目链接

题目大意

在一个方格中,从(0,0)点开始出发,每次可以向上,向下,向右,向左前进一个格子,分别用(U,D,R,L)字符表示,给你一个只包含(U,D,R,L)的字符串,删除尽可能少的字符并且重新排序,同时每个点最多经过一次,使得最后回到(0,0)这个点,输出最后的字符串。

解题思路

分别统计(U,D,R,L)的个数,要使得最后回到(0,0)这个点,那么(U)(D)字符的个数必须一样,(R)(L)字符的个数必须一样,则取两者的最大值,再分别输出即可,这里要求一个点最多经过一次,只要(U,D,R,L)中任意一个字符的个数为(0),字符的长度最多为(2)

AC代码

#include <bits/stdc++.h>
const int maxn = 1e5+100;
typedef long long ll;
using namespace std;
int main()
{
    // freopen("data.txt","r",stdin);
    int q;
    cin>>q;
    while(q--){
        string s;
        cin>>s;
        int a=0,b=0,c=0,d=0;
        for(int i=0;i<s.size();i++){
            if(s[i]=='U'){
                a++;
            }else if(s[i]=='D'){
                b++;
            }else if(s[i]=='R'){
                c++;
            }else{
                d++;
            }
        }
        int res = min(a,b);
        int res2 = min(c,d);
        if(res==0||res2==0){
            res2=min(res2,1);
            res = min(res,1);
        }
        cout<<(res+res2)*2<<endl;
        for(int i=0;i<res;i++){
            cout<<'U';
        }
        for(int i=0;i<res2;i++){
            cout<<'R';
        }
        for(int i=0;i<res;i++){
            cout<<'D';
        }
        for(int i=0;i<res2;i++){
            cout<<'L';
        }
        cout<<endl;
    }
}

C. Yet Another Broken Keyboard

题目链接

题目大意

给你一个字符串(s),同时给你(k)个字符的字符数组(t),求(s)中只包含(t)中字符的字串个数。

解题思路

首先先遍历字符数组(t),用另外一个数组(str)标记每个字符是否可用,遍历整个字符串,求出每个符合要求的最长字串长度(len),然后答案加上(frac{n imesleft(n+1 ight)}{2})即可。

AC代码

#include <bits/stdc++.h>
const int maxn = 1e5+100;
typedef long long ll;
using namespace std;
bool str[200];
int main()
{
    // freopen("data.txt","r",stdin);
    for(int i=0;i<200;i++){
        str[i]=true;
    }
    int n,k;
    cin>>n>>k;
    string s;
    cin>>s;
    getchar();
    for(int i=0;i<k;i++){
        char c;
        cin>>c;
        str[c-'0']=false;
    }
    ll res=0;
    int cnt=0;
    for(int i=0;i<s.size();i++){
        if(str[s[i]-'0']==false){
            cnt++;
        }else{
            res+=1LL*cnt*(1LL*cnt+1)/2;
            cnt=0;
        }
    }
    res+=1LL*cnt*(1LL*cnt+1)/2;
    cout<<res<<endl;
}

D. Remove One Element

题目链接

题目大意

给你一个包含(n)个数字的数组(a),可以删除数组(a)中最多一个数组,求最长严格递增子数组的长度。

解题思路

这道题是个典型的动态规划的题目。

数组dp[i]表示以(a_i)为起点的最长子数组长度,转移方程为:

[dp[i]=egin{cases} 1 & i=n \ 1 & a[i]geq a[i+1] \ dp[i+1]+1 & a[i]<a[i+1] \ end{cases} ]

数组dp2[i]表示以(a_i)为终点的最长子数组长度,转移方程为:

[dp2[i]=egin{cases} 1 & i=1 \ 1 & a[i-1]geq a[i] \ dp[i-1]+1 & a[i-1]<a[i] \ end{cases} ]

遍历整个数组(a),如果(a_{i+1}> a_{i-1}),那么结果为(max(result,dp2[i+1]+dp[i-1]))

AC代码

#include <bits/stdc++.h>
const int maxn = 2e5+100;
typedef long long ll;
using namespace std;
int number[maxn];
ll dp[maxn];
ll dp2[maxn];
int main()
{
    // freopen("data.txt","r",stdin);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>number[i];
    }
    dp[n]=1;
    ll res=1;
    for(int i=n-1;i>=1;i--){
        if(number[i]<number[i+1]){
            dp[i] = dp[i+1]+1;
            res = max(res,dp[i]);
        }else{
            dp[i]=1;
        }
    }
    dp2[0]=0;
    for(int i=1;i<=n;i++){
        if(number[i]>number[i-1]){
            dp2[i] = dp2[i-1]+1;
        }else{
            dp2[i]=1;
        }
    }
    for(int i=1;i<=n;i++){
        if(i+1<=n&&number[i+1]>number[i-1]){
            res=max(res,dp[i+1]+dp2[i-1]);
        }
    }
    cout<<res<<endl;
}

E. Nearest Opposite Parity

题目大意

给你一个由(n)个整数组成的数组。 一键移动,您可以从位置(i)跳到位置(i-a_i)(如果(1leq (i-a_i)))或跳到位置(i+a_i)(如果(i+a_i leq n))。

对于从(1)(n)的每个位置(i),您想知道到达任何位置(j)所需的最小移动次数,以使(a_j)(a_i)具有相反的奇偶性(即,如果(a_i)为奇数,则(a_j)必须为偶数,反之亦然)。

解题思路

  • 思路一:反向建立图,对于偶数的情况,偶数(a_i)到奇数(a_j)的最小移动次数反向建图之后就是所有奇数(a_j)(a_i)的最短路径的长度即位答案,但偶数有那么多个,每个偶数都去计算所有奇数到它的最短路径,时间复杂度太高,这个时候就可以使用超级源点,设一个点到所有奇数的距离为(0),跑一个最短路径出来,所有偶数的答案都出来了。对于奇数的情况也是一样的。即找到两个超级源点跑两次最短路径。
  • 思路二:同样是反向建图,但可以不用最短路径,在建图的过程中找到跳转一次就可以到达的点放入队列中,然后BFS搜索即可。这样的代码量可以减少很多。

AC代码

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f
const int maxn=2e5+10;
typedef long long ll;
using namespace std;
typedef pair<long long,int> P;
struct edge
{
    int to,cost;
};
vector<edge>G[maxn];
int number[maxn];
ll d[maxn];
int n;
ll res[maxn];
void dijks(int s)
{
    priority_queue<P,vector<P>,greater<P> >pq;
    fill(d,d+n+1,INF);
    d[s]=0;
    pq.push(P(0,s));
    while(!pq.empty())
    {
        P p=pq.top();
        pq.pop();
        int v=p.second;
        if(d[v]<p.first)
            continue;
        else
        {
            for(int i=0;i<G[v].size();i++)
            {
                if(d[G[v][i].to]>d[v]+G[v][i].cost)
                {
                    d[G[v][i].to]=d[v]+G[v][i].cost;
                    pq.push(P(d[G[v][i].to],G[v][i].to));
                }
            }
        }
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>number[i];
    }
    for(int i=1;i<=n;i++){
        if(number[i]+i<=n){
            edge e = {i,1};
            G[number[i]+i].push_back(e);
        }
        if(i-number[i]>=1){
            edge e = {i,1};
            G[i-number[i]].push_back(e);
        }
    }
    for(int i=1;i<=n;i++){
        if(number[i]%2==1){
            edge e = {i,0};
            G[0].push_back(e);
        }
    }
    dijks(0);
    for(int i=1;i<=n;i++){
        if(number[i]%2==0){
            res[i] = d[i];
        }
    }
    G[0].clear();
    for(int i=1;i<=n;i++){
        if(number[i]%2==0){
            edge e = {i,0};
            G[0].push_back(e);
        }
    }
    dijks(0);
    for(int i=1;i<=n;i++){
        if(number[i]%2==1){
            res[i] = d[i];
        }
    }
    for(int i=1;i<=n;i++){
        if(res[i]>=INF){
            cout<<"-1"<<" ";
        }else{
            cout<<res[i]<<" ";
        }
    }
    cout<<endl;
}

F. Two Bracket Sequences

题目大意

给你两个括号字符串(s,t),找到一个最短合法字符串满足其子序列包括字符串(s,t)

解题思路

这道题也是一道动态规划的题,不过这道题是三维的动态规划。(dp[i][j][k])表示(s)串匹配到(i)(t)串匹配到(j)的时候有(k)(()没有匹配的最小长度。转移方程不是很好写,具体看代码。因为要求子序列包含(s)(t)并且最后的字符串尽可能的短,那么就应该用字符串(s)(t)尽可能的构造合法的括号

AC代码

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
const int maxn = 2e2+40;
typedef long long ll;
using namespace std;
int n,m;
string s,t;
bool res[maxn][maxn][2*maxn];
int dp[maxn][maxn][2*maxn];
int dfs(int i,int j,int cnt){
    if(i==n&&j==m)
        return cnt;
    if(cnt>n+m){
        return 1e9;
    }
    if(~dp[i][j][cnt]){
        return dp[i][j][cnt];
    }
    int op1 = 1 + dfs(i+(i<n && s[i]=='('),j + (j<m && t[j]=='(') ,cnt+1),op2 = 1e9;;
    if(cnt){
        op2 = 1 + dfs(i+(i<n && s[i]==')'),j + (j<m && t[j]==')') ,cnt-1);
    }
    res[i][j][cnt]=op1<op2;
    return dp[i][j][cnt]=min(op1,op2);
}
int main()
{
    ios::sync_with_stdio(false);
    memset(dp,-1,sizeof(dp));
    cin>>s>>t;
    n = s.size();
    m = t.size();
    dfs(0,0,0);
    int i=0,j=0,cnt=0;
    while(i<s.size()||j<t.size()){
        if(res[i][j][cnt]){
            i+=(i<n && s[i]=='(');
            j+=(j<m && t[j]=='(');
            cnt++;
            cout<<"(";
        }else{
            i+=(i<n && s[i]==')');
            j+=(j<m && t[j]==')');
            cnt--;
            cout<<")";
        }
    }
    while(cnt--){
        cout<<")";
    }
    cout<<endl;
}

总结

终于有一次能把(CodeForces)上的题完整的补完一次了。(A)题真的傻了,那么简单的,当时居然卡了好一会儿。(B)(C,D)题比较简单,只是(D)有个小细节没有处理好,(wa)了两次。(E)(F)是赛后交流之后补的。终于涨分了,呜呜呜

原文地址:https://www.cnblogs.com/zrcsy/p/12188662.html