HNUCM 2021年春季蓝桥杯&天梯赛选拔赛第1场题解

本文探讨了处理大数的加法、乘法算法,并通过实例展示了如何应用于数列求解。同时,介绍了使用STL map统计整数出现频率的方法,以及在不同场景下如走楼梯问题、最长匹配括号子串、X星人密文解密等的解决方案。
摘要由CSDN通过智能技术生成

简单数列
思路:观察数列可以发现a[n]=a[n-1]*3+a[n-2]*2+a[n-3];
注意题目的数据范围最大为1000,a[100]已经爆long long 了
所以要套用大数乘法和大数加法模板

#include<bits/stdc++.h>
using namespace std;
string add(string a,string b)//只限两个非负整数相加
{
    string ans;
    int na[1200]= {0},nb[1200]= {0};
    int la=a.size();
    int lb=b.size();
    // 倒叙存储
    for(int i=0; i<la; i++)
        na[la-1-i]=a[i]-'0';
    // 倒叙存储
    for(int i=0; i<lb; i++)
        nb[lb-1-i]=b[i]-'0';
    int lmax=la>lb?la:lb;
    // 从个位开始计算
    for(int i=0; i<lmax; i++)
    {
        na[i]+=nb[i];
        na[i+1]+=na[i]/10;
        na[i]%=10;
    }
    // 去除前置0
    if(!na[lmax])
        lmax--;
    for(int i=lmax; i>=0; i--)
        ans+=na[i]+'0';
    return ans;
}
string mul(string a,string b)//高精度乘法a,b,均为非负整数
{
    const int L=1200;
    string s;
    int na[L],nb[L],nc[L],La=a.size(),Lb=b.size();//na存储被乘数,nb存储乘数,nc存储积
    fill(na,na+L,0);
    fill(nb,nb+L,0);
    fill(nc,nc+L,0);//将na,nb,nc都置为0
    for(int i=La-1; i>=0; i--)
        na[La-i]=a[i]-'0';//将字符串表示的大整形数转成i整形数组表示的大整形数
    for(int i=Lb-1; i>=0; i--)
        nb[Lb-i]=b[i]-'0';
    for(int i=1; i<=La; i++)
        for(int j=1; j<=Lb; j++)
            nc[i+j-1]+=na[i]*nb[j];//a的第i位乘以b的第j位为积的第i+j-1位(先不考虑进位)
    for(int i=1; i<=La+Lb; i++)
        nc[i+1]+=nc[i]/10,nc[i]%=10;//统一处理进位
    if(nc[La+Lb])
        s+=nc[La+Lb]+'0';//判断第i+j位上的数字是不是0
    for(int i=La+Lb-1; i>=1; i--)
        s+=nc[i]+'0';//将整形数组转成字符串
    return s;
}
int main()
{
    string a="1",b="1",c="2";
    int n;
    scanf("%d",&n);
    if(n==1)
        puts("1");
    else if(n==2)
        puts("1");
    else if(n==3)
        puts("2");
    else
    {
        for(int i=4; i<=n; ++i)
        {
            string tem=add(add(mul(c,"3"),mul(b,"2")),a);
            a=b,b=c,c=tem;
        }
        cout<<c<<endl;
    }

}

整数统计
思路:用数组来统计奇数出现的次数,但一般的数组最大范围1e7左右,所以要用到STL的map,定义map<long long,int>即可解决

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll sca(){
ll su=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))su=su*10+c-'0',c=getchar();
return su*f;
}
map<ll,int>mp;
int main()
{
    int n;
    scanf("%d",&n);
    ll x;
    for(int i=1;i<=n;++i)
        scanf("%lld",&x),mp[x]++;
    int num=0;
    for(map<ll,int>::iterator it=mp.begin();it!=mp.end();++it)
        if((it->second)&1)++num;
    printf("%d\n",num);
}

小h走楼梯
思路:每走一层的期望等于1/P1,例如P1=0.5,也就是说走两步才有可能走到上一层;

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll sca()
{
    ll su=0,f=1;
    char c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while(isdigit(c))
        su=su*10+c-'0',c=getchar();
    return su*f;
}
double a[100005],b;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
        scanf("%lf %lf",&a[i],&b);
    double ans=0;
    for(int i=1;i<n;++i)
        ans+=1.0/a[i];
    printf("%.3lf\n",ans);
}

最长匹配括号子串
dp[i]表示从s[0]到s[i]包含s[i]的最长的有效匹配括号子串长度。
dp[0]=0;
每次循环
如果s[i]是右括号,跳过前面匹配的括号序列j=i-1-dp[i-1]
判断s[j]是否是匹配的左括号,如果匹配,dp[i]=dp[i-1]+2;注意要加上匹配的左括号的前面是已匹配的括号子串长度。

