# 交大GPE程式檢定2025.09.24題目+題解 ## 第一次考GPE的心得 原本想說多刷點Uva上的題目再報名,但是看到許晉瑞第一次考就破台(甚至只花了兩個小時,好電ww),我覺得好像可以先去考看看?於是就直接報名了這一場。考完之後才發現GPE好像都是搬Uva一模一樣的題目拿來考,也因為這樣,碰到有些比較老的Uva題目,它輸入輸出方式跟一般競程題目不太一樣,會給一些莫名其妙的字元來干擾我們正常cin>>東西進來,像是這次pE,pF的輸入輸出就特別繁瑣,我花了一堆時間處理這個問題,可能要特別注意一下這點。然後GPE的計分方式是每題100分,看你通過了幾個測資會有部分分數,滿分600分。我這次考試AC了pA,pB,pD,而pE,pF只拿到60,40分,最後總成績是400分,好像沒達到A+的門檻,所以應該還要再考下一次。 ## 題目 ### pA Sum of Consecutive Prime Numbers Uva原題連結: https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3651 #### 題意: 給定一個整數$n$,問有幾組連續的質數的和加起來剛好等於$n$? #### 範圍: $2 \leq n \leq 10^{4}$ #### 想法: 先計算出所有小於$10000$的質數,發現只有$1229$個,代表我們可以用簡單的兩層迴圈枚舉所有可能的連續質數和,並用map存它出現的次數,之後每一次詢問$n$都可以直接查表輸出。 #### 扣德: ```cpp= #include<bits/stdc++.h> #define int long long using namespace std; signed main(){ vector<int>v; for(int i=2;i<=10000;i++){ bool isPrime=true; for(int j=2;j*j<=i;j++){ if(i%j==0){ isPrime=false; break; } } if(isPrime)v.push_back(i); } map<int,int>mp; int arr[1300]; int idx=0; for(auto x:v){ arr[idx]=x; idx++; } for(int i=0;i<idx;i++){ int sum=arr[i]; mp[sum]++; for(int j=i+1;j<idx;j++){ sum+=arr[j]; mp[sum]++; } } int n; while(cin>>n&&n){ cout<<mp[n]<<'\n'; } } ``` --- ### pB Children's Game Uva原題連結: https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1846 #### 題意: 給定$n$個整數,求這些整數怎樣排列才能夠形成最大的整數? #### 範圍: $n \leq 50$ #### 想法: 觀察發現對於兩個數字$a$,$b$,比較$ab$,$ba$的大小進行排序就對了,可以用內建的sort(),再自己寫cmp函式。考試的時候我是用long long來存數字,但不知道為什麼同樣的寫法傳到Uva不會過,底下的code是用string來實作。 備注:這邊說的$ab$不是$a*b$,若$a=45$,$b=123$,則$ab=45123$ #### 扣德: ```cpp= #include<bits/stdc++.h> #define int long long using namespace std; bool cmp(string a,string b){ return a+b>b+a; } signed main(){ int n; string arr[55]; while(cin>>n&&n){ for(int i=0;i<n;i++)cin>>arr[i]; sort(arr,arr+n,cmp); for(int i=0;i<n;i++)cout<<arr[i]; cout<<'\n'; } } ``` --- ### pC Square root Uva原題連結: https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=964 #### 題意: 給一個整數$y$,求出$\sqrt{y}$?(題目保證答案也是整數) #### 範圍: $1 \leq y \leq 10^{1000}$ #### 想法: 由於題目給的$y$範圍非常大,必須要實作大數乘法,所以考試的時候我直接放棄這一題(我考試的時候不會大數...)。 #### 扣德: ```cpp= #include <cstring> #include <cstdio> char buf[1111]; int tag[505], mul[505], tem[1111]; int lef[505], rig[505], mid[505]; void bs(int L) { for (int i = 0; i <= 500; ++ i) lef[i] = rig[i] = 0; int l = (L+1)>>1; rig[l] = 1; int ans = 0, small = 1, end = L; while (small) { for (int i = 0; i <= l; ++ i) mid[i] = lef[i]+rig[i]; mid[0] ++; for (int i = 0; i <= l; ++ i) { mid[i+1] += mid[i]/1000; mid[i] %= 1000; } for (int i = l; i >= 0; -- i) { if (i && mid[i]%2) mid[i-1] += 1000; mid[i] >>= 1; } end = l; while (end > 0 && !mid[end]) -- end; for (int i = 0; i <= L; ++ i) mul[i] = 0; for (int i = 0; i <= end; ++ i) for (int j = 0; j <= end; ++ j) mul[i+j] += mid[i]*mid[j]; for (int i = 0; i <= L; ++ i) { mul[i+1] += mul[i]/1000; mul[i] %= 1000; } ans = 0; for (int i = L; i >= 0; -- i) if (mul[i] != tag[i]) { ans = mul[i]-tag[i]; break; } if (ans > 0) { for (int i = 0; i <= l; ++ i) rig[i] = mid[i]; rig[0] --; for (int i = 0; rig[i] < 0; ++ i) { rig[i] += 1000; rig[i+1] --; } }else { for (int i = 0; i <= l; ++ i) lef[i] = mid[i]; } end = l; while (end >= 0 && lef[end] == rig[end]) -- end; small = (end >= 0 && lef[end] < rig[end]); } end = l; while (end > 0 && !lef[end]) -- end; printf("%d",lef[end --]); while (end >= 0) printf("%03d",lef[end --]); printf("\n"); } int main() { int n; scanf("%d",&n); while (n --) { memset(tag, 0, sizeof(tag)); memset(tem, 0, sizeof(tem)); scanf("%s",buf); int L = strlen(buf); for (int i = L-1; i >= 0; -- i) tem[i] = buf[L-1-i]-'0'; int save = 0; for (int i = 0; i < L; i += 3) tag[save ++] = tem[i]+tem[i+1]*10+tem[i+2]*100; bs(save); if (n) printf("\n"); } return 0; } ``` --- ### pD Longest Common Subsequence Uva原題連結: https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1346 #### 題意: 給定兩個字串$s$,$t$,求兩者的最長共同子序列(經典DP題目) #### 範圍: $s.size()$,$t.size()$<$1000$ #### 想法: 定義dp[i][j]為結尾在s[i],t[j]的最長共同子字串,則$dp[i][j]=max(max(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1]+ (s[i]==t[j])?1:0)$ #### 扣德: ```cpp= #include<bits/stdc++.h> #define MAX 1005 using namespace std; int main(){ string s,t; while(getline(cin,s)){ getline(cin,t); int dp[MAX][MAX]={0}; s=" "+s; t=" "+t; for(int i=1;i<s.size();i++){ for(int j=1;j<t.size();j++){ int k=dp[i-1][j-1]; if(s[i]==t[j])k++; dp[i][j]=max(max(dp[i-1][j],dp[i][j-1]),k); } } cout<<dp[s.size()-1][t.size()-1]<<'\n'; } } ``` --- ### pE Show the Sequence Uva原題連結:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=938 #### 題意: 每一輪都按照順序執行一次$[]$內的加法或乘法,輸出前$n$輪最後的計算結果。 #### 範圍: $2\leq n\leq 50$ #### 想法: 這題的實作的難點是要怎麼把類似$[2*[1+[2+[1]]]]$這種東西正確的讀取進來,我是用遞迴的方式來實作,遇到新的左括號就開始遞迴。讀進來之後就用動態規劃的方式,從最小的的括號開始計算它前$n$輪的值,更大的括號可以用小括號的值計算出來。 另外,這題我考試的時候一直WA,只拿到60分,結果同樣的程式碼上傳到Uva卻AC了,蠻神奇的。 #### 扣德: ```cpp= #include<bits/stdc++.h> #define MAX 50005 #define int long long using namespace std; int arr[MAX]; char brr[MAX]; int idx1=0,idx2=0; int cnt=0; void dfs(){ cnt++; cin>>arr[idx1]; idx1++; cin>>brr[idx2]; if(brr[idx2]==']')return; idx2++; char x;cin>>x; if(x=='[')dfs(); else return; } signed main(){ char c; while(cin>>c){ idx1=0;idx2=0;cnt=0; dfs(); for(int i=0;i<cnt-1;i++){char x;cin>>x;}; int k;cin>>k; vector<int>v_new(k+1,0),v_old(k+1,0); for(int i=0;i<=k;i++){ v_old[i]=arr[idx1-1]; } for(int i=idx1-2;i>=0;i--){ if(brr[i]=='+'){ v_new[1]=arr[i]; for(int i=2;i<=k;i++){ v_new[i]=v_new[i-1]+v_old[i-1]; } }else{ v_new[0]=arr[i]; for(int i=1;i<=k;i++){ v_new[i]=v_new[i-1]*v_old[i]; } } swap(v_new,v_old); } for(int i=1;i<=k;i++){ if(i==k)cout<<v_old[i]; else cout<<v_old[i]<<' '; } cout<<'\n'; } } ``` --- ### pF Set partition Uva原題連結:我找不到... #### 題意: 給長度為$n$的整數陣列$a$,求有幾種分組方式,可以將$a$的每個元素都分配到兩組,使得兩組的和相等。 #### 範圍: $0< n \leq 30$,$0<a_i \leq 10^{12}$ #### 想法: 由於這題的$n$只有到$30$,我可以用int來表示集合,比方說6=(110)二進制,代表第一個數與第二個數一組,第三個數自己一組。枚舉出所有的集合,計算哪些集合符合兩組的和相等就可以了。考試的時候一直有bug沒找出來,蠻可惜的。 --- ## 最後的小小抱怨 這學期前三場GPE分別是9/17,9/24,10/15,除了我這次考的這場以外,另外兩場分別跟資工系直屬日、系抓撞期,學校真的很會挑時間耶~。