Codeforces Round #605 (Div. 3)题解报告

A、Three Friends

在这里插入图片描述

【题意】

在直线上有三个点 a、b、c,每个点都可以向左或者向右移动1个单位,问 |a−b|+|a−c|+|b−c| 最小值是多少。

【思路、解题过程及感想】

首先将a、b、c排列(这里我直接装进数组用的sort),然后进行判断。排序后a >= b >= c,那么|a−b|+|a−c|+|b−c|就可以化成2×(a - c),最小的数就是a == c的情况,为0。首先判断a、b、c三个数想不相等,如果相等就可以直接输出0,不相等的话a–,c++,因为要使2×(a - c)最小,那么就要a减小c增大,使区间范围变小(当b与a或者c相等时可以跟随着a或者c移动),但这里要再进行一次判断,如果变化后a>c的话那么没有影响,输出2×(a - c);如果a<=c的话,那么说明a与c之间的间距小于等于2,经过移动后可以相等,输出0。
这道题做的时候wa了一次,因为判断条件过于复杂,判断a和c改变值用了多组if、else,有些条件没有考虑到导致的错误,最后再次细致的观察演算了一下,才简化改进了一下ac。
做的时候以及过了半个多小时还没ac一道题,因为练习时题目难度并不是按照顺序排列的,前面做一道题卡住了,换了这道题之后有些着急了,然后没有细致考虑就直接写的代码,wa了一遍,所以说静下心来解题很重要。

【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
int main ()
{
    
    
    ll t,a[5];
    cin >>  t ;
    while(t--)
    {
    
    
        for(int i=0; i<3; i++)
            cin >> a[i];
        sort(a,a+3);
        if (a[0]==a[1]&&a[1]==a[2])
            cout << 0 <<endl;
        else
        {
    
    
            a[0]++;
            a[2]--;
            if(a[2]-a[0]>0)
                cout << 2*(a[2]-a[0]) <<endl;
            else
                cout << 0 <<endl;
        }
    }
    return 0;
}

B、Snow Walking Robot

在这里插入图片描述

【题意】

给出一段机器指令,你可以删去一些指令,使最后剩余的指令操作之后回到原点,且这个过程中不能经过相同点,问最多可以剩下多少指令。
其实就是在原来指令基础上重新排列指令,让其从原点出发绕一圈回到原点。

【思路、解题过程及感想】

这个题有几点需要注意的,第一是可以删除指令,可以重新排列指令,第二是除了起点之外不能在同一个点经过两次。基于第一点与题意可以推断出,L和R数量应该相等,U和D数量应该相等,若不等则都置为每组最小的那个数;基于第二点可以推断出,假如{L、R}和{U、D}这两组中,有一组中有0,那么另一组就只能走两步,出去,回来,多走的话回来的时候就会重复经过同一个点。再就是让他的路径成为一个正方形就可以了。
这道题是补的,ACD三道题用了挺长时间,且这道题题目有点长,看起来有点麻烦,最后理解错意思了,也没出来代码,但真的回头来仔细看一看这道题目,其实也不难,理解题意之后就很好解决了。

【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
#define ll long long
int main()
{
    
    
    int t;
    cin >> t;
    string s;
    while(t--)
    {
    
    
        int l=0,r=0,u=0,d=0;
        cin >> s;
        int len=s.length();
        for(int i=0;i<len;i++)
        {
    
    
            if(s[i]=='L') l++;
            else if(s[i]=='R') r++;
            else if(s[i]=='U') u++;
            else if(s[i]=='D') d++;
        }
        int x=min(l,r),y=min(u,d);
        if(x&&y)
        {
    
    
            cout << ((x<<1) + (y<<1)) << endl;
            for(int i=0;i<x;i++) cout << 'L';
            for(int i=0;i<y;i++) cout << 'U';
            for(int i=0;i<x;i++) cout << 'R';
            for(int i=0;i<y;i++) cout << 'D';
            cout << endl;
        }
        else
        {
    
    
            if(x) cout << 2 << endl << "LR" << endl;
            else if(y) cout << 2 << endl << "UD" << endl;
            else cout << 0 << endl;
        }
    }
    return 0;
}

C、Yet Another Broke Keyboard

在这里插入图片描述

【题意】

即使找了翻译这个题也是半半卡卡的,但根据样例还是摸索出来题意了。
给定一个字母序列和几个字母,然后找出含给出字母的连续子串。
其实真正让你做的就是把给出的字母序列分割成若干段,用的是第二行没有给出的字母来分割,例如:
9 3
abdradara
a b d
这个例子是把序列分成 abd | ada | a 三段,然后求每段子串个数个计算公式题目中给了,设n为一段的长度,其子串的个数就是n(n+1)/2,最后把每一段的加起来就可以了。

【思路、解题过程及感想】

思路就像是题意中写的一样,首先在输入字母的时候用一个数组来记录一下作为判断,然后分段并计算每一段的字串,累加即可。再具体参考代码。

【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn=2e5+10;
ll str[maxn];
int main ()
{
    
    
    ll n,k,ans,num;string a;
    cin >> n >> k >> a;
    char b;
    for(int i=0;i<k;i++)
    {
    
    
        cin >> b;
        str[b] = 1;//记录
    }
    num = ans = 0;
    for(int i=0;i<n;i++)
    {
    
    
        if(str[a[i]])
            num++;
        else{
    
    
            ans += num*(num+1)/2;
            num = 0;
        }
    }
    //这里注意如果最后一个字母也被记录,那么最后一段的子串种类就没有加上,所以最后要再加一次。
    cout << ans+num*(num+1)/2 <<endl;
    return 0;
}

