码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces Round #605 (Div. 3)

时间:2020-01-13 19:38:05      阅读:50      评论:0      收藏:0      [点我收藏+]

标签:lin   number   sizeof   problem   字符串   splay   复杂度   keyboard   转移   

A. Three Friends

题目链接

题目大意

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

解题思路

  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 \times\left(n+1\right)}{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]=\begin{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]=\begin{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\)(如果\(1\leq (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\)是赛后交流之后补的。终于涨分了,呜呜呜

Codeforces Round #605 (Div. 3)

标签:lin   number   sizeof   problem   字符串   splay   复杂度   keyboard   转移   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!