写二叉树程序时为什么总是报运行时错误_2019 CSPJ 入门组初赛解析(阅读程序和完善程序)...

16.

#include #include using namespace std;char st[100];int main() {    scanf("%s", st);    int n = strlen(st);    for (int i = 1; i <= n; ++i) {        if (n % i == 0) {            char c = st[i - 1];            if (c >= 'a')                st[i - 1] = c - 'a' + 'A';        }    }    printf("%s", st);    return 0;}

•判断题

(1)输入的字符串只能由小写字母或大写字母组成。()

答案:错。对于输入没有限制。

(2)若将第8行的“i = 1”改为“i = 0”,程序运行时会发生错误。()

答案:对。如果i=0,下面st[i-1]会出错,数组越界。

(3)若将第8行的“i <= n”改为“i * i<= n”,程序运行结果不会改变。()

答案:错。i*i<=n的话,i多数情况到达不了n,至少i=n的情况没有执行。

(4)若输入的字符串全部由大写字母组成,那么输出的字符串就跟输入的字符串一样。()

答案:对。程序的功能是将n因子位置上的小写字母变成大写字母,如果本来就是大写字母,输出等于输入。

•选择题

(5)若输入的字符串长度为18,那么输入的字符串跟输出的字符串相比,至多有()个字符不同。

A. 18

 B. 6

 C.10

 D. 1

答案:B。18有6个因子:1,2,3,6,9,18,这些位置上的字符可能会变。

(6)若输入的字符串长度为(),那么输入的字符串跟输出的字符串相比,至多有36个字符不同。

A. 36

 B.100000

 C. 1

 D.128

答案:B。4个选项中,有36个因子的选项只有B。

程序大意:对于字符串中的第i位,如果i是n的约数,并且第i个字符比'a'(97)要大,就减少'a'(97)-'A'(65)。

17.

#includeusing namespace std;int n, m;int a[100], b[100];int main() {    scanf("%d%d", &n, &m);    for (int i = 1; i <= n; ++i)        a[i] = b[i] = 0;    for (int i = 1; i <= m; ++i) {        int x, y;        scanf("%d%d", &x, &y);        if (a[x] < y && b[y] < x) {            if (a[x] > 0)                b[a[x]] = 0;            if (b[y] > 0)                a[b[y]] = 0;            a[x] = y;            b[y] = x;        }    }    int ans = 0;    for (int i = 1; i <= n; ++i) {        if (a[i] == 0)            ++ans;        if (b[i] == 0)            ++ans;    }    printf("%d", ans);    return 0;}

假设输入的n和m都是正整数,x和y都是在[1, n]的范围内的整数,完成下面的判断题和单选题:

•判断题

(1)当m>0时,输出的值一定小于2n。()

答案:对。ans统计的是a数组和b数组0的数量,输入的x和y都非0,那么a数组和b数组各至少有一个非0的值。

(2)执行完第27行的"++ans"时,ans —定是偶数。()

答案:错。统计是按照元素编号的顺序进行的,如果有一个x < y的配对,那么统计完b中的第x个元素时ans会变成奇数。

(3)a[i]和b[i]不可能同时大于0。()

答案:错。如果ab中的第i个元素进行配对,则a[i]和b[i]同时>0。

(4)右程序执行到第13行时,x总是小于y,那么第15行不会被执行。()

答案:错。x总是小于y不代表x之前没有别的配对,如果x之前有别的配对,则照样会执行清理。

反例:可以依次对(1, 1)和(1, 2)进行配对,则第二次操作时会对1的配对进行清理。

•选择题

(5)若m个x两两不同,且m个y两两不同,则输出的值为()

A. 2n-2m

 B.2n+2

 C.2n-2

 D.2n

答案:A。x和y两两不同时,每次都会成功进行配对,m次会产生m对配对,所以和0配对的元素数量为2n - 2m。

(6)若m个x两两不同,且m个y都相等,则输出的值为()

A. 2n-2

 B.2n

 C.2m

 D.2n-2m

答案:A。x各不相同,y每次相等,那么x和y配对时每次都会清理掉之前的配对,最终只会产生一组配对,所以和0配对的元素数量为2n - 2。

程序大意:有ab两组元素,两组元素之间可以两两配对(0可以任意次配对)。初始时所有元素都和另一组的0配对,每次考察a组的第x个元素和b组的第y个元素,如果这两个元素之前的配对交叉了,则把它们之前的配对元素都重新配对到0,并将这两个元素配对。最后求有多少个元素和0配对。

18.

