2019年第十届蓝桥杯C/C++国赛b组

试题A:平方序列

在这里插入图片描述

题解
签到!

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define N 9999
int main()
{
    long long int i,j;
    for(i=2020;i<=N;i++)
    {
        long long int x=i*i-2019*2019;
        long long int jj=i*i+x;
        j=sqrt(jj);
        if(j*j==jj)
        {
            printf("%d",i+j);
            return 0;
        }
    }
    return 0;
}

答案:7020

试题B:质数拆开

在这里插入图片描述

题解
(01背包变形:我过去写的01背包以及一道uva的01背包题:https://blog.csdn.net/m0_52103105/article/details/116330899
状态转移方程:dp(i,j)+=dp(i-1,j-perm【i】);
i代表第i个素数,j代表0~2019的数,我们要求第i个素数时,j能有多少种组成方法,直到最后的dp【素数数目】【2019】就能得到最终答案

代码中省略了对第一个素数的状态转移,但我们很简单的推出第一个素数只能组成2。
注意:dp【i】【0】也要为1,因为题目说明了是若干个也就是1个素数也可以算作组成的一种。

注意:这题不能用线性动态,线性动态是要在不同顺序算不同情况下使用的,这道题是不分顺序的(而且存在标记的情况下线性动态不能使用记忆化)

线性动态求不同顺序:

#include <stdio.h>
#include <stdlib.h>
#define N 2019

int perm[N];
int flag[N+1];
int count=0;
int vis[2020];
long long int d[2020];
long long int dp(int S)
{
    int i;
    if(S==0)
        return 1;
    if(S<0)
        return 0;
    /*if(d[S]!=-1)
        return d[S];
       */
    d[S]=0;
    for(i=0;i<count;i++)
    {
        if(vis[i]==1)
            continue;
        vis[i]=1;
        d[S]+=dp(S-perm[i]);
        vis[i]=0;
    }
    return d[S];
}

int main()
{
    //普通筛选法求素数,素数的倍数不是素数
    int i,j;

    for(i=2;i<=N;i++)
    {
        if(!flag[i])
        {
            perm[count++]=i;
            for(j=i*2;j<=N;j+=i)
                flag[j]=1;
        }
    }
    memset(d,-1,sizeof(d));
    printf("%lld",dp(2019));
    return 0;
}

正确答案:

#include <stdio.h>
#include <stdlib.h>
#define N 2019

int perm[N];
int flag[N+1];
int count=0;
long long int dp[400][2020]={0};
int isPerm(int x)
{
    for(int i=0;i<count;i++)
        if(x==perm[i])
        return 1;
    return 0;
}

int main()
{
    //普通筛选法求素数,素数的倍数不是素数
    int i,j;

    for(i=2;i<=N;i++)
    {
        if(!flag[i])
        {
            perm[count++]=i;
            for(j=i*2;j<=N;j+=i)
                flag[j]=1;
        }
    }
    /*printf("count%d\n",count);
    for(i=0;i<count;i++)
        printf("%d\n",perm[i]);*/

    dp[0][2]=dp[0][0]=1;
    for(i=1;i<count;i++)
      for(j=0;j<=2019;j++)
      {
          dp[i][j]=dp[i-1][j];  //要继承上一个素数所原有的组合数
          if(j>=perm[i])
            dp[i][j]+=dp[i-1][j-perm[i]];
      }
    printf("%lld",dp[count-1][2019]);

    return 0;
}

状态压缩:
注意:要自顶向下开始,自底向上开始会改变前面的数导致后面的数重复运算而过大

#include <stdio.h>
#include <stdlib.h>

int count=0;
int flag[2020];
int perm[390];
int main()
{
    int i,j;
    for(i=2;i<=2019;i++)
    {
        if(!flag[i])
        {
            perm[count++]=i;
            for(j=i*2;j<=2019;j+=i)
                flag[j]=1;
        }
    }
    long long int dp[2020]={1,0};
    for(i=0;i<count;i++)
        for(j=2019;j>=perm[i];j--)   //要自顶向下开始因为自底向上开始会改变前面的数导致后面的数重复运算
            dp[j]+=dp[j-perm[i]];
    printf("%lld",dp[2019]);
    return 0;
}

答案:55965365465060

试题C:拼接

在这里插入图片描述

题解
要让右边图形翻转也能与左边的图形重合,也就是说我们翻转后右边的图形并没有改变,那么如何让图形不改变:就是轴对称图形!(利用由对称轴分割出来的两边图形相等)
还要注意一点:题目说的是一根木根我们切割的只是三个三角形中中间的一个,
比如说:
在这里插入图片描述
我们由此进一步确立了右边图形的对称轴该是此7x7正方形的对称轴(注意对称轴有两条),不然就会因为右边正方形的限制导致虽然图形没有改变但是无法拼接。
由于我们只能翻转右边所以我们只需要考虑一条对称轴
在这里插入图片描述
了解了最难的原理:那么我们很简单就能想出代码,就是dfs,从(0,0)有多少种到对称轴的办法。

为什么从(0,0)开始:从(0,0)开始不会少情况的,你dfs可以直接右走或往下走的,相当于从(0,1)(0,2)等等开始或者(1,0),(2,0)等等开始。至于从(1,2),(1,3)这些中间的格子开始我那个图也说明了由于右边存在一个正方形是无法拼接的情况。

#include <stdio.h>
#include <stdlib.h>

int vis[8][8];
int ans;
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};