#include <bit/stdc++.h>
using namespace std;
#define ll long long
char s[100010];
int dp[100010];
int solve1()
{
    int maxn = 0,len=strlen(s);
    for(int i = 0; i < len; i++)
        dp[i] = 0;
    for(int i = 1; i < len; i++)
    {
        if(s[i] == ')')
        {
            int j = i-1-dp[i-1];
            if(j >= 0 && s[j] == '(')
            {
                dp[i] = dp[i-1] + 2;
                if(j-1 >= 0)
                    dp[i] += dp[j-1];
            }
        }
        if(s[i] == ']')
        {
            int j = i-1-dp[i-1];
            if(j >= 0 && s[j] == '[')
            {
                dp[i] = dp[i-1] + 2;
                if(j-1 >= 0)
                    dp[i] += dp[j-1];
            }
        }
        if(s[i] == '}')
        {
            int j = i-1-dp[i-1];
            if(j >= 0 && s[j] == '{')
            {
                dp[i] = dp[i-1] + 2;
                if(j-1 >= 0)
                    dp[i] += dp[j-1];
            }
        }
        if(s[i] == '>')
        {
            int j = i-1-dp[i-1];
            if(j >= 0 && s[j] == '<')
            {
                dp[i] = dp[i-1] + 2;
                if(j-1 >= 0)
                    dp[i] += dp[j-1];
            }
        }
        maxn=max(maxn,dp[i]);
    }
    return maxn;
}
int main()
{
    scanf("%s", s);
    printf("%d\n", solve1());
    return 0;
}


破解X星人的密文
思路:直接按题意模拟

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll sca()
{
    ll su=0,f=1;
    char c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while(isdigit(c))
        su=su*10+c-'0',c=getchar();
    return su*f;
}
double a[100005];
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        getchar();
        char s[1002];
        gets(s);
        int ls=strlen(s)/n;
        for(int i=1;i<=ls;++i)
        {
            for(int j=1;j<=n;++j)
                putchar(s[i*n-j]);
        }
        for(int i=strlen(s)-1;i>=ls*n;--i)
            putchar(s[i]);
        putchar('\n');
    }
 
} 

斐波那契三角形
思路:简单模拟,注意数据范围就行

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll sca(){
ll su=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))su=su*10+c-'0',c=getchar();
return su*f;
}
int main()
{
    int n;
    ll a[81];
    a[1]=1,a[2]=1;
    for(int i=3;i<=80;++i)
        a[i]=a[i-1]+a[i-2];
    scanf("%d",&n);
    if(n==1)
    {
        puts("1");
        return 0;
    }
    if(n==2)
    {
        printf("1\n1 1 1\n");
    }
    printf("1\n1 1 1\n");
    for(int i=3;i<=n;++i)
    {
        printf("1");
        for(int j=2;j<=i;++j)
            printf(" %d",a[j]);
        for(int j=i-1;j>=1;--j)
            printf(" %d",a[j]);
        putchar('\n');
    }
}

简单的数数
思路:直接模拟,正数直接统计二进制1的个数,负数先转正数,再取反,负数的补码=反码+1,反码+1注意要进位

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        if(n>=0)
        {
            int ans=0;
            while(n)
            {
                if(n%2)
                    ++ans;
                n/=2;
            }
            printf("%d\n",ans);
        }
        else
        {
            int ans=0,a[33]={0},cn=0;
            n=abs(n);
            while(n)
            {
                a[++cn]=n%2;
                n/=2;
            }
            for(int i=1;i<=32;++i)
            if(a[i])a[i]=0;
            else a[i]=1;
            a[1]+=1;
            int l=1;
            while(a[l]==2)//反码进位
                a[l]=0,a[l+1]+=1,l++;
            for(int i=1;i<=32;++i)
                if(a[i])
                ++ans;
            printf("%d\n",ans);
        }
    }
}

陷阱迷宫
思路:优先队列版bfs,定义优先队列按时间由小到大排序,每次时间最短的先遍历

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
char e[105][105];
int n,k;
int vi[105][105];
int d[4][2]= {1,0,0,1,-1,0,0,-1};
struct node
{
    int x,y,ti;
    bool operator<(const node &p)const
    {
        return ti>p.ti;//定义优先队列按时间逆序排列
    }
};
int  ans=0;
void bfs()
{
    priority_queue<node> q;
    q.push({1,1,0});
    while(!q.empty())
    {
        node no=q.top();
        q.pop();
        if(no.x==n&&no.y==n)
        {
            //printf("%d\n",c);
            ans=no.ti;
            break;
        }
        if(vi[no.x][no.y])
            continue;
        vi[no.x][no.y]=1;
        for(int i=0; i<4; i++)
        {
            int xx=no.x+d[i][0];
            int yy=no.y+d[i][1];
            if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&!vi[xx][yy]&&e[xx][yy]!='1')
            {
                if(e[xx][yy]=='#')
                    q.push({xx,yy,no.ti+k+1});
                else
                    q.push({xx,yy,no.ti+1});
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; ++j)
            scanf(" %c",&e[i][j]);
    bfs();
    if(!ans)
        printf("No solution\n");
    else
        printf("%d\n",ans);
    return 0;
}

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值