2020牛客寒假算法基础集训营5题解

A: 模板

在这里插入图片描述 输入

4 3
WXYZ
WXY
输出

1

只需找到两个字符串中不一样的字符替换,长度补齐即可

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int a,b,ans=0;
char s1[100050],s2[100050];

int main(){
    scanf("%d%d%s%s",&a,&b,s1,s2);
    ans+=abs(a-b);
    if(a>b){swap(s1,s2);swap(a,b);}
    for(int i=0;i<a;i++)if(s1[i]!=s2[i])ans++;
    cout<<ans;
    return 0;
}

B: 牛牛战队的比赛地

在这里插入图片描述 输入

3
0 0
2 0
0 2

输出

2

说明

当在(0,0)(0,0)比赛时,到三个训练基地的最大距离是22。可以证明这是最小值。

我们设,从x坐标轴最左边向最右边移动的过程中最远点的距离为f(x),那么f(x)一定是一个先减后增的函数(仔细想一下,如果是先减后增后减后增那么一定第一次增加之前最远点就变了)所以可以直接使用三分看这里

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define max(X,Y) (X>Y?X:Y)
#define min(X,Y) (X<Y?X:Y)
using namespace std;

const int INF=0x3f3f3f3f;
const int MAX_N=1e5+50;
const double esp=1e-5;

int n,x[MAX_N],y[MAX_N],max_x=-INF,min_x=INF;

double dis(double p){
    double dis_max=-INF;
    for(int i=0;i<n;i++){
        dis_max=max(dis_max,y[i]*y[i]+(p-x[i])*(p-x[i]));
    }
    return dis_max;
}

int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d%d",&x[i],&y[i]);
        max_x=max(max_x,x[i]);
        min_x=min(min_x,x[i]);
    }
    double l=min_x,r=max_x,ml=l+(r-l)/3,mr=l+(r-l)/3*2;
    while(ml+esp<mr){
        if(dis(ml)<dis(mr))r=mr;
        else l=ml;
        ml=l+(r-l)/3.0;
        mr=l+(r-l)/3.0*2.0;
    }
    printf("%lf",sqrt(dis((ml+mr)/2)));
    return 0;
}
## C: C语言IDE 在这里插入图片描述 在这里插入图片描述 输入

#include <stdio.h>

int plus(int a, int b)
{
 return a + b;
}
int main()
{
 int a,b;
 scanf("%d%d", &a, &b);
 printf("%d\n", plus(a, b));
 return 0;
}

输出

int plus(int,int)
int main()

匹配括号,模拟即可

#include <bits/stdc++.h>
using namespace std;