D、Remove One Element

在这里插入图片描述

【题意】

给出一个序列,最多可以删除一个字符,求最长连续上升子串。

【思路、解题过程及感想】

思路其实很简单,两个dp数组,一个从前向后记录,一个从后向前记录,然后在序列中找最长的子串即可。
一开始走了一些弯路,用的一遍dp,但是判断越多越容易出现漏洞,越容易出现问题,且数组开小了,就出了好多问题。
一开始有的想法就是拼接,后来想到了以前做过有些类似的题目,就想到了两个数组两个方向记录然后拼接最后ac。

【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn=2e5+10;
ll a[maxn];
ll b[maxn],c[maxn];
int main ()
{
    
    
    int n;
    ll maxx = -1;
    cin >> n;
    for(int i=0; i<n; i++)
    {
    
    
        cin >> a[i];
        b[i] = c[i] = 1;
    }
    for(int i=1; i<n; i++)
        if(a[i]>a[i-1])
            b[i]=b[i-1]+1;
    for(int i=n-2; i>=0; i--)
        if(a[i]<a[i+1])
            c[i]=c[i+1]+1;
    for(int i=0; i<n; i++)
    {
    
    
        if(a[i]<a[i+2])
            maxx = max(maxx,b[i]+c[i+2]);
        else
            maxx = max(maxx,max(b[i],c[i]));
    }
    cout << maxx <<endl;
    return 0;
}

E、Nearest Opposite Parity

在这里插入图片描述

【题意】

第i个元素可以跳到i+a[i]或i-a[i]的位置j(跳跃条件是不能跳出边界),然后从j又能跳a[j]的距离来进行移动,求从i跳多少次才能到达原数组中与a[i]奇偶性不同的点,如果跳不到就输出-1。

【思路、解题过程及感想】

用bfs,注意点的编号和点的值的区别。这道题说用的反向建图,图论这个东西不是很懂,这也是需要补的一点,这个题也是找的大佬博客来解决的。

【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn = 200005;
struct node
{
    
    
    int val, x, y;
} a[maxn];
vector<int> v[maxn];
int main()
{
    
    
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
    
    
        a[i].x = a[i].y = inf;
        cin >> a[i].val;
        if (i + a[i].val <= n)
            v[i + a[i].val].push_back(i);
        if (i - a[i].val >= 1)
            v[i - a[i].val].push_back(i);
    }
    queue<int>q;
    for (int i = 1; i <= n; i++)
    {
    
    
        q.push(i);
    }
    while (!q.empty())
    {
    
    
        int head = q.front();
        q.pop();
        for (auto i : v[head])//v[head]中放的是下一次移动能到达head位置的点的编号,即上一个点的编号
        {
    
    
            if (a[head].val & 1)//如果head点值为奇数
            {
    
    
                if (a[i].x > 1 || a[i].y > a[head].y + 1)//head点为奇数值,那么它上一个点到奇数值点的最短距离只有1一种可能。i的x值更新为1,而y记录的是head点到下一个y点的距离
                {
    
    
                    a[i].x = 1;
                    if (a[i].y > a[head].y + 1)//位置i的点用对应的y继承位置head点到值为偶数的点的最短距离
                        a[i].y = a[head].y + 1;
                    q.push(i);
                }
            }
            else
            {
    
    
                if (a[i].y > 1 || a[i].x > a[head].x + 1)
                {
    
    
                    a[i].y = 1;
                    if (a[i].x > a[head].x + 1)
                        a[i].x = a[head].x + 1;
                    q.push(i);
                }
            }
        }
    }
    for (int i = 1; i <= n; i++)
    {
    
    
        if (a[i].val & 1)
        {
    
    
            cout << (a[i].y == inf ? -1 : a[i].y) << " ";
        }
        else
            cout << (a[i].x == inf ? -1 : a[i].x) << " ";
    }
}

F、Two Bracket Sequences

在这里插入图片描述

【题意】

输入两个括号序列 s,t,你需要构造一个尽可能短的合法括号序列使得s,t 都是这个序列的子序列(子序列意味着不用连续)

【思路、解题过程及感想】

这道题是真的想不出来怎么解,找的题解说是是三维dp + 深搜,不怎么理解。
具体思路:设dp[i][j][v]表示s串已匹配 前i个括号,t串 已经匹配前j个括号,此时构造的括号序列左括号比右括号多 v 个的构造序列的最小长度。
转移:枚举每一位放 ( 还是放 )
放置 ( :若 s[i] == ( 则 ni = i + 1,同理 nj,dp[ni][ni][v + 1] = dp[i][j][v] + 1;
放置 ) :若 s[i] == ) 则 ni = i + 1,同理 nj,dp[ni][ni][v - 1] = dp[i][j][v] + 1;
要注意整个过程要保证 0 <= v <= 200,不在这个范围内一定不是正解
要构造最后的答案,需要记录转移的父节点。
看到这道题还以为是括号匹配问题,结果看了半天没看明白题意,看样例看的有点蒙,后来找题解发现了最后意思是构建新序列其子序列中有s和t。
欠缺还是很多。
大佬的题解博客

总结

相关知识漏洞太多了,需要补的知识有图论、树还有一些算法知识,都需要补充。
这个假期将会是很充实的一个假期。

猜你喜欢

转载自blog.csdn.net/qq_45949914/article/details/107163937