2019年CSP提高組初賽答案及解析


原網址https://www.bilibili.com/read/cv3833576/

一、單項選擇題

1. 若有定義:int a=7; float x=2.5,y=4.7;則表達式x+a%3*(int)(x+y)%2的值是:(    )

A.0.000000          B.2.750000           C.2.500000          D.3.500000 

解析:D。基礎,考察數據類型和算術優先級。

 

 

2. 下列屬於圖像文件格式的有(   )

A.WMV          B.MPEG          C.JPEG         D.AVI

解析:C。計算機基礎知識,其他三個是視頻格式。

 

 

3. 二進制數11 1011 1001 0111 和 01 0110 1110 1011 進行邏輯或運算的結果是(   )。

A.11 1111 1111 1101      B.11 1111 1111 1101         C.10 1111 1111 1111        D.11 1111 1111 1111

解析:D。位運算基本知識,兩位上有一位為1的時候結果就為1。0|0=0;1|0=1;0|1=1;1|1=1。

 

 

4. 編譯器的功能是(  )

A.將源程序重新組合

B.將一種語言(通常是高級語言)翻譯成另一種語言(通常是低級語言)

C.將低級語言翻譯成高級語言

D.將一種編程語言翻譯成自然語言

B。編譯器是將高級語言(如C++)編譯成計算機能夠理解的二進制0和1(機器語言、低級語言)

 

5. 設變量x為float型且已賦值,則以下語句中能將x中的數值保留到小數點后兩位,並將第三位四舍五入的是(   )

A.X=(x*100+0.5)/100.0;

B.x=(int)(x*100+0.5)/100.0;

C.x=(x/100+0.5)*100.0;

D.x=x*100+0.5/100.0;

B。主要考察強制類型轉換,加0.5再轉成int是四舍五入常用操作。

 

 

6. 由數字1,1,2,4,8,8所組成的不同的4位數的個數是(   )

A.104        B.102        C.98           D.100

B。排列組合問題,也可以用窮舉算。 若由4種不同數字即1、2、4、8有A(4,4)=24種; 若有且只有2個數一樣,共有1124;1128;1148;1288;1488;2488六類,共6*A(4,4)/A(2,2)=72種; 若1、1、8、8組合有A(4,4)/(A(2,2)*2)=6種;

 

7. 排序的算法很多,若按排序的穩定性和不穩定性分類,則(    )是不穩定排序。

A.冒泡排序 B.直接插入排序 C.快速排序 D.歸並排序 

C。排序的穩定性特點是排序完成后,之前相同的元素排序不會改變。快速排序在排序時在交換中間元素時可能會打亂順序。如3、1、1、2、1、6、7、8、9,在一開始3與中間1交換后,穩定性已被打破。

 

8. G是一個非連通無向圖(沒有重邊和自環),共有28條邊,則該圖至少有(   )個頂點。

A.10          B.9        C.11          D.8

B。要求最小的點就是要盡可能占用邊, n 個點的完全無向圖最多占用n*(n+1)/2 條邊,n=8的時候是8*7/2=28,意味着8個頂點最多有28條邊。由於題目是求非連通圖,則再加上單獨第9個點。

 

9. 一些數字可以顛倒過來看,例如0、1、8顛倒過來看還是本身,6顛倒過來是9,9顛倒過來看還是6,其他數字顛倒過來都不構成數字。類似的,一些多位數也可以顛倒過來看,比如106顛倒過來是901。假設某個城市的車牌只有5位數字,每一位都可以取0到9。請問這個城市有多少個車牌倒過來恰好還是原來的車牌,並且車牌上的5位數能被3整除?(     )

A.40          B.25        C.30          D.20

B。第1、2位有(0、1、8、6、9)五個數字,第3位有(0、1、8)三個數字,第4、5位由第1、2位決定。由於0,1,8模3正好余0,1,2,所以其他位確定則第3位自然確定,共5*5=25種。

 

10. 一次期末考試,某班有15人數學得滿分,有12人語文得滿分,並且有4人語、數都是滿分,那么這個班至少有一門得滿分的同學有多少人?(   )

A.23          B.21          C.20          D.22  

A。容斥原理,至少一門滿分人數=數學滿分+語文滿分-語文數學滿分=15+12-4=23。

 

11. 設A和B是兩個長為n的有序數組,現在需要將A和B合並成一個排好序的數組,請問任何以元素比較作為基本運算的歸並算法,在最壞情況下至少要做多少次比較?( )  

A.n²          B.n log n          C.2n         D.2n-1