string source;
void replaceAll(string &s, string oldstr, string newstr)
{
    for (string::size_type pos = 0; pos != string::npos; pos += newstr.length())
        if ((pos = s.find(oldstr, pos)) != string::npos)
            s.replace(pos, oldstr.length(), newstr);
        else
            break;
}
struct functions
{
    string inClass, name, outputType;
    vector<string> inputType;
    functions(string inClass = "", string name = "", string outputType = "void", vector<string> inputType = vector<string>(0))
        : inClass(inClass), name(name), outputType(outputType), inputType(inputType) {}
};
vector<functions> funs;
void solve(string &s)
{
    replaceAll(s, "/*", " /* ");
    replaceAll(s, "*/", " */ ");
    replaceAll(s, "//", " // ");
    replaceAll(s, "(", " ( ");
    replaceAll(s, ")", " ) ");
    replaceAll(s, "{", " { ");
    replaceAll(s, "}", " } ");
    replaceAll(s, "=", " = ");
    replaceAll(s, "\"", " \" ");
    replaceAll(s, "'", " ' ");
    replaceAll(s, ";", " ; ");
    replaceAll(s, ",", " , ");
    replaceAll(s, "+ = ", "+=");
    replaceAll(s, "- = ", "-=");
    replaceAll(s, "* = ", "*=");
    replaceAll(s, "/ = ", "/=");
    replaceAll(s, "^ = ", "^=");
    replaceAll(s, "| = ", "|=");
    replaceAll(s, "& = ", "&=");
    replaceAll(s, ":", " : ");
    replaceAll(s, " :  : ", "::");
    vector<string> tokens;
    string now = "";
    for (int i = 0; s[i]; i++)
    {
        if (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n' || s[i] == '\0')
        {
            if (now != "")
            {
                if (now == ":" && tokens.back() == ")")
                {
                    string tmpnow = "";
                    for (int j = i + 1; s[j]; j++)
                    {
                        if (s[j] == ' ' || s[j] == '\t' || s[j] == '\r' || s[j] == '\n' || s[j] == '\0')
                        {
                            if (tmpnow == "{")
                            {
                                now = "{";
                                i = j - 1;
                                break;
                            }
                            tmpnow = "";
                        }
                        else
                            tmpnow += s[j];
                    }
                    continue;
                }
                if (now == "const")
                {
                    now = "";
                    continue;
                }
                if (now == "//")
                {
                    for (int j = i; s[j]; j++)
                    {
                        if (s[j] == '\n')
                        {
                            i = j - 1;
                            break;
                        }
                    }
                    now = "";
                    continue;
                }
                if (now == "/*")
                {
                    int num = 1;
                    string tmpnow = "";
                    for (int j = i + 1; s[j]; j++)
                    {
                        if (s[j] == ' ' || s[j] == '\t' || s[j] == '\r' || s[j] == '\n' || s[j] == '\0')
                        {
                            if (tmpnow == "/*")
                                num++;
                            if (tmpnow == "*/")
                            {
                                num--;
                                if (num == 0)
                                {
                                    i = j - 1;
                                    break;
                                }
                            }
                            tmpnow = "";
                        }
                        else
                            tmpnow += s[j];
                    }
                    now = "";
                    continue;
                }
                tokens.push_back(now);
                now = "";
            }
        }
        else
            now += s[i];
    }
    int cnt = 0;
    string nowNamespace = "";
    for (int i = 1; i < (int)tokens.size(); i++)
    {
        if ((tokens[i] == "struct" || tokens[i] == "class") && tokens[i + 2] == "{")
        {
            cnt = 0;
            nowNamespace = tokens[i + 1];
            i += 2;
        }
        functions tmp(nowNamespace);
        if (tokens[i] == "{" && tokens[i - 1] == ")")
        {
            int num = 1;
            for (int j = i - 2; j >= 0; j--)
            {
                if (tokens[j] == ")")
                    num++;
                if (tokens[j] == "(")
                {
                    num--;
                    if (num == 0)
                    {
                        tmp.name = tokens[j - 1];
                        tmp.outputType = "";
                        for (int k = j - 2; k >= 0; k--)
                            if (tokens[k] != "}" && tokens[k] != "}" && tokens[k] != ";" &&
                                tokens[k].back() != ':' && tokens[k] != "inline" &&
                                tokens[k] != "static" && tokens[k][0] != '#' &&
                                tokens[k].back() != '\"' && tokens[k].back() != '>')
                                tmp.outputType = tmp.outputType == "" ? tokens[k] : tokens[k] + " " + tmp.outputType;
                            else
                                break;
                        int last = i - 2;
                        for (int k = i - 2; k >= j; k--)
                        {
                            if (tokens[k] == "(" || tokens[k] == ",")
                            {
                                string tt = "";
                                for (int t = k + 1; t < last; t++)
                                    tt = tt == "" ? tokens[t] : tt + " " + tokens[t];
                                if (tt != "")
                                    tmp.inputType.push_back(tt);
                                last = k - 1;
                            }
                            if (tokens[k] == "=" || tokens[k] == ")")
                                last = k - 1;
                        }
                        reverse(tmp.inputType.begin(), tmp.inputType.end());
                        break;
                    }
                }
            }
            funs.push_back(tmp);
            num = 1;
            for (int j = i + 1; j < (int)tokens.size(); j++)
            {
                if (tokens[j] == "{")
                    num++;
                if (tokens[j] == "}")
                {
                    num--;
                    if (num == 0)
                    {
                        i = j;
                        break;
                    }
                }
            }
            continue;
        }
        if (nowNamespace != "")
        {
            if (tokens[i] == "{")
                cnt++;
            if (tokens[i] == "}")
            {
                cnt--;
                if (!cnt)
                    nowNamespace = "";
            }
        }
    }
}
int main()
{
    char ch;
    while ((ch = getchar()) != EOF)
        source += ch;
    solve(source);
    for (auto &i : funs)
    {
        if (i.outputType != "")
            cout << i.outputType << " ";
        if (i.inClass != "")
            cout << i.inClass << "::";
        cout << i.name << "(";
        for (int j = 0; j < (int)i.inputType.size(); j++)
            cout << i.inputType[j] << (j == (int)i.inputType.size() - 1 ? ")" : ",");
        if ((int)i.inputType.size() == 0)
            cout << ")";
        cout << endl;
    }
    return 0;
}

D: 牛牛与牛妹的约会

在这里插入图片描述 输入

2
3 -1
1 2

输出

3.442249570
1.000000000

只需对于每一次枚举判断闪现是否比直接走值得就可以了,然后判断最后一次闪现过头后回头走是否比不闪现更快即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        double a,b;
        scanf("%lf%lf",&a,&b);
        if(a<0){a=-a;b=-b;}
        if(a<b)printf("%.9f\n",b-a);
        else{
            double ans=0,lstp=a;
            while(a>b&&(a-cbrt(a)>1)){ans+=1;lstp=a;a=cbrt(a);}
            ans=min(ans-1+abs(b-lstp),ans+abs(b-a));
            printf("%.9f\n",ans);
        }
    }
    return 0;
}