#include 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数组有重复的数字,则程序运行时会发生错误。()

答案:错。重复的a值不影响查找最小元素,所以不会出错。

(2)如果b数组全为0,则输出为0。()

答案:对。所有元素的b值为0,则权重也全部为0,总权重就是0。可以根据程序观察一下,所有的计算都会和b值进行相乘,没有常数,也可以猜到结果为0。

•选择题

(3)当n=100时,最坏情况下,与第12行的比较运算执行的次数最接近的是:()。

A. 5000

 B.600

 C. 6

 D.100

答案:A。每个子树中的每个节点都会进行一次比较,最坏情况下二叉树有n层(每次只划分出一个子树),所以比较次数为100 + 99 + ... + 1 = 5050,与5000最接近。

(4)当n=100时,最好情况下,与第12行的比较运算执行的次数最接近的是:()。

A. 100

 B. 6

 C.5000

 D.600

答案:D。最少情况下二叉树有log层,100个节点约为7层,每一层的总节点数估算为n,则比较次数大约为700次,与600最接近。

(5)当n=10时,若b数组满足,对任意0<=i都有b[i] = i + 1,那么输出最大为()。

A. 386

 B.383

 C.384

 D.385

答案:D。为了让输出最大,则需要让b值大的元素深度尽可能的大,一条链可以达成这个要求,则权值为1^2 + 2^2 + ... + 10^2 = 385。

(6)当n=100时,若b数组满足,对任意0<=i都有b[i]=1,那么输出最小为()。

A. 582

 B.580

 C.579

 D.581

答案:B。每个元素的b值均为1,则计算的是元素的深度和,所以需要让二叉树的深度尽可能小。那么第一层可以有1个节点,第二层可以有2个,第三层可以有4个,以此类推。权值为1 * 1 + 2 * 2 + 4 * 3 + 8 * 4 + ... + 32 * 6 + 37 * 7 = 580,注意最后一层节点数不满。

 程序大意:将一个元素序列根据a值构造成一棵二叉树,每次在序列中选择a值最小且最靠前的元素作为根,根之前的序列构建左子树,根之后的序列构建右子树。

最后求出每个节点的深度乘以b值的和。

19.(矩阵变幻)有一个奇幻的矩阵,在不停的变幻,其变幻方式为:数字0变成矩阵

0 0

0 1

数字1变成矩阵

1 1

1 0

最初该矩阵只有一个元素0,变幻n次后,矩阵会变成什么样?

例如,矩阵最初为:[0];矩阵变幻1次后:

0 0

0 1

矩阵变幻2次后:

0 0 0 0

0 1 0 1

0 0 1 1

0 1 1 0

输入一行一个不超过10的正整数n。输出变幻n次后的矩阵。试补全程序。

提示:

"<表示二进制左移运算符,例如(11)_2<<1=(1100)_2

而“^”表示二进制异或运算符,它将两个参与运算的数中的每个对应的二进制位—进行比较,若两个二进制位相同,则运算结果的对应二进制位为0,反之为1。

#include using namespace std;int n;const int max_size = 1 << 10;int res[max_size][max_size];void recursive(int x, int y, int n, int t) {    if (n == 0) {        res[x][y] = ①;        return;    }    int step = 1 << (n - 1);    recursive(②, n - 1, t);    recursive(x, y + step, n - 1, t);    recursive(x + step, y, n - 1, t);    recursive(③, n - 1, !t);}int main() {    scanf("%d", &n);    recursive(0, 0, ④);    int size = ⑤;    for (int i = 0; i < size; i++) {        for (int j = 0; j < size; j++)            printf("%d", res[i][j]);        puts("");    }    return 0;}

①处应填()

A. n%2

 B. 0

 C. t

 D. 1

答案:C。如果填写一个常数,那整个矩阵就是常数矩阵了。而且这个t在函数中传递下来就只有这里可以直接使用,其他地方都是参数。所以除了t也没别的选择。

②处应填()

A. x-step,y-step

 B. x,y-step

 C.x-step,y

 D. x,y

答案:D。分成四个子矩阵,看不懂程序的根据另外两行都能猜出来。另外两行中可以发现,x和y分别有加与不加两种可能,组合起来正好四种可能,所以肯定没减号。

③处应填()

A. x-step,y-step

 B.x+step,y+step

 C.x-step,y

 D. x,y-step

答案:B。

④处应填()

A. n-1,n%2

 B.n,0

 C.n,n%2

 D.n-1,0

答案:B。通过函数中的参数命名可以猜出肯定是n了。然后测一下n = 1就可以确定第二个参数填0。