D。兩個數組從小到大依次比較,哪邊小哪邊入數組,當某一數組全部計入結果數組后,剩下的也依次進入。最好的情況是數組A所有數都比數組B第一個數小,只要比較n次。最壞情況是全部比較完,最后AB只剩最后一個數比較,總比較次數就是2n-1。

 

12. 以下哪個結構可以用來存儲圖?(            )

A.棧          B.二叉樹          C.隊列      D.鄰接矩陣  

D。數據結構基礎。

 

13. 以下哪些算法不屬於貪心算法(                 )。

A.Di.jkstra算法       B.Floyd算法       C.Prim算法          D.Kruskal算法

B。Floyd算法枚舉了全部情況自然不是貪心,其他算法均有取最小值。

 

14. 有一個等比數列,共有奇數項,其中第一項和最后一項分別是2和118098,中間一項是486,請問一下哪個數是可能的公比?(         )。

A.5          B.3      C.4          D.2

B。直接代入看是否整除可以快速求得答案。可令公比為q,2*q^(2n-2)=118098,得q^(n-1)=248,四個選項中只有3是248的約數。

 

15. 有正實數構成的數字三角形排列形式如圖所示。第一行的數為a(1,1),第二行a(2,1),a(2,2),第n行的數為a(n,1),a(n,2),…,a(n,n)。從a(1,1)開始,每一行的數a(i,j)只有兩條邊可以分別通向下一行的兩個數a(i+1,j)和a(i+1,j+1)。用動態規划算法找出一條從a(1,1)向下通道a(n,1),a(n,2),…,a(n,n)中某個數的路徑,使得該路徑上的數之和最大。 令C[i][j]是從a(1,1)到a(i,j)的路徑上的數的最大和,並且C[i][0]= C[0][j]=0,則C[i][j]=( )

 

A.max{C[i-1][j-1],C[i-1][j]}+ a(i,j)      B.C[i-1][j-1]+C[i-1][j]      C.max{C[i-1][j-1],c[i-1][j]}+1      D.max{C[i][j-1],C[i-1][j]}+ a(i,j)

A。每個點只能從上方兩個點過來,自然取最大的加a(i,j)。

二、閱讀程序

1.

 1 #include <cstdio>
 2 using namespace std;  3 int n;  4 int a[100];  5 
 6 int main() {  7     scanf("%d", &n);  8     for (int i = 1; i <= n; ++i)  9         scanf("%d", &a[i]); 10     int ans = 1; 11     for (int i = 1; i <= n; ++i) { 12         if (i > 1 && a[i] < a[i - 1]) 13             ans = i; 14         while (ans < n && a[i] >= a[ans + 1]) 15             ++ans; 16         printf("%d\n", ans); 17  } 18     return 0; 19 }

程序分析: 本段程序的目的是找到每個a[i]之后第一個大於a[i]的位置(下標ans+1)。代碼簡單,即使看不懂也可以代入幾個數字去試驗,畢竟選擇題。

 

(1)   第16行輸出ans時,ans的值一定大於i。(            )

錯。只要14行while循環不執行,則ans=i,如n=1,則ans等於i。

 

(2)程序輸出的ans小於等於n。(         )

對。ans初始值為i小於n,且小於n是其自增的一個條件,顯然不會超過。

 

(3)若將第12行的“<”改為“!=”,程序輸出的結果不會改變。(         )

對。改成!=只是增加了一些沒意義的比較,對結果沒有影響。

 

 

(4)當程序執行到第16行時,若ans-i>2,則a[i+1]≦a[i]。(           )

對。因為ans+1是第一個大於a[i]的位置,所以從a[i+1]至a[ans]都是小於等於a[i]的。

 

(5)若輸入的a數組是一個嚴格單調遞增的數列,此程序的時間復雜度是( )。

A.O(logn)          B.O(n^2)        C.O(nlog n)        D. O(n)

D。嚴格單調遞增則14行必定不執行,while循環每次只執行一次,時間復雜度為n。

 

(6)最壞情況下,此程序的時間復雜度是(   )。

A. O(n^2)      B. O(logn)          C. O(n)         D. O(nlog n)

