CSP-J 2019 入门级 第一轮(初赛) 阅读程序(3)

【题目】

CSP-J 2019 入门级 第一轮(初赛) 阅读程序(3)

#include <iostream>
using namespace std;
const int maxn = 10000;
int n;
int a[maxn];
int b[maxn];
int f(int l, int r, int depth) {
    if (l > r)
        return 0;
    int min = maxn, mink;
    for (int i = l; i <= r; ++i) {
        if (min > a[i]) {
            min = a[i];
            mink = i;
        }
    }
    int lres = f(l, mink - 1, depth + 1);
    int rres = f(mink + 1, r, depth + 1);
    return lres + rres + depth * b[mink];
}
int main() {
    cin >> n;
    for (int i = 0; i < n; ++i)
        cin >> a[i];
    for (int i = 0; i < n; ++i)
        cin >> b[i];
    cout << f(0, n - 1, 1) << endl;
    return 0;
}

判断题

  1. 如果 a 数组有重复的数字,则程序运行时会发生错误。()
  2. 如果 b 数组全为 0,则输出为 0。()

选择题
3. 当n=100时,最坏情况下,与第 12 行的比较运算执行的次数最接近的是:()
A. 5000
B. 600
C. 6
D. 100
4. 当n=100时,最好情况下,与第12行的比较运算执行的次数最接近的是:()
A. 100
B. 6
C. 5000
D. 600
5. 当n=10时,若b数组满足,对任意0≤i<n,都有b[i]=i+1,那么输出最大为()。
A. 386
B. 383
C. 384
D. 385
6. 当n=100时,若b数组满足,对任意0≤i<n,都有b[i]=1,那么输出最小为()。
A. 582
B. 580
C. 579
D. 581

【题目考点】

1. 递归
2. 树

【解题思路】

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i)
        cin >> a[i];
    for (int i = 0; i < n; ++i)
        cin >> b[i];
    cout << f(0, n - 1, 1) << endl;
    return 0;
}

输入a、b数组,下标从0到n-1,调用递归函数f,传入区间左右端点,第三个参数是层数,起始层数为1。

int f(int l, int r, int depth) {
    if (l > r)
        return 0;
    int min = maxn, mink;
    for (int i = l; i <= r; ++i) {
        if (min > a[i]) {
            min = a[i];
            mink = i;
        }
    }
    int lres = f(l, mink - 1, depth + 1);
    int rres = f(mink + 1, r, depth + 1);
    return lres + rres + depth * b[mink];
}

函数参数是左右端点和深度。
l<=r时进行递归,l>r时结束递归,是递归出口。
i从l到r循环,求a数组的最小值min,最小值的下标mink。
整个区间分为[l, mink-1],[mink+1, r]两个区间,对两个区间进行递归调用,深度depth+1。
返回值为[l, mink-1]求出的值加[mink+1, r]求出的值加深度乘以b数组中mink位置的值。
该算法建立了一个树形结构,对于每个区间,选a数组中最小值为根结点,对最小值左侧和右侧区间分别用相同方式建树。函数的返回值为每个结点下标对应b数组中的值乘以深度的加和。

【答案及解析】

1. 如果 a 数组有重复的数字,则程序运行时会发生错误。()
答:F
如果有重复数字,求最小值时会认为区间第一个数字是最小值,程序不会有错误

2. 如果 b 数组全为 0,则输出为 0。()
答:T
最后结果是每个结点深度乘以b数组中的值加和,如果b数组的值都为0,那么结果一定为0。

3. 当n=100时,最坏情况下,与第 12 行的比较运算执行的次数最接近的是:()
A. 5000
B. 600
C. 6
D. 100

答:A
第12行是if (min > a[i]),该语句进行比较的次数,就是a数组中元素被遍历到的次数。
最坏情况下,每次选出的最小值在区间的左端或右端,这样下一次面对的区间的长度是上一次面对的区间长度减1。
最开始面对的区间长度为n,因此区间中元素被遍历到的次数为:
n + ( n − 1 ) + ( n − 2 ) + . . . + 1 = ( 1 + n ) n / 2 n+(n-1)+(n-2)+...+1=(1+n)n/2 n+(n1)+(n2)+...+1=(1+n)n/2
将n=100代入,得 ( 1 + 100 ) ∗ 100 / 2 = 5050 (1+100)*100/2=5050 (1+100)100/2=5050,A选项5000最接近该数值,选A。

4. 当n=100时,最好情况下,与第12行的比较运算执行的次数最接近的是:()
A. 100
B. 6
C. 5000
D. 600

答:D
第12行是if (min > a[i]),该语句进行比较的次数,就是a数组中元素被遍历到的次数。
最好情况下,每次选出的最小值的位置在区间的中点,这样将整个区间分为两个子区间,遍历两个子区间的长度加和为n。两个子区间又分为4个子区间,4个子区间的长度加和为n,……
整个区间不断一分为二,直到每个区间长度为1,也就是在求n除以2多少次后为1,除的次数为 log ⁡ 2 n \log_2{n} log2n,对于每次分治,所有子区间的长度加和都为n,因此对于数组中元素的总遍历次数近似为 n log ⁡ 2 n n\log_2{n} nlog2n
将n=100代入,得到 100 ∗ log ⁡ 2 100 ≈ 100 ∗ 6 = 600 100*\log_2{100}\approx 100*6=600 100log21001006=600,选D。
第3、4题可以使用类似分析“快速排序平均时间复杂度、最坏时间复杂度”的分析方法来分析。