void DFS(int x,int y)
{
    if(x+y==7)
    {
        ans++;
        return;
    }
    for(int i=0;i<4;i++)
    {
        int xx=x+dir[i][0];
        int yy=y+dir[i][1];
        if(xx>=0 && xx<=7 && yy>=0 && yy<=7 && !vis[xx][yy] && xx+yy<=7)  //注意图片把正方形分成七份有八个顶点不注意的话答案就错了
        {
            vis[xx][yy]=1;
            DFS(xx,yy);
            vis[xx][yy]=0;
        }
    }
}


int main()
{
    vis[0][0]=1;
    DFS(0,0);
    printf("%d",ans-2);  //答案还要减2去掉一直向右和一直向下的情况
    return 0;
}

答案:45406

试题D:求值

在这里插入图片描述
题解
送分题

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i,j;
    for(i=100;;i++)
    {
        int count=0;
        for(j=1;j<=i;j++)
            if(i%j==0)
            count++;
        if(count==100)
        {
            printf("%d",i);
            return 0;
        }
    }
    return 0;
}

答案:45360

试题E

有一个7X7的方格。方格左上角顶点坐标为(0,0),右下角坐标为(7,7)。
求满足下列条件的路径条数:
1、起点和终点都是(0,0)
2、路径不自交
3、路径长度不大于12
4、对于每一个顶点,有上下左右四个方向可以走,但是不能越界。
例如,图中路线,左上角顶点(0,0),路线长度为10
在这里插入图片描述
题解
dfs送分题了,但要注意按照我的写法,答案要减2。
因为题目要求不能自交,我(0,0)是没有标记的,所以有两个ans是往右走后直接往左走回来和往下走后直接往上走回来。

#include <stdio.h>
#include <stdlib.h>
//又是分成七份八个格子别想让我再上当了
int vis[8][8];
int ans=0;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int flag=0;
void DFS(int x,int y,int step)
{
    if(step>12)
        return;
    if(x==0 && y==0)
    {
        flag++;
        if(flag==2 && step<=12)
        {
            flag=1;
            ans++;
            return;
        }
    }
    for(int i=0;i<4;i++)
    {
        int xx=x+dir[i][0];
        int yy=y+dir[i][1];
        if(xx>=0 && xx<=7 && yy>=0 && yy<=7 && !vis[xx][yy])
        {
            vis[xx][yy]=1;
            DFS(xx,yy,step+1);
            vis[xx][yy]=0;
        }
    }

}

int main()
{
    DFS(0,0,0);
    printf("%d",ans-2);
    /*答案要减2,因为题目要求不能自交,我(0,0)是没有标记的,
    所以有两个ans是往右走后直接往左走回来和往下走后直接往上走回来*/
    return 0;
}

答案:206

  • 14
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值