A。最壞的情況為嚴格單調遞減,14行if判斷每次都執行,while循環每次都查找到n,時間復雜度為n+(n-1)+……+2+1=n*(n+1)/2,即O(n^2)。

 

 2.

 1 #include <iostream>
 2 using namespace std;
 3 
 4 const int maxn = 1000;
 5 int n;
 6 int fa[maxn], cnt[maxn];
 7 
 8 int getRoot(int v) {
 9     if (fa[v] == v) return v;
10     return getRoot(fa[v]);
11 }
12 
13 int main() {
14     cin >> n;
15     for (int i = 0; i < n; ++i) {
16         fa[i] = i;
17         cnt[i] = 1;
18     }
19     int ans = 0;
20     for (int i = 0; i < n - 1; ++i) {
21         int a, b, x, y;
22         cin >> a >> b;
23         x = getRoot(a);
24         y = getRoot(b);
25         ans += cnt[x] * cnt[y];
26         fa[x] = y;
27         cnt[y] += cnt[x];
28     }
29     cout << ans << endl;
30     return 0;
31 }

程序分析: 本題就是一個並查集操作,getRoot函數是查詢根節點,循環中對集合進行合並。

 

 

(1)輸入的a和b值應在[0,n-1]的范圍內。(         )

對。輸入的a、b是集合的下標,自然應該在[0,n-1]之間。

 

(2)   第16行改成“fa[i]=0;”, 不影響程序運行結果。(        )

錯。未合並前初始根節點是其本身,為0顯然不符合題意。

 

(3)   若輸入的a和b值均在[0, n-1]的范圍內,則對於任意0≤i<n,都有0≤fa[i]<n。(          )

對。fa[i]是根節點,不管在怎么合並,根節點必然也是在[0,n-1]之間。

 

(4)  若輸入的a和b值均在[0,n-1]的范圍內,則對於任意0≤i<n,都有1≤cnt[i]≤n。(           )

錯。cnt[i]是合並之后集合的元素個數,嚴謹的並查集操作cnt[i]是不會超過n的,但是本題沒有判斷是否重復合並。所以如果先輸入(1,2),(3,4),之后一直不斷輸入(2,4),最后cnt[4]會超過n。

 

(5)   當n等於50時,若a、b的值都在[0,49]的范圍內,且在第25行時x總是不等於y,那么輸出為(           )。

A.1276          B.1176          C.1225          D.1250

C。本題前提下cnt[i]表示當前集合的元素個數,每次執行都是兩個集合合並,我們可令每次都是單個集合合並進入大集合,ans=1*1+1*2+1*3+……1*48+1*49=49*(49+1)/2=1225。

 

(6)   此程序的時間復雜度是(      )。

A. O(n)          B. O(logn)         C. O(n^2)      D. O(nlogn)

C。getRoot是依次查找上一個父元素,沒有“壓縮路徑”,時間復雜度最差為n,所以總的時間復雜度為n^2。

 

3.t是s的子序列的意思是:從s中刪去若干個字符,可以得到t;特別的,如果s=t,那么t也是s的子序列;空串是任何串的子序列。例如:"acd"是“abcde”的子序列,“acd"是“acd”的子序列,但"adc” 不是“abcde”的子序列。
s[x..y]表示s[x] ...s[y]共y-x+l個字符構成的字符串,若x>y則 s[x..y]是空串。t[x..y]同理。

 1 #include <iostream>
 2 #include <string>
 3 using namespace std;
 4 const int max1 = 202;
 5 string s, t;
 6 int pre[max1], suf[max1];
 7 
 8 int main() {
 9     cin >> s >> t;
10     int slen = s.length(), tlen = t.length();
11 
12     for (int i = 0, j = 0; i < slen; ++i) {
13         if (j < tlen && s[i] == t[j]) ++j;
14         pre[i] = j; // t[0..j-1] 是 s[0..i] 的子序列
15     }
16 
17     for (int  i = slen - 1 , j = tlen - 1; i >= 0; --i) {
18         if(j >= 0 && s[i] == t [j]) --j;
19         suf[i]= j; // t[j+1..tlen-1] 是 s[i..slen-1] 的子序列
20     }
21 
22     suf[slen] = tlen -1;
23     int ans = 0;
24     for (int i = 0, j = 0, tmp = 0; i <= slen; ++i){
25         while(j <= slen && tmp >= suf[j] + 1) ++j;
26         ans = max(ans, j - i - 1);
27         tmp = pre[i];
28     }
29     cout << ans << endl;
30     return 0;
31 }

程序分析: 本題主要需了解兩個數組的含義,pre[i]表示s[0..i]至多可以從前往后匹配到t串的哪一個字符,此時t[0..pre[i]-1]是s[0..i]的子序列。sub[i]用於記錄s[i..slen-1]至多從后到前匹配到t的哪一個字符,此時t[suf[i]+1..tlen-1]是s[i..slen-1]的子序列。本題是求s中連續刪除至多幾個字母后,t仍然是s的子序列。

 