5. 当n=10时,若b数组满足,对任意0≤i<n,都有b[i]=i+1,那么输出最大为()。
A. 386
B. 383
C. 384
D. 385

答:D
b数组中下标0到9位置的值分别为1到10
为了使结果最大,那么应该让第1层的值为1,第2层的值为2,。。。,第10层的值为10,结果为:
1 2 + 2 2 + . . . + 1 0 2 1^2+2^2+...+10^2 12+22+...+102,已知 1 2 + 2 2 + . . . + n 2 = n ( n + 1 ) ( 2 n + 1 ) / 6 1^2+2^2+...+n^2=n(n+1)(2n+1)/6 12+22+...+n2=n(n+1)(2n+1)/6
1 2 + 2 2 + . . . + 1 0 2 = 10 ∗ 11 ∗ 21 / 6 = 385 1^2+2^2+...+10^2=10*11*21/6=385 12+22+...+102=101121/6=385,选D。(不会公式的话,硬算也是可以的。)

6. 当n=100时,若b数组满足,对任意0≤i<n,都有b[i]=1,那么输出最小为()。
A. 582
B. 580
C. 579
D. 581

答:B
b数组中下标0到99位置的值都为1,那么最后的结果为所有结点层数加和
使得层数尽量少。要想使层数尽量少,那么每次a数组最小值的下标应该在整个区间中点的位置
d p [ i ] dp[i] dp[i]表示长为i的区间根据题目要求构成的二叉树中,每结点层数加和的最小值。
对于长为i的区间,如果a数组最小值下标为区间的第x位置,则将其分为长为x-1和长为i-x两段区间,两段区间生成的树的结点层数加和分别为 d p [ x − 1 ] dp[x-1] dp[x1] d p [ i − x ] dp[i-x] dp[ix],而这两个树又要作为
第x位置元素的子树,树中所有结点的层数都要加1,再加上第x位置元素在第1层,所以
d p [ i ] = d p [ x − 1 ] + d p [ i − x ] + x − 1 + i − x + 1 = d p [ x − 1 ] + d p [ i − x ] + i dp[i] = dp[x-1]+dp[i-x]+x-1+i-x+1=dp[x-1]+dp[i-x]+i dp[i]=dp[x1]+dp[ix]+x1+ix+1=dp[x1]+dp[ix]+i
对于长为100的区间,中点应该在第50个数字的位置,将整个区间分为长为49和长为50的区间。
d p [ 100 ] = d p [ 49 ] + d p [ 50 ] + 100 dp[100] = dp[49]+dp[50]+100 dp[100]=dp[49]+dp[50]+100
对于长为49的区间,中点在第25个数字的位置,将整个区间分为两个长为24的区间。
以下情况以此类推,不再赘述。
d p [ 49 ] = d p [ 24 ] ∗ 2 + 49 dp[49] = dp[24]*2+49 dp[49]=dp[24]2+49
d p [ 50 ] = d p [ 24 ] + d p [ 25 ] + 50 dp[50] = dp[24]+dp[25]+50 dp[50]=dp[24]+dp[25]+50
d p [ 100 ] = d p [ 24 ] ∗ 3 + d p [ 25 ] + 199 dp[100] = dp[24]*3+dp[25]+199 dp[100]=dp[24]3+dp[25]+199
d p [ 24 ] = d p [ 12 ] + d p [ 11 ] + 24 dp[24] = dp[12]+dp[11]+24 dp[24]=dp[12]+dp[11]+24
d p [ 25 ] = 2 ∗ d p [ 12 ] + 25 dp[25]=2*dp[12]+25 dp[25]=2dp[12]+25
d p [ 100 ] = d p [ 12 ] ∗ 5 + d p [ 11 ] ∗ 3 + 296 dp[100] = dp[12]*5+dp[11]*3+296 dp[100]=dp[12]5+dp[11]3+296
d p [ 12 ] = d p [ 6 ] + d p [ 5 ] + 12 dp[12] = dp[6]+dp[5]+12 dp[12]=dp[6]+dp[5]+12
d p [ 11 ] = 2 ∗ d p [ 5 ] + 11 dp[11]=2*dp[5]+11 dp[11]=2dp[5]+11
d p [ 100 ] = d p [ 5 ] ∗ 11 + d p [ 6 ] ∗ 5 + 389 dp[100] = dp[5]*11+dp[6]*5+389 dp[100]=dp[5]11+dp[6]5+389
易知 d p [ 1 ] = 1 , d p [ 2 ] = 3 , d p [ 3 ] = 5 dp[1]=1,dp[2]=3,dp[3]=5 dp[1]=1dp[2]=3dp[3]=5
d p [ 6 ] = d p [ 2 ] + d p [ 3 ] + 6 = 14 dp[6]=dp[2]+dp[3]+6=14 dp[6]=dp[2]+dp[3]+6=14
d p [ 5 ] = 2 ∗ d p [ 2 ] + 5 = 11 dp[5]=2*dp[2]+5=11 dp[5]=2dp[2]+5=11
d p [ 100 ] = 11 ∗ 11 + 14 ∗ 5 + 389 = 580 dp[100] = 11*11+14*5+389=580 dp[100]=1111+145+389=580,选B。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值