2019 CSP-J 真题 题目、答案以及解析
目錄
- 寫在前面的話
- 題目
- 解析
- 單項選擇題
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 閱讀程序題
- 一、
- 1
- 2
- 3
- 4
- 5
- 6
- 二、
- 1
- 2
- 3
- 4
- 5
- 6
- 三、
- 1
- 2
- 3
- 4
- 5
- 6
- 完善程序題
- 一、
- 1
- 2
- 3
- 4
- 5
- 二、
- 1
- 2
- 3
- 4 //部分思路(我到現在也沒搞懂)懂了的跟我說一聲啊!!!
- 5
- 尾聲
寫在前面的話
最近快要CSP了,為了幫助大家[zì jǐ]更好的復習歷年真題特地作此題解一篇。
我寫完之后看了一遍,感覺有點啰嗦,大家看不看隨意。
還有,有沒有大佬講講閱讀程序最后一題的倒數第二問?
蒟蒻我看不懂😭😭😭😭😭😭😭😭😭😭
題目
洛谷版
CCF版
建議使用CCF版。因為洛谷版有打印錯誤。
兩種版本題目都有題目、答案所以這里不過多講解,下面是解析內容↓
解析
單項選擇題
1.
這題沒什么可講的,死知識,自己背就完了
2.
與運算即數位對齊,若兩個數相同數位都為True,運算結果即為True, 否則為False
我們來將兩數執行上述操作:
1 1 1 0 1 1 1 0 0 1 0 1 1 1
0 1 0 1 1 0 1 1 1 0 1 0 1 1
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
0 1 0 0 1 0 1 0 0 0 0 0 1 1
答案就出來了。
3.
32位即為32個二進制位,一個字節8個二進制位,32 / 8 = 4
于是乎答案出來了。
4.
b從1~C,共循環了C次,減去C個1即為-C
于是乎,答案出來了
5.
折半查找的復雜度為O(long n),log 100 為6~7之間,最大值即為7
6.
鏈表數據結構的特點為:
- 執行插入、刪除等操作消耗O(1)的時間復雜度;
- 訪問元素需要消耗O(n)的復雜度;
- 不必事先預估所需空間;
- 不可隨機訪問元素。
- 所消耗的空間與普通數組成正比例
所以,答案大家可以看出來了吧
7.
這題沒什么好說的,直接人肉暴力一下就好了。
注意!袋子和袋子沒有區別,球和球也沒有區別,所以:
- 8 0 0 0 0
和 - 0 8 0 0 0
沒有區別
有如下結果:
一共18個答案。
8.
用一維數組儲存二叉樹的方法題目中也說了:
(根結點的下標為1,若某結點的下標為i ,則其左孩子位于下標2i處、右孩子位于下標2i+l處)
那么,問題來了:如果某非葉子節點沒有左孩子,那么這一個下標放什么呢?
空著。
因為如果把其他節點放過來了,就無法滿足上述條件了,那么就無法正確訪問該二叉樹了。
所以,我們要將其余位置補全,使其變成一棵完全二叉樹
原圖如下:
更改為完全二叉樹后如下:
可知共需15個節點的空間
(圖畫的有點抽象,請諒解)
9.
這個你要是不會,請到小學回爐重造
10.
將兩數分別分解質因數即可。
11.
類似于背包問題
數據范圍較小,人肉DP即可
12.
重復最少 即抽取結果各個花色盡可能平均,若每種花色各抽4張,一共12張,還需再抽一張,故最少5張花色相同。
13.
可顛倒的五位數:
- 第一位有5種情況(0、1、6、8、9)
- 第二位有5種情況(0、1、6、8、9)
- 第三位有3種情況(6、9不行,因為顛倒后與原來不符,而中間位顛倒后依然在原來的地方)
- 第四、第五位只有1種情況(必須與第一、第二位對應)
根據乘法原理,共5x5x3 = 75種情況。
愿意的可以人肉暴力一下。
14.
這是一道二叉樹的遍歷題。后中推前。
可以通過后序遍歷 + 中序遍歷還原二叉樹。
還原過程如下:
后序遍歷:D G J H E B I F C A
中序遍歷:D B G E H J A C I F
最終,可以還原二叉樹。
然后進行前序遍歷即可。
P.S.P.S.P.S. 如果連前中后序遍歷都不知道的同學,請自行學習相關知識。
15.
死知識:“計算機領域的諾貝爾獎”為“圖靈獎”
閱讀程序題
一、
#include <cstdio> #include <cstring> 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
對
若進行上述更改,第九行 則會發生÷0問題,RE
3
很明顯錯誤。
舉個反例:aaaaaaaa
原程序運行結果:AAaAaaaA
修改后運行結果:AAaaaaaa
大家可以自行復制代碼嘗試一下。
4
正確
簡單看一看代碼就會發現:特定字符的ASCLL碼≥97時,才會進行修改,而大寫字母Z的ASCLL碼也才71。
所以不會進行任何更改
5
至多
符合情況的都要算。
有兩層篩選條件:
第九行:n % i == 0
第十一行:c >= 'a'
只要輸入的都是小寫字母,就可以滿足第十一行的條件。
而第九行的條件可理解為:“是n的因數”
若輸入的字符串長度為18
即:n = 18
18的因數共有6個:1、2、3、6、9、18
可知答案。
6
本題是上一題的變體,相信看懂了上一題的同學一定可以明白:本題問的是“?有36個因數”
這里教大家一個快速求因數個數的方法:
先將其分解質因數
然后將所有質因數的指數+1,在相乘。
我們來逐一分解:
36 = 22 * 32
(2+1)(2+1) = 9
100000 = 2? * 5?
(5+1)(5+1) = 36
答案就出來了。
有興趣的同學可以證明一下我提供的方法。
二、
#include<cstdio> using 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; }1
錯誤
可以嘗試證明一下:
第一次運行一定會進入第13行的if語句,則一定會有兩個數被身為非零的數。
- 之后如果沒有進入if語句,則這兩個數一直非零,直到運行結束,ans = n - 2
- 如果進入了,則會將兩個數設為0,兩個數設為非0,此時最好情況下0的個數也不會改變
綜上所述:ans最大為n-2
2
錯誤
舉個反例:
3 1
1 2
在執行完第十行的循環之后,數組如下:
| 數組a: | 2 | 0 | 0 |
| 數組b: | 0 | 1 | 0 |
在第一次執行完27行的ans++時,ans == 1
3
錯
舉個反例:
1 1
1 1
很明顯了吧?
4
依然錯
舉個反例:
2 1
1 2
大家自己算一算
5
自己造一組數據即可:
例如:
3 3
1 1
2 2
3 3
ans:0
有興趣的同學可以自行證明,愿意的話發給我看看
6
同樣,自己造數據
3 3
1 1
2 1
3 1
ans:4
三、
#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
錯誤
相等的數字對該程序沒有任何影響
這題選錯的講講你的思路唄😁😁😁
2
正確
返回值有兩種情況:
- return 0
- return lres + rres + depth * b[mink];
第一種情況不受b數組的影響,不用考慮
第二種情況受四個因素的影響:左孩子、右孩子、數組b、depth
由于數組b全部為零,第二種情況只受左右孩子的影響。
左右孩子又有兩種情況了:葉子節點、非葉子節點
前一個繼續往下遞歸,后一個return 0
所以,發現了嗎?返回值要么是0, 要么是0+0+0
所以, ans = ……
3
這題的代碼是一道遞歸題,它的 最好/最壞 情況類似于快速排序
所以,最壞情況為每次將最左邊/右邊的一個端點去掉,一共需要去掉n次而每一層遞歸都會比較n次
總復雜度約為O(n2),n = 100時,約為10000次,最接近的即為A
4
最好情況下,每次都能二分整個區間,復雜度為O(n log n),n = 100時,在600到700之間,故選D
5
要想輸出最大,遞歸的深度也要最大,每次都去掉左端點(權值最小)
這樣,ans將會達到12 + 22 + 32 + … + n2,n = 10時,答案為385。
6
Tips:Tips:Tips: 使用“洛谷有題”的同學注意了,題目打錯了,原題應當為:
當 n=100 時,若 b 數組滿足,對于任意 0 ≤ i < n,都有 b[i] = 1,那么輸出最小為( )
要想結果最小,就要讓二叉樹盡可能平衡,最平衡的二叉樹即為完全二叉樹
用100個節點搭建一棵完全二叉樹,每層的節點數依次為:1,2,4,8,16,32,37
將每層的節點數與其對應的權值相乘,求出總和即可。
(有沒有人像我一樣 把最后一層的節點數算成64,然后抓耳撓腮想不明白?有的話在評論區里打一個心有靈犀😁😁😁)
完善程序題
一、
#include <cstdio> 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; }1
t表示這一個格子應當寫什么
就算你想不明白這一點,換個角度想:要是這里不用t,t有什么用?
2
根據一開始的例子,可知:每一次變換之后,右下格變為與自己不同的一項,右、下、自己變為自己原本的數字。
而這一行t沒有變,所以是右、下、自己中的一個。
而右、下已經寫過了,所以x、y不用變
3
用CCF提供的題庫的同學注意了!
該題的解析似乎有錯:
當然,也有可能是我錯了,歡迎指出
下面是我的思路:
這里t做了變化,所以是右下角,即兩個都+step
4
初始位置應該為0(見題目),深度應該為n(初始狀態為遞歸0次)
5
矩陣最終大小應當為2n,即 1 << n
二、
本題與計數排序有著密切的關系,不會的同學可以先學習一下相關知識
#include <cstdio> #include <cstring> 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; }1
有了注釋應該很容易理解,這里在對第二個參數進行計數排序(一下稱之為初排)
2
ord[i]表示:在輸入時下標為i的數字 在對第二個關鍵字排序之后的下標
知道了這個應該很好選
3
對第一個關鍵字進行計數排序(以下稱之為復排)
4 //部分思路(我到現在也沒搞懂)懂了的跟我說一聲啊!!!
答案為 res[--cnt[a[ord[i]]]] = ord[i]
我們來理一理思路(一層層數組看的我頭暈)
首先,i表示輸入時的第i個數
ord[i] 表示 第i個數現在(初排后)的位置
a[ord[i]] 表示 i初排后的位置上 初排前的元素 的第一個關鍵字
--cnt[a[ord[i]]] 表示 i初排后的位置上 初排前的元素 的第一個關鍵字 復排后應該在的位置
res[--cnt[a[ord[i]]]] 表示 復排后 i初排后的位置上 初排前的元素 的第一個關鍵字 復排后應該在的位置的下標
res[--cnt[a[ord[i]]]] = ord[i] 表示……
將 復排后 i初排后的位置上 初排前的元素 的第一個關鍵字 復排后應該在的位置的下標 設為 初排后 i 的位置
然后……
我就懵了
懇請大佬幫忙!!!
5
res存儲的就是最終結果了
尾聲
這篇文章對你的幫助大嗎?
謝謝閱讀。
有什么改進建議歡迎評論區講。
總結
以上是生活随笔為你收集整理的2019 CSP-J 真题 题目、答案以及解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 王阳明《何陋轩记》
- 下一篇: 2020年最值得期待的几大BPM厂商一览