(1)   程序輸出時,suf數組滿足:對任意0≤i<slen,suf[i] ≤suf[i+1]。(         )

對。從15至19行可以看出,sub數組的值是從尾部往前減小或不變,所以suf[i]≤suf[i+1]。

 

(2)  當t是s的子序列時,輸出一定不為0。(          )

錯。有題目目的可知,當t=s時輸出為0。

 

(3)  程序運行到第23行時,“j-i-1”一定不小於0(         )

錯。若第一循環時while不執行,則j-i-1為-1,如s="bacb",t="ac"。

 

(4) 當t是s的子序列時,pre數組和suf數組滿足:對任意0≤i<slen,pre[i]>suf[i+1]+1(     )

錯。由含義可知若t是s子序列,t[0..pre[i]-1],t[sub[i+1]+1..lent-1]是s[0..i],s[i+1..lens-1]的子序列,不會重疊,即pre[i]-1< sub[i+1]+1,即pre[i] <= sub[i+1]+1。

 

(5)   若tlen=10,輸出為0,則slen最小為(       )。 A. 10        B. 12 C.0          D.1

D。若t不是s子串(或t==s)輸出都為0,但為保證程序執行,最少應輸入一個字符。

 

(6)   若tlen=10,輸出為2,則slen最小為(       )。 A.0            B.10 C.12          D.1

C。輸出為2說明slen最多連續刪除2個后為10,所以最小為12。

 

三、完善程序(單選題,每小題3分,共計30分)

1.(匠人的自我修養)一個匠人決定要學習n個新技術。要想成功學習一個新技術,他不僅要擁有一定的經驗值,而且還必須要先學會若干個相關的技術。學會一個新技術之后,他的經驗值會增加一個對應的值。給定每個技術的學習條件和習得后獲得的經驗值,給定他已有的經驗值,請問他最 多能學會多少個新技術。

輸入第一行有兩個數,分別為新技術個數n (l<=n<=10^3),以及己有經驗值(<=10^7)。

接下來n行。第i行的兩個正整數,分別表示學習第i個技術所需的最低經驗值(<=10^7),以及學會第i個技術后可獲得的經驗值(<=10^7)

接下來n行。第i行的第一個數mi0<=mi<n),表示第ii個技術的相關技術數量。緊跟着m個兩兩不同的數,表示第ii個技術的相關技術編號。

輸出最多能學會的新技術個數。

下面的程序以(n^2)的時間復雜度完成這個問題,試補全程序。

#include<cstdio>
using namespace std;
const int maxn = 1001;

int n;
int cnt[maxn];
int child [maxn][maxn];
int unlock[maxn];
int threshold[maxn], bonus[maxn];
int points;
bool find(){
    int target = -1;
    for (int i = 1; i <= n; ++i)
        if(① && ②){
            target = i;
            break;
    }
    if(target == -1)
        return false;
    unlock[target] = -1;
    ③
    for (int i = 0; i < cnt[target]; ++i)
        ④
    return true;
}

int main(){
    scanf("%d%d", &n, &points);
    for (int i = 1; i <= n; ++i){
        cnt[i] = 0;
        scanf("%d%d", &threshold[i], &bonus[i]);
    }
    for (int i = 1; i <= n; ++i){
        int m;
        scanf("%d", &m);
        ⑤
        for (int j = 0; j < m; ++j){
            int fa;
            scanf("%d", &fa);
            child[fa][cnt[fa]] = i;
            ++cnt[fa];
        }
    }

    int ans = 0;
    while(find())
        ++ans;

    printf("%d\n", ans);
    return 0;
}

程序分析: 程序每次都先學習一個已經達到條件但為學習的技能,學習后更新經驗值和其他技能與該技能有關的學習條件,不斷重復至沒有技能可以學。unlock數組為對應技能需學習的前置技能數,大於0說明有前置技能要學,為-1表示已學習。

 

(1) ①處應填( )

A. unlock[i]<=0       B. unlock[i]>=0        C. unlock[i]==0       D. unlock[i]==-1

C。學習技能條件一,需要的前置技能數為0且為學習。

 

(2) ②處應填( )

A. threshold[i]>points       B. threshold[i]>=points      C. points>threshold[i]      D. points>=threshold[i]

D。學習技能條件二,總經驗達到該技能的經驗要求。

 

(3) ③處應填( )

A. target = -1        B. --cnt[target]        C. bbonus[target]       D. points += bonus[target]

D。技能學習后總經驗增加。

 

(4) ④處應填( )