E: Enjoy the game

在这里插入图片描述 输入

2

输出

Alice

说明

先手必须拿走一张牌,然后后手拿走了另一张牌,游戏结束。

找规律可以发现,当张数是\(2^n\)的时候输出Alice,否则输出Bob。 如果不是2^n ,先手直接拿等同于其二进制最低位的数量的张数,后手将无力还击~

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int main(){
    long long t;
    cin>>t;
    if((t&(t-1))==0)cout<<"Alice";
    else cout<<"Bob";
    return 0;
}

F: 碎碎念

在这里插入图片描述 输入

3
3
3 3
1 4
1 5

输出

2
7
11

说明

第一组询问:可以是三个AC,或者一个RJ。
第二组询问:可以是1~4个AC,一个AC和一个RJ(共2种顺序),或者一个RJ。
第三组询问:可以有1~5个AC,两个AC和一个RJ(共3种顺序),一个AC和一个RJ(共2种顺序),或者一个RJ。

备注

AC RJ AC AC 和 AC AC AC RJ 虽然都是3个AC,1个RJ,但是因为提交顺序的不同,视为不同种类。

考虑动态规划,

  1. dp[i][0]表示一共说了i句话,最后的一次评测记录是AC,那么如果我说了i句话,最后一次是AC,那么说之前一定是说了i-1句话,但是因为这一次是AC,所以上一次(i-1句话时)不论是AC还是RJ都可以转移到i,即: dp[i][0]=dp[i-1][0]+dp[i-1][1]
  2. dp[i][1]表示一共说了i句话,最后的一次评测记录是RJ,那么如果我说了i句话,最后一次是RJ,那么说之前一定是说了i-k句话,因为这一次是RJ所以上一次是AC dp[i][1]=dp[i-k][0]
  3. 边界条件是dp[0][0]=1,dp[0][1]=0 我们可以读入所有的\(l_i,r_i\)然后预处理\(0 \to max(r_i)\)的dp值,并计算前缀和s,我们需要输出\(l_i \to r_i\)直接输出\(s[r_i]-s[l_i-1]\)即可
#include <stdio.h>
const int N=1e5+100;
const int mod=1e9+7;

int dp[N][3];

int main(void){
    int x;
    scanf("%d",&x);
    dp[0][0]=1;
    for(int i=1;i<100100;++i){
        dp[i][0]=(dp[i-1][0]+dp[i-1][1])%mod;
        if(i>=x){
            dp[i][1]=dp[i-x][0];
        }
    }
    dp[0][2]=1;
    for(int i=1;i<100100;++i){
        dp[i][2]=(dp[i][0]+dp[i][1])%mod;
        dp[i][2]=(dp[i][2]+dp[i-1][2])%mod;
    }
    int q;
    scanf("%d",&q);
    while(q--){
        int l,r;
        scanf("%d %d",&l,&r);
        printf("%d\n",(dp[r][2]-dp[l-1][2]+mod)%mod);
    }
    return 0;
}

G: 街机争霸

在这里插入图片描述 输入

3 3 1 3
&&A
###
&&L
2 1 RIGHT

输出

2

说明

如果用*代表僵尸的位置,则起始时状态为
&&A
*##
&&L
一个单位时间后,牛能上走,僵尸向右走,两者并没有碰上
&&A
#*L
&&#
再一个单位时间后,牛能向上走,追上牛牛,取得胜利。
&&L
##*
&&#

输入

4 4 1 2
L#&A
##&#
#&##
####
3 3 DOWN

输出

Oh no

说明

初始状态
L#&A
##&#
#&*#
####
一单位时间后
##&A
L#&#
#&##
##*#
二单位时间后
##&A
##&#
L&*#
####
三单位时间后
##&A
##&#
#&##
L#*#
四单位时间后
##&A
##&#
#&*#
#L##
牛能如果再向左走的话就会跟僵尸碰个正着,而且不论牛能怎么往回走,在(4,3)总能遇见僵尸,所以他失败了。

迷宫题,似乎用图论算法处理不了,于是想到bfs,但是最毒瘤的是他可以走到他以前走过的空格...那BFS是永远都走不完的!,但是我们发现这个题有一个特殊性质,僵尸的运动长度是固定的. 所以每隔2k-2个单位长度,僵尸的位置会重复一次,所以对于一个点,这个点在时间t的僵尸状态与t+2k-2的时候在这个点状态是重复的!!所以往回走的时候如果这个点在t-n(2k-2)访问过就算重复,所以不访问,否则访问。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

