Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

Sprout 2013 Solution

639 visualizaciones

Publicado el

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

Publicado en: Ciencias
  • Sé el primero en comentar

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!

×