⑤处应填()

A. 1<

 B.1<

 C.n+1

 D.1<

答案:B。测一下n = 1就知道肯定是1 << n。

 程序思路:考察分治算法,通过递归的方式来对矩阵进行填充。

从0作为元开始,每次将当前的矩阵划分成四个子矩阵,对应了一次变换,并根据变化规则分别得到四个子矩阵的元。

子矩阵用递归的方式进行填充,直到不再变换。

20. (计数排序)计数排序是一个广泛使用的排序方法。下面的程序使用双关键字计数排序,将n对10000以内的整数,从小到大排序。

例如有三对整数(3,4)、(2,4)、(3,3),那么排序之后应该是(2,4)、(3,3)、(3,4)。

输入第一行为n,接下来n行,第i行有两个数a[i]和b[i],分别表示第i对整数的第一关键字和第二关键字。

从小到大排序后输出。

数据范围:1

提示:应先对第二关键字排序,再对第一关键字排序。数组ord[]存储第二关键字排序的结果,数组res[]存储双关键字排序的结果。

试补全程序。

#include #include using namespace std;const int maxn = 10000000;const int maxs = 10000;int n;unsigned a[maxn], b[maxn],res[maxn], ord[maxn];unsigned cnt[maxs + 1];int main() {    scanf("%d", &n);    for (int i = 0; i < n; ++i)         scanf("%d%d", &a[i], &b[i]);    memset(cnt, 0, sizeof(cnt));    for (int i = 0; i < maxs; ++i)        ①; // 利用 cnt 数组统计数量    for (int i = 0; i < n; ++i)         cnt[i + 1] += cnt[i];    for (int i = 0; i < n; ++i)        ②; // 记录初步排序结果    memset(cnt, 0, sizeof(cnt));    for (int i = 0; i < n; ++i)        ③; // 利用 cnt 数组统计数量    for (int i = 0; i < maxs; ++i)        cnt[i + 1] += cnt[i];    for (int i = n - 1; i >= 0; --i)        ④ // 记录最终排序结果    for (int i = 0; i < n; i++)        printf("%d %d", ⑤);    return 0;}

①处应填()

A. ++cnt [i]

 B.++cnt[b[i]]

 C.++cnt[a[i] * maxs + b[i]]

 D.++cnt[a[i]]

答案:B。先排第二关键字,肯定要选只有b[i]的选项。

②处应填()

A. ord[--cnt[a[i]]] = i

 B.ord[--cnt[b[i]]] = a[i]

 C.ord[--cnt[a[i]]] = b[i]

 D.ord[--cnt[b[i]]] = i

答案:D。按照第二关键字排序,答案肯定跟b[i]相关。每个元素有ab两个值,ord中只能记录元素编号,所以肯定是保存i。

③处应填()

A. ++cnt[b[i]]

 B.++cnt[a[i] * maxs + b[i]]

 C.++cnt[a[i]]

 D.++cnt [i]

答案:C。现在根据第一关键字排序,答案应该是跟a[i]有关。有个与a[i]和b[i]同时有关的选项明显越界了,肯定不可能。

④处应填()

A. res[--cnt[a[ord[i]]]] = ord[i]

 B.res[--cnt[b[ord[i]]]] = ord[i]

 C.res[--cnt[b[i]]] = ord[i]

 D.res[--cnt[a[i]]] = ord[i]

答案:A。ord里面存的是元素编号,肯定不能跟a[i]配合使用,而是要跟a[ord[i]]配合使用。同时这是根据第一关键字排序,应该是与a有关。

⑤处应填()

A. a[i], b[i]

 B.a[res[i]], b[res[i]]

 C.a[ord[res[i]]]j b[ord[res[i]]]

 D.a[res[ord[i]]]j b[res[ord[i]]]

答案:B。res和ord里面存的都是元素编号,肯定不能嵌套使用。答案要按照排序输出,肯定与res或者ord有关。

 程序思路:就是计数排序,和桶排序的原理有些相似。

核心思想是,先统计每个值x出现的次数,再计算出小于等于每个值x的元素数量t,这个数量t决定了排序时值为x的元素应该从第t大开始逐个往前排,这样就完成了单关键字排序。

双关键字排序时,要先排第二关键字,让元素都是根据第二关键字有序的。然后根据第一关键字重新排序时,首先可以保证结果是第一关键字有序的,同时对于第一关键字相同的元素,它们已经按照第二关键字排好序了,所以最后的结果是双关键字排序。

d09221fd645b3586ea13468ecd1dc444.gif

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值