int dx[10]={-1,0,1,0},dy[10]={0,1,0,-1};        //上右下左
int n,m,p,k,G[550][550],JS[100][3],sx,sy,ex,ey;
bool vst[550][550][120];
queue<pair<int,pair<int,int> > >q;

inline bool ok(int x,int y,int t){
    int tt=t%(2*k-2);
    // cout<<">>>>> Try "<<x<<" "<<y<<" "<<t<<endl;
    if(x<0||x>=n||y<0||y>=m||vst[x][y][tt]||G[x][y]=='&')return false;
    // cout<<">>>>> Pass 1 "<<endl;
    for(int i=0;i<p;i++){
        int pos_jsx=JS[i][0]+dx[JS[i][2]]*(tt>k-1?2*k-2-tt:tt);
        int pos_jsy=JS[i][1]+dy[JS[i][2]]*(tt>k-1?2*k-2-tt:tt);
        // cout<<"@ JS "<<i<<" jsx= "<<pos_jsx<<" jsy= "<<pos_jsy<<" xx= "<<(tt>k-1?2*k-2-tt:tt)<<endl;
        if(x==pos_jsx&&y==pos_jsy)return false;

    }
    // cout<<">>>>> Pass 2 "<<endl;
    vst[x][y][tt]=true;
    return true;
}

int bfs(int sx,int sy){
    q.push(make_pair(sx,make_pair(sy,0)));
    vst[sx][sy][0]=true;
    while(!q.empty()){
        int nx=q.front().first,ny=q.front().second.first,time=q.front().second.second;
        q.pop();
        // cout<<">>> "<<nx<<" "<<ny<<" "<<time<<endl;
        for(int i=0;i<4;i++){
            int tx=nx+dx[i],ty=ny+dy[i];
            if(tx==ex&&ty==ey)return time+1;
            if(ok(tx,ty,time+1))
                q.push(make_pair(tx,make_pair(ty,time+1)));
        }
    }
    return -1;
}

int main(){
    scanf("%d%d%d%d",&n,&m,&p,&k);
    getchar();
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            scanf("%c",&G[i][j]);
            if(G[i][j]=='A'){ex=i;ey=j;}
            if(G[i][j]=='L'){sx=i;sy=j;}
        }
        getchar();
    }

    // for(int i=0;i<n;i++){
    //     for(int j=0;j<m;j++)
    //         cout<<G[i][j]<<" ";
    //     cout<<endl;
    // }


    for(int i=0;i<p;i++){
        char d[10];
        scanf("%d%d%s",&JS[i][0],&JS[i][1],d);
        JS[i][0]--;JS[i][1]--;
        if(d[0]=='U')JS[i][2]=0;
        else if(d[0]=='R')JS[i][2]=1;
        else if(d[0]=='D')JS[i][2]=2;
        else JS[i][2]=3;
    }

    // cout<<JS[0][2]<<"***********\n";

    int ans=bfs(sx,sy);
    if(ans==-1)cout<<"Oh no";
    else cout<<ans;
    return 0;
}

H: Hash

在这里插入图片描述 输入

abcdef 11

输出

abcdeq

实际上,可以看到这就是一个26进制数,但是,对于所有的排列 ,可以排满所有的数字吗?似乎不可以,于是 最开始的思路是dfs,但是T掉了 但是实际上,他是可以完全覆盖所有的数字,所以直接写一个函数26->10进制,然后+mod,转26进制

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

char s[10];
long long mod,n=0;

int main(){
    while(scanf("%s%d",s,&mod)==2){
        n=0;
        for(int i=0;i<=5;i++)n=n*26+s[i]-'a';
        n+=mod;
        for(int i=5;i>=0;i--){
            s[i]='a'+n%26;
            n/=26;
        }
        if(n)printf("-1\n");
        else printf("%s\n",s);
    }
    return 0;
}

I: I题是个签到题

在这里插入图片描述 输入

9 100
100 100 100 100 100 100 100 100 100

输出

Yes

签到题,直接判定

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n,m,num_i,a[50];

bool cmp(int a,int b){
    return a>b;
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){scanf("%d",&a[i]);}
    num_i=a[9];
    if(num_i>=0.8*m){cout<<"Yes";return 0;}
    sort(a+1,a+1+n,cmp);
    if(num_i>=a[3]){cout<<"Yes";return 0;}
    cout<<"No";
    return 0;
}

J: 牛牛战队的秀场

在这里插入图片描述 输入

4 1
1 2

输出

1.414214

用弧度的定义\(\alpha=\frac{l}{r}\)

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

int r,n,i,j;
double pi=atan(1)*4;

int main(){
    scanf("%d%d%d%d",&n,&r,&i,&j);
    double l=2*sin(pi/n)*r;
    printf("%.6f",l*min(abs(i-j),n-abs(i-j)));
    return 0;
}