原網址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行的第一個數mi(0<=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