Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Sprout 2013 Solution

683 views

Published on

The first problem solving report written by me!
Just a small test slide.

Published in: Science
  • Be the first to comment

Sprout 2013 Solution

  1. 1. By Andy, 129623. Using C++
  2. 2. 題目選單 PROBLEMS By Andy, 129623. Problem # Title Hi-score 1 a plus b 2/2 2 寂寞的手裏劍 10/10 3 可憐的動物管理員 0/10 4 覺醒吧!完美蚖! 10/10 5 天啊!是南啼! 10/10 6 括弧字串 10/10 7 撈哥慨慨 10/10 8 文章查詢 8/10 9 Cat~MeowRio~ 10/10 Total 70/82
  3. 3. 輸 入 輸 出 By Andy, 129623. 兩個正整數a,b,中間用一空白隔開, 皆不大於10^9。 一行,a+b的值。
  4. 4. CONCEPT  兩個數都不大於10^9,相加也不會大於2 × 109 ,可以用int 做就好。  int(其實是long int)可存到231 − 1 = 2147483647  除此之外好像沒有任何技巧(? By Andy, 129623.
  5. 5. CODE FOR #1 By Andy, 129623. #include<iostream> using namespace std; int main(){ int a,b; cin>>a>>b; cout<<a+b<<endl; return 0; }
  6. 6. 輸 入 輸 出 By Andy, 129623. 一個非負整數n(n < 1000) 輸出這n個手裏劍在完美情況下可以 造成的最大傷害值 (=個數n +安心對個數 m)
  7. 7. CONCEPT  列舉觀察  n=1, m=0, ans=1  n=2, m=1, ans=3  n=3, m=3, ans=6  n=4, m=6, ans=10  難道答案是 𝑛 𝑛+1 2 ? By Andy, 129623.
  8. 8. 遞迴(RECURSION)  觀察到最優解的產生方式總是如此: By Andy, 129623. 可以證明:照著這種擺法必定有最優解。 只要考慮n<=2的特殊情形就可以了!
  9. 9. CODE FOR #2 By Andy, 129623. #include<iostream> using namespace std; int main(){ int n; cin>>n; if(n<=2)cout<<n+(n*(n-1)/2)<<endl; else cout<<4*n-6<<endl; return 0; }
  10. 10. 輸 入 輸 出 By Andy, 129623. 8行,每行為8個大寫英文字母 一行輸出一組輸出合法的交換,必 須排序。
  11. 11. CONCEPT  想:有哪些情形可以交換?  要3個方塊相同,若且唯若原本已經 有兩個方塊相同。  像右邊這樣考慮…  垂直情形相同!  所以,對每一格,掃右和下1~2格  再來,可以寫一個函數專門來確認橘色格子是否可以移動到 藍色格子對上灰色格子,並將可以的解答記錄下來。 By Andy, 129623.
  12. 12. 解答排序(SOLUTION SORTING)  大魔王來了,解答出來怎麼排序? 1. 解答對(x1,y1)、(x2,y2)排序  注意到格子只能上下交換、左右交換  所以一定有一個座標值(x或y)是相同的!  因此,可以做一個「通吃的解法」寫成 𝑎𝑛𝑠 = min(𝑥1, 𝑥2) , min y1,y2 , min(𝑥1, 𝑥2) ,max(y1,y2) 2. 解答之間排序  我知道排序可以用sort(),可是有四個維度要怎麼排?  每個維度都是1~8,可以把它們壓成四位數再排!  輸出的時候再解開就好。  排序之後,判斷重複就方便了! By Andy, 129623.
  13. 13. CODE FOR #3 By Andy, 129623. #include<iostream> #include<algorithm> using namespace std; char t[11][11]; //解答 int ans[1000],solcnt=0; //確認並儲存(tx,ty)->(nx,ny)解答對 void check(int x,int y,int dir,int ndir){ int tx=x,ty=y; switch(dir){ case 1:tx--;ty--;break; case 2:tx--;ty++;break; case 3:tx++;ty--;break; case 4:tx++;ty++;break; case 5:tx-=2;break; case 6:ty+=2;break; case 7:tx+=2;break; case 8:ty-=2;break; } //如果不合法就退出 if(tx>8||ty>8||tx<=0||ty<=0)return; int nx=tx,ny=ty; switch(ndir){ case 1:nx--;break; case 2:nx++;break; case 3:ny--;break; case 4:ny++;break; } if(t[x][y]==t[tx][ty]){ //為解答編碼成4位數字,左上角先存 ans[solcnt++]=100*(10*min(tx,nx)+min( ty,ny))+10*max(tx,nx)+max(ty,ny); } } 注:dir: 1左上 2右上 3左下 4右下 5上x2 6右x2 7下x2 8左x2 ndir: 1上2下3左4右
  14. 14. CODE FOR #3 CONT. By Andy, 129623. void sort(){ sort(ans,ans+solcnt); //避免輸重複解答 int last=ans[0]; for(int i=1;i<solcnt;i++) if(ans[i]==last){ ans[i]=-1; }else{ last=ans[i]; } } void print(){ for(int i=0;i<solcnt;i++){ if(ans[i]==-1)continue; int from[2]={(ans[i]/100)/10,(ans[i]/100)%10}; int to[2]={(ans[i]%100)/10,(ans[i]%100)%10}; cout<<"("<<from[0]<<","<<from[1]<<"),"; cout<<"("<<to[0]<<","<<to[1]<<")"<<endl; } }
  15. 15. CODE FOR #3 CONT. By Andy, 129623. int main(){ //先鋪1層外圍的#,表示沒有東西 for(int i=0;i<=10;i++)for(int j=0;j<=10;j++)t[i][j]='#'; //輸入 for(int i=1;i<=8;i++)for(int j=1;j<=8;j++)cin>>t[i][j]; //依序掃每一格上下左右是否有相鄰 for(int i=1;i<=8;i++){ for(int j=1;j<=8;j++){ //只要往下往右掃就可 if(t[i][j]==t[i+1][j]){ check(i,j,1,4); check(i,j,2,3); check(i+1,j,3,4); check(i+1,j,4,3); check(i,j,5,2); check(i+1,j,7,1); } if(t[i][j]==t[i+2][j]){ check(i,j,3,4); check(i,j,4,3); }
  16. 16. CODE FOR #3 CONT. By Andy, 129623. if(t[i][j]==t[i][j+1]){ check(i,j,1,2); check(i,j+1,2,2); check(i,j,3,1); check(i,j+1,4,1); check(i,j,8,4); check(i,j+1,6,3); } if(t[i][j]==t[i][j+2]){ check(i,j,2,2); check(i,j,4,1); } } } sort(); print(); return 0; }
  17. 17. 輸 入 輸 出 By Andy, 129623. 兩行長度相同且不超過1000個字元、 由ATCG組成的字串s1、s2。 旋轉置換一次代價為6,更改其中一 個碼代價為4,輸出最少成本使s1 成為s2。
  18. 18. CONCEPT  乍看之下好像很複雜  一邊轉一邊改,還不如先轉轉看再改!  因為只能整段旋轉,其實可以直接掃:  ATCGATCG  TCGATCGA  CGATCGAT  GATCGATC …  每次轉一位,看看有幾個基因需要改,統計最小值即可!  設字串長度為n,左旋轉a次可以替換成右旋轉n-a次!  轉a位可以想成迴圈變數i加上a,字串取超過的時候從頭繼續取就好  可以用取餘數(%)完成這個操作! By Andy, 129623.
  19. 19. CODE FOR #4 (CORE FUNCTIONS) By Andy, 129623. string s1,s2; char getS1(int index){ //超過就從頭開始取 return s1[index%s1.size()]; } char getS2(int index){ //超過就從頭開始取 return s2[index%s2.size()]; }
  20. 20. CODE FOR #4 CONT. By Andy, 129623. int main(){ cin>>s1>>s2; int ans=-1,len=s1.size(); for(int begin=0;begin<len;begin++){ //定義兩種操作 int opa=min(begin,len-begin),opb=0; //比較差異 for(int i=0;i<len;i++)opb+=getS1(begin+i)!=getS2(i); //更新最優解 if(ans==-1)ans=6*opa+4*opb; ans=min(ans,6*opa+4*opb); } cout<<ans; return 0; }
  21. 21. 輸 入 輸 出 By Andy, 129623. 兩行字串s1、s2。 分別統計s1、s2出現X、D、r、z出 現的總次數,若s1<=s2輸出”Yes!!!”, 否則輸出”NO QAQ”。
  22. 22. CONCEPT  可以寫一個函數專門做這種比對(其實也可以不用 By Andy, 129623.
  23. 23. CODE FOR #5 By Andy, 129623. #include<iostream> using namespace std; bool match(char c){ return c=='X'||c=='D'||c=='r'||c=='z'; } int main(){ string a,b; int x=0,y=0; getline(cin,a); getline(cin,b); for(int i=0;i<a.size();i++)x+=match(a[i]); for(int i=0;i<b.size();i++)y+=match(b[i]); if(x<=y)cout<<"Yes!!!";else cout<<"NO QAQ"; return 0; }
  24. 24. 輸 入 輸 出 By Andy, 129623. 輸入的每一行包含一個括弧字串。 同一個字串內不會有多餘空白。每 一個字串長度不超過1000000。 輸出括弧排列是否合法。
  25. 25. CONCEPT  想想看自己平常是怎麼看括號合不合法?  合法:(())((()))  合法:(((()))())))  不合法:())(())()  Why? 因為它有個孤獨的右括號!  你怎麼知道它是孤獨的?  因為它前面沒有足夠的左括號!(這是關鍵)  所以,從頭開始數左括號和右括號,一旦右括號比左括號 多就不用繼續比了!  或者可以簡化:令一個變數初始為0,遇到左括號+1,遇到右括號-1, 但如果不夠減就可以停了!  最後當然要判斷左括號和右括號一不一樣多。 By Andy, 129623.
  26. 26. 陷阱!  再來是這題最大的陷阱:空字串合法!可是如果只用cin讀 入空字串,根本不會處理。 By Andy, 129623.
  27. 27. CODE FOR #6 By Andy, 129623. #include<iostream> using namespace std; int main(){ string s; while(getline(cin,s)){ int cnt=0,flag=1; for(int i=0;i<s.size()&&flag;i++){ if(s[i]=='(')cnt++; else { if(cnt<=0)flag=0; else cnt--; } } if(cnt)flag=0; cout<<(flag?"Yes":"No")<<endl; } }
  28. 28. 輸 入 輸 出 By Andy, 129623. 輸入的第一行包含兩個整數n, k (1 ≤ n ≤ 105 , 0 ≤ k ≤ 109),代表有n位 女生,撈網的直徑為k。 第二行包含n個整數xi (0 ≤ xi ≤ 109 ), 代表這n個女生所在的座標位置。 一個整數,為在直徑k範圍內的女生 數量
  29. 29. HINT  這題觀念不難,難在要優化,否則會… By Andy, 129623.
  30. 30. CONCEPT  既然只跟直徑有關,就不需要管圓心了  邊界一定要是整數,在整數點之間沒有比較好  顯然可以枚舉起點(自xi最小值到最大值)再統計區間中的女 生個數,複雜度O(n*(max(xi)-min(xi)))  在xi範圍很大時,太慢了!  還可以怎麼做?  有一個邊界一定要在女生上,否則像套橡皮筋一樣,真正能抓到女 生的範圍就變小了!  所以枚舉的起點就只有k個,複雜度降為O(kn)  統計優化:可以發現其實先把陣列排好序可以節省很多時間!  做二分搜,可以讓複雜度降為O(klogn),不過循序已經夠好了。 By Andy, 129623.
  31. 31. CODE FOR #7 By Andy, 129623. #include<iostream> #include<algorithm> using namespace std; int main(){ int n,k; cin>>n>>k; int a[n]; for(int i=0;i<n;i++)cin>>a[i]; //排序 sort(a,a+n); int ans=0; //窮舉左邊界 for(int i=0;i<n;i++){ int l=a[i],r=l+k,cnt=0; for(int j=i;j<n;j++){ if(a[j]>=l) if(a[j]<=r)cnt++; else break; } ans=cnt>ans?cnt:ans; if(r>a[n-1])break; } cout<<ans; return 0; }
  32. 32. 輸 入 輸 出 By Andy, 129623. 兩行,第一行為一篇文章,以句點 為分隔,除末句外句點後面保證有 一空白。第二行為一關鍵字。 所有包含此關鍵字,且前後無多餘 字母的句子,不含行末空白。
  33. 33. CONCEPT  先列出大致的流程  抓出所有句子開頭的索引值  掃這個字出現在哪  找出是在哪句出現的  如果沒輸出過,輸出那個句子(到句點為止)  接著只要一一執行就可。  小細節,比較關鍵字時要忽略大小寫  可以把它們全轉成大寫再比! By Andy, 129623.
  34. 34. CODE FOR #8 (CORE FUNCTIONS) By Andy, 129623. #include<iostream> using namespace std; //判斷是否相等,大小寫視為相同 bool isSame(char a,char b){ //先都轉換成大寫 if(b>='a'&&b<='z') b=b-'a'+'A'; if(a>='a'&&a<='z') a=a-'a'+'A'; return a==b; } bool isAlpha(char x){ return (x>='a'&&x<='z')||(x>='A'&&x<='Z'); }
  35. 35. CODE FOR #8 CONT. By Andy, 129623. int main(){ int has=0; string s,tar; int strBegin[1000],printed[1000],sbcnt=1; getline(cin,s); cin>>tar; strBegin[0]=printed[0]=0; //記錄所有開始 (類似split作用) for(int i=0;i<s.size();i++) if(s[i]==‘.’){ //.->空白->下一句開頭 strBegin[sbcnt]=i+2; printed[sbcnt]=0; sbcnt++; } //末句不算,避免越界 strBegin[sbcnt--]=0; for(int i=0;i<s.size();i++){ int offset=0,flag=0; //如果首字元(前字元為-1),前面必定 不是字母,傳回* char prevChar=(i!=0)?s[i- 1]:'*'; char nextChar=(i+tar.size() <s.size())?s[i+tar.size()]:'*'; if(!isAlpha(prevChar)&&!isAlpha(n extChar)) while(isSame(tar[offset],s[i+offs et])){ if(offset==tar.size()-1)flag=1; offset++; } else flag=0;
  36. 36. CODE FOR #8 CONT. By Andy, 129623. if(flag){ has=1; int jj; for(jj=0;jj<sbcnt;jj++){ if(i<strBegin[jj])break; } if(!printed[jj]){ for(int j=strBegin[jj-1];s[j-1]!='.';j++){ cout<<s[j]; } cout<<endl; printed[jj]=1; } } } if(!has)cout<<"None."; return 0; }
  37. 37. 輸 入 輸 出 By Andy, 129623. 第一行為兩個正整數n (n < 21) 與 Wmax,分別代表金幣個數以及總重 量的上限。 第二行為n個非負整數,代表這n個 金幣的重量。 最大總金幣重量。
  38. 38. CONCEPT  規模不是很大(𝑛 ≤ 20),用O(2n)再剪枝就很快了。  可以用DFS(Depth-First Search,深度優先走訪)來做,若 n的硬幣依序考慮,第k個硬幣重量Wk,且 𝑓(𝑘, 𝑊)為前k個 硬幣所能得到的最佳解,遞迴關係可以列如下: 𝑓 𝑘, 𝑊 = 0,當𝑘 = 0 0,當𝑊 < 0 max 𝑓 𝑘 − 1, 𝑊 − 𝑊𝑘 + 𝑊𝑘, 𝑓 𝑘 − 1, 𝑊 , 𝑊 ,其他情形  沒那麼複雜,其實max裡就是選與不選這兩種而已!  而其他的情形就是邊界條件(boundary condition),你不會 希望無窮遞迴的! By Andy, 129623.
  39. 39. THINK FURTHER  其實可以DP(Dynamic Programming,動態規劃),只不過 這題不需要而已。  請參考:0/1 Knapsack Problem@演算法筆記 By Andy, 129623.
  40. 40. CODE FOR #9 By Andy, 129623. #include<iostream> using namespace std; int n,wmax,ans=0; int coin[30]; void dfs(int dep,int sum){ if(sum>wmax)return; if(dep==n){ ans=sum>ans?sum:ans; return; } dfs(dep+1,sum+coin[dep]); dfs(dep+1,sum); } int main(){ cin>>n>>wmax; for(int i=0;i<n;i++)cin>>coin[i]; dfs(0,0); cout<<ans; }
  41. 41. By Andy, 129623. Any comments or better solutions are welcome!

×