A. cnt [child[target][i]] -=1       B. cnt [child[target][i]] =0         C. unlock[child[target][i]] -= 1          D. unlock[child[target][i]] =0

C。即使看不懂,也應該猜到是學完技能后,相關技能的前置條件-1。

 

(5) ⑤處應填( ) A. unlock[i] = cnt[i] B. unlock[i] =m C. unlock[i] = 0 D. unlock[i] =-1

B。定義每個技能學習需要的前置技能數。

 

2.(取石子)Alice和Bob兩個人在玩取石子游戲。他們制定了n條取石子的規則,第ii條規則為:如果剩余石子的個數大於等於a[i]且大於等於b[i], 那么他們可以取走b[i]個石子。他們輪流取石子。如果輪到某個人取石子, 而他無法按照任何規則取走石子,那么他就輸了。一開始石子有m個。請問先取石子的人是否有必勝的方法?

輸入第一行有兩個正整數,分別為規則個數n (l<n<64),以及石子個數 m (<=10^7)。

接下來nn行。第i行有兩個正整數a[i]b[i]。(l<=a[i]<=10^7,l<=b[i]<=64l)

如果先取石子的人必勝,那么輸出“Win”,否則輸出“Loss”。

提示:

  可以使用動態規划解決這個問題。由於b[i]不超過64,所以可以使用64位無符號整數去壓縮必要的狀態。

  status是勝負狀態的二進制壓縮,trans是狀態轉移的二進制壓縮。

試補全程序。

代碼說明:

~表示二進制補碼運算符,它將每個二進制位的0變為1、1變為0;

而“^”表示二進制異或運算符,它將兩個參與運算的數中的每個對應的二進制位一一進行比較,若兩個二進制位相同,則運算結果的對應二進制位為0,反之為1。

ull標識符表示它前面的數字是unsigned long long類型。

 1 #include <cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn = 64;
 5 int n, m;
 6 int a[maxn], b[maxn];
 7 unsigned long long status, trans;
 8 bool win;
 9 int main(){
10     scanf("%d%d", &n, &m);
11     for (int i = 0; i < n; ++i)
12         scanf("%d%d", &a[i], &b[i]);
13     for(int i = 0; i < n; ++i)
14         for(int j = i + 1; j < n; ++j)
15             if (aa[i] > a[j]){
16                 swap(a[i], a[j]);
17                 swap(b[i], b[j]);
18             }
19     status = ①;
20     trans = 0;
21     for(int i = 1, j = 0; i <= m; ++i){
22         while (j < n && ②){
23             ③;
24             ++j;
25         }
26         win = ④;
27         ⑤;
28     }
29 
30     puts(win ? "Win" : "Loss");
31 
32     return 0;
33 }    

程序分析:通常我們可以設置bool數字f[i]表示有i個石子時有無先手必贏策略。若對於i個石子有先手必贏策略,則存在j(a[j]<=i且b[j]<=i)使得i-b[j]個石子先手無必贏策略,則得到轉移方程f[i]=OR{!f[i-b[j]} (a[j]<=i且b[j]<=i)。因為本題策略數和數組b數字都不超過64,所以僅考慮f[i-1]..f[i-64],可將其狀態壓縮至一個64位數中。其中status用於記錄對於i個石子,i-1..i-64是否有先手必勝策略。

 

(1)①處應填( )

A.0                  B .~0ull           C.~0ull^1        D.1

C。根據題目要求,狀態壓縮到64位,A和D默認是32位整數,所以B或者C。最開始石子是0個,應該是輸的狀態,所以最低位不能是1,選C。

 

(2)處應填( )

A.a[j]< i        B.a[j]==i         C.a[j] !=i          D.a[j] >i

B。題目實現有將規則按a[i]進行從小到大排序,所以可使用規則只增加不減少。此循環用於增加當前可選的規則,當i達到a[j]時規則可以使用。

 

(3)③處應填( )

A. trans |= 1ull <<(b[j] - 1)           B. status |= 1ull << (b[j]- 1)           C. status += 1ull << (b[j]-1)           D. trans += 1ull<< (b[j]-1)

A。此行是用來在原有規則上,新增”取b[j]個棋子”的規則。二進制新增用|。

 

(4)④處應填( )

A. ~status|trans         B. status&trans          C. status|trans        D. ~status&trans

D。

 

(5)⑤處應填( )

A. trans = status | trans ^win         B. status = trans >> 1^win        C. trans = status ^trans |win          D. status =status <<1^win

D。

 
作者:泉五菜老師
https://www.bilibili.com/read/cv3833576/
出處: bilibili


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM