蓝桥杯 2021年 国赛 填空题 (带宽,纯质数,完全日期,最小权值)


填空题前三个是5分一个,最后一个10分 A.带宽 小蓝家的网络带宽是 200Mbps,请问,使用小蓝家的网络理论上每秒钟最多可以从网上下载多少MB 的内容。 Mbps即Mb per(每) s,即多少Mb每秒,b是bit,8bit是一个字节Byte Mbps =1024Kb/s=1024K*(1/8)Byte/s=128KB/s 200*128 / 1024 = 25

B.纯质数 (5分)
如果一个正整数只有 1 和它本身两个约数,则称为一个质数(又称素数)。
前几个质数是: 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 , ? ? ? 
如果一个质数的所有十进制数位都是质数,我们称它为纯质数。例如:2,3,5,7,23,37 都是纯质数,而 11 , 13 , 17 , 19 , 29 , 31不是纯质数。当然 1 , 4 , 35 也不是纯质数。
请问,在 1到 20210605 中,有多少个纯质数?

#include
using namespace std;
//                  0,1,2,3,4,5,6,7,8,9
int f[20210606]={0,0,1,1,0,1,0,1,0,0};
bool isPWZ(int n){
    while(n){
        if(f[n%10]==0) return false;
        n/=10;
    }
    return true;
}
bool isZ(int n){
    for(int i=2;i<=sqrt(n);i++) if(n%i==0) return false;
    return true;
}
int main(){
    int sum=0; 
    for(int i=2;i<=20210605;i++) if(isPWZ(i)) if(isZ(i)) sum++; 
    cout<endl; //1903
    return 0;
} 

C.完全日期

如果一个日期中年月日的各位数字之和是完全平方数,则称为一个完全日期。
例如: 2021年 6月 5日的各位数字之和为 2 + 0 + 2 + 1 + 6 + 5 = 16 而 16是一个完全平方数,它是 4的平方。所以 2021年 6月 5日是一个完全日期。
例如: 2021年 6月23日的各位数字之和为 2 + 0 + 2 + 1 + 6 + 2 + 3 = 16 ,是一个完全平方数。所以 2021年 6月 23日也是一个完全日期。
请问,从 2001年 1月 1 日到 2021 年 12月 31日中,一共有多少个完全日期?

#include
using namespace std;
int M[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int getV(int n){
    int ans=0;
    while(n){
        ans+=n%10;
        n/=10;
    }
    return ans;
}

bool ok(int m){
    double mq=sqrt(m);
    return mq-(int)mq<10e-6;//这里因为浮点精度问题所以存在一个极小的偏差,忽略这个偏差才能得到正确结果 
}
int main(){
    int sum=0;
    int startYear=2001,startMonth=1,startDay=1;
    int endYear=2021,endMonth=12,endDay=31;//2+0+2+1+1+2+3+1=12不是完全平方 
    while(!(startYear==endYear &&startMonth==endMonth &&startDay==endDay)){
        //备注:先在逻辑上正确,再斟酌过程上的简化,简化可能会带来无法察觉的逻辑变化. 
        int tmp= getV(startYear)+getV(startMonth)+getV(startDay);
        if(ok(tmp)){
            sum++;
        }
        startDay++;
        if(startDay>M[startMonth]){
            startMonth++;
            startDay=1; 
        } 
        if(startMonth>12){
            startYear++;
            startMonth=1;
            M[2]=(startYear%400==0||startYear%4==0 && startYear%10!=0)?29:28; 
        }         
    }
    cout<endl;
    return 0;
} 
 D.最小权值

对于一棵有根二叉树 T,小蓝定义这棵树中结点的权值 W ( T )如下:
空子树的权值为 0。
如果一个结点 v有左子树 L, 右子树 R,分别有 C ( L )和 C ( R ) 个结点,则 W ( v ) = 1 + 2 W ( L ) + 3 W ( R ) + ( C ( L ) ) *( C ( L ) ) *C ( R )
树的权值定义为树的根结点的权值。
小蓝想知道,对于一棵有 2021个结点的二叉树,树的权值最小可能是多
少?

本题可以用dp做,参考背包问题可以得到 dp[i][j]=min(dp[i-1][j],dp[i-1][j-v[i]]+w[i])这个递推式

这里主要是dp[i][j][...]...=w这个多维数组的意义赋予比较难去思考,

本题我们抽出值为数量的关键主体:节点数,权值,总节点数(定量)

题目的目标是找到最小的权值,而权值和节点数能计算出新的权值,我们要求的是权值,所以数组的值的含义就能暂时确定为权值,数组必然有一维与节点数有关,我们不妨赋予第一维i以总节点数的意义

这样dp就确定下来了

对dp的一些理解,可以略过

dp可以看作是对输入不同的维度的数据确定一个唯一的答案的一个函数,通过不断优化最后得到正确结果的过程.这个结果不是一下就出来的,

像是背包问题,在背包容积确定和物品个数和价值确定的情况下便能得到最优解,可能存在一样的不过对于最值问题知道它即可,所以dp[i][j]的含义是选择到第i个物品空间为j时的最优解,

为什么w(价值)和v(体积)不参与dp的一个维度呢?其实这和高数中的一个知识点有关.

如果你有几个表达式,变量很多,但是你仔细理一理,发现可以去掉很多变量,最后简化成只有两个或三个变量,其实基本的思想是消元,背包问题中每一个i和它的w和v唯一对应,所以顶层简化成i,但是光有物品清单无法得到结果,所以还需要背包容积作为第二个维度,即dp[i][j]表示为dp[物品个数][空间大小],而本题的输入只有总节点数,所以总节点数便能确定最终值

继续正题

dp[i] = min(dp[i], 1ll + 2ll * dp[j] + 3ll * dp[i - j - 1] + j * j * (i - j - 1));

知道总节点数,肯定还得知道左节点和右节点数,不妨设置左节点数为i,右节点数为j,对于根节点,x=i+j+1

然后我们得到递推式dp[i+j+1]=min(dp[i+j+1],1+2*dp[i]+3*dp[j]+i*i*j);其实我们是按照总节点来去递推的dp[i+j+1]不符合我们遍历的要求,我们把i+j+1都换成x;

然后得到 dp[x]=min(dp[x],1+2*dp[x-j-1]+3*dp[j]+(x-j-1)*(x-j-1)*j)然后第一层从1到2021表示总节点数,第二层(有几维就需要几层循环)从0到2020表示右节点数j

#include 
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
ll dp[2022];
int main() {
    memset(dp, INF, sizeof(dp));
    dp[0] = 0;
    for (ll x = 1; x <= 2021; x++) {
        for (ll j = 0; j < x; j++) {
            dp[x]=min(dp[x],1+2*dp[x-j-1]+3*dp[j]+(x-j-1)*(x-j-1)*j);
        }
    }
    cout<2021]<<endl;
    return 0;
}

然后运行,得到2653631372