# APCS 2016年10月題解 ## 第一題 三角形辨別 ### 題目敘述 https://zerojudge.tw/ShowProblem?problemid=c294 ### 解套 N/A ### 題目分析 本題分成兩個部分:決定大小順序和判斷三角形種類 因為只有三個數字,所以在大小方面最小的就對三邊取最小值、最大就對三邊取最大值、而中間的我是用三邊邊長總合減最大與最小來計算 判斷三型種類則是用簡單的if依序判斷就好 時間複雜度$O(1)$ ### 解題流程 輸入$→$排序$→$判斷、輸出 ### 解題過程 #### 變數宣告 ```cpp=3 int a,b,c,d,e,f; ``` d,e,f吃輸入;a,b,c存排序後的邊長 #### 輸入 ```cpp=5 cin>>d>>e>>f; ``` #### 排序 ```cpp=6 a=min({d,e,f}); c=max({d,e,f}); b=d+e+f-a-c; ``` 方法同前述 #### 輸出、判斷 ```cpp=9 cout<<a<<" "<<b<<" "<<c<<"\n"; if(a+b<=c)cout<<"No"; else{ a*=a;b*=b;c*=c; if(a+b<c)cout<<"Obtuse"; else if(a+b==c)cout<<"Right"; else cout<<"Acute"; } ``` ### 完整程式碼 ```cpp=1 #include<bits/stdc++.h> using namespace std; int a,b,c,d,e,f; int main(){ cin>>d>>e>>f; a=min({d,e,f}); c=max({d,e,f}); b=d+e+f-a-c; cout<<a<<" "<<b<<" "<<c<<"\n"; if(a+b<=c)cout<<"No"; else{ a*=a;b*=b;c*=c; if(a+b<c)cout<<"Obtuse"; else if(a+b==c)cout<<"Right"; else cout<<"Acute"; } } ``` ## 第二題 最大和 ### 題目敘述 https://zerojudge.tw/ShowProblem?problemid=c295 ### 解套 N/A ### 題目分析 直接操作即可。 最直觀的作法會是用二維陣列存所有數字,不過我用稍微難一點的方法,就是對每組數字邊吃輸入更新最大值,並把最大值加到總和並存起來,最後一個一個判斷是否整除總和即可 另外因為要求結尾不能有空格,所以答案先全部存起來再輸出 時間複雜度$O(mn)$ ### 解題流程 輸入、決定最大值$→$輸出 ### 解題過程 #### 變數宣告 ```cpp=3 int n,m,sum=0,inp; vector<int>picked,last; ``` n,m同題述,sum存總和,inp是輸入用,picked存被選到的數字,last存能整除的 #### 輸入、更新 ```cpp=6 cin>>n>>m; picked.assign(n,0); for(int i=0;i<n;i++){ int mx=INT_MIN; for(int j=0;j<m;j++){ cin>>inp; mx=max(mx,inp); } picked[i]=mx; sum+=mx; } ``` 依照n決定picked的大小(第7行) 接著對每組數字,先設一個最大值,並邊輸入邊更新最大值,最後把它加進總和並放入picked #### 輸出 ``` cpp=17 cout<<sum<<"\n"; for(int i:picked)if(sum%i==0)last.push_back(i); if(last.empty())cout<<-1; else{ cout<<last[0]; for(int i=1;i<last.size();i++)cout<<" "<<last[i]; } ``` 先輸出總和(第17行) 接著把能整除總和的放進last(第18行) 然後按題述輸出即可(注意沒有符合的要輸出-1,且結尾不能有空白) ### 完整程式碼 ```cpp=1 #include<bits/stdc++.h> using namespace std; int n,m,sum=0,inp; vector<int>picked,last; int main(){ cin>>n>>m; picked.assign(n,0); for(int i=0;i<n;i++){ int mx=INT_MIN; for(int j=0;j<m;j++){ cin>>inp; mx=max(mx,inp); } picked[i]=mx; sum+=mx; } cout<<sum<<"\n"; for(int i:picked)if(sum%i==0)last.push_back(i); if(last.empty())cout<<-1; else{ cout<<last[0]; for(int i=1;i<last.size();i++)cout<<" "<<last[i]; } } ``` ## 第三題 定時K彈 ### 題目敘述 https://zerojudge.tw/ShowProblem?problemid=c296 ### 解套 約瑟夫問題 ### 題目分析 基本上跟經典的[約瑟夫問題](https://oi-wiki.org/misc/josephus/)非常像,因此只需要對約瑟夫問題的$O(n)$解法做一些些微的調整就好 如果最後只剩一個人解法會長這樣: ```cpp int josephus(int n,int m) { int res=0; for(int i=1;i<=n;i++)res=(res+m)%i; return res; } ``` 其中n代表有幾個人,m代表一次屬幾個人 但這題只會去除k個人,而且第一個人編號是1,所我們稍微做一下改寫: ```cpp int josephus(int n,int m) { int res=0; for(int i=n-k+1;i<=n;i++)res=(res+m)%i; return res+1; } ``` 把i=1~n改成i=n-k+1讓我們只去除k個人,要注意的是不能改成i=1~k (因為對i取模是在處理剩下人數) 最後加一是為了變回1-base 時間複雜度$O(k)$ ### 解題流程 輸入$→$套公式$→$輸出 ### 解題過程 #### 變數宣告 ```cpp=3 int n,m,k; ``` 變數名同題述 #### 輸入 ```cpp=5 cin>>n>>m>>k; ``` #### 找答案、輸出 ```cpp=6 int tmp=0; for(int i=n-k+1;i<=n;i++)tmp=(tmp+m)%i; cout<<tmp+1<<"\n"; ``` 跟前面一樣,只是不是用函式的形式呈現 ### 完整程式碼 ```cpp=1 #include<bits/stdc++.h> using namespace std; int n,m,k; int main(){ cin>>n>>m>>k; int tmp=0; for(int i=n-k+1;i<=n;i++)tmp=(tmp+m)%i; cout<<tmp+1<<"\n"; } ``` ## 第四題 棒球遊戲 ### 題目敘述 https://zerojudge.tw/ShowProblem?problemid=c297 ### 解套 N/A ### 題目分析 這題到底是想考甚麼...? 早期的APCS都這樣嗎? 唯一比較要注意的是打擊順序是由上而下,由左而右的,所以不能直接吃輸入 我的作法是用9個vector存每個打者的狀況,再合併起來處理 其餘就直接照規則做就好 時間複雜度$O(1)$(最多只有45個數字) ### 解題流程 輸入$→$調整順序$→$按規則計算分數$→$輸出 ### 解題過程 #### 變數宣告 ```cpp=3 int a,b,cnt=0,pts=0,outs=0; bool one=0,two=0,three=0; vector<string>v[9],arr; ``` a,b輸入用,cnt紀錄有幾次打擊,pts紀錄分數,outs紀錄出局數,one,two,three紀錄一二三壘有沒有人,v存初始輸入,arr按順序存打擊狀態 #### 輸入 ```cpp=7 for(int i=0;i<9;i++){ cin>>a; cnt+=a; v[i].assign(a,""); for(int j=0;j<a;j++)cin>>v[i][j]; } cin>>b; ``` #### 轉成按順序存 ```cpp=14 for(int i=0;i<5&&cnt;i++){ for(int j=0;j<9&&cnt;j++){ arr.push_back(v[j][i]); cnt--; } } ``` cnt確保不會多存導致記憶區間錯誤 #### 計算分數 ```cpp=20 for(int i=0;i<arr.size()&&b>0;i++){ if(arr[i]=="1B"){ if(three){ pts++; three=0; } if(two){ three=1; two=0; } if(one){ two=1; one=0; } one=1; } else if(arr[i]=="2B"){ if(three){ pts++; three=0; } if(two){ pts++; two=0; } if(one){ three=1; one=0; } two=1; } else if(arr[i]=="3B"){ if(three){ pts++; three=0; } if(two){ pts++; two=0; } if(one){ pts++; one=0; } three=1; } else if(arr[i]=="HR"){ if(three){ pts++; three=0; } if(two){ pts++; two=0; } if(one){ pts++; one=0; } pts++; } else{ outs++; b--; if(outs%3==0)one=two=three=0; } } ``` 比較直接的做法,只是按照規則寫而已 雖然題目的三種出局分別給不同的代號,但統一處理即可,所以統一放在else裡面 需要特別注意的是如果出局數達到三的倍數就要清空壘包(第84行) #### 輸出 ```cpp=87 cout<<pts; ``` ### 完整程式碼 ```cpp=1 #include<bits/stdc++.h> using namespace std; int a,b,cnt=0,pts=0,outs=0; bool one=0,two=0,three=0; vector<string>v[9],arr; int main(){ for(int i=0;i<9;i++){ cin>>a; cnt+=a; v[i].assign(a,""); for(int j=0;j<a;j++)cin>>v[i][j]; } cin>>b; for(int i=0;i<5&&cnt;i++){ for(int j=0;j<9&&cnt;j++){ arr.push_back(v[j][i]); cnt--; } } for(int i=0;i<arr.size()&&b>0;i++){ if(arr[i]=="1B"){ if(three){ pts++; three=0; } if(two){ three=1; two=0; } if(one){ two=1; one=0; } one=1; } else if(arr[i]=="2B"){ if(three){ pts++; three=0; } if(two){ pts++; two=0; } if(one){ three=1; one=0; } two=1; } else if(arr[i]=="3B"){ if(three){ pts++; three=0; } if(two){ pts++; two=0; } if(one){ pts++; one=0; } three=1; } else if(arr[i]=="HR"){ if(three){ pts++; three=0; } if(two){ pts++; two=0; } if(one){ pts++; one=0; } pts++; } else{ outs++; b--; if(outs%3==0)one=two=three=0; } } cout<<pts; } ```