清北學堂Day1
阿新 • • 發佈:2017-10-01
一點 雙向bfs 要求 col iostream .net 排除 b- 研究
Part 1:模擬考試總結
這次第一題拿了60,第二題拿了49(不知道怎麽拿的)。
第一題:
我的想法(60分,原本是可以得70的,結果數組開小了)是,首先在輸入的時候初始化,a[i][x]指前i個裏有a[i][x]個x這個字母(類似於前綴和)。分別枚舉區間的左右端點,之後在區間內枚舉26個字母的最大值-最小值的最大值。時間復雜度是O(26*n^2)。
代碼:
1 #include <iostream> 2 #include <cmath> 3 #include <cstring> 4 #include <cstdio> 5 #include <cstdlib> 6a#include <algorithm> 7 using namespace std; 8 char s[101010]; 9 int a[101010][30]; 10 int main() 11 { 12 13 freopen("a.in","r",stdin); 14 freopen("a.out","w",stdout); 15 16 int n; 17 scanf("%d",&n); 18 for(int i=1;i<=n;i++) 19 { 20 cin>>s[i]; 21a[i][s[i]-‘a‘+1]=a[i-1][s[i]-‘a‘+1]+1; 22 for(int j=1;j<=26;j++) 23 if(j==s[i]-‘a‘+1) continue; 24 else 25 { 26 if(i==n && a[i-1][j]==0/**/) a[i][j]=-1; 27 else a[i][j]=a[i-1][j]; 28 } 29 } 30int maxn=0,minn=9999999; 31 int ans=0; 32 for(int i=1;i<=n;i++) 33 { 34 for(int j=0;j<i;j++) 35 { 36 maxn=0;minn=9999999;/**/ 37 for(int k=1;k<=26;k++) 38 { 39 if(a[n][k]==-1) continue; 40 maxn=max(maxn,a[i][k]-a[j][k]); 41 if(a[i][k]-a[j][k]==0) continue;/**/ 42 minn=min(minn,a[i][k]-a[j][k]); 43 ans=max(ans,maxn-minn); 44 } 45 } 46 } 47 printf("%d",ans); 48 return 0; 49 }
其中有/**/標誌的,是最開始沒想全面的地方。
100分做法:具體見註釋。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<vector> 5 6 using namespace std; 7 8 const int maxn=1000010; 9 10 int n,ans,p[26][26],minv[26][26],sum[26],last[26]; 11 12 char s[maxn]; 13 14 int main() 15 { 16 freopen("a.in","r",stdin); 17 freopen("a.out","w",stdout); 18 19 scanf("%d",&n); 20 scanf("%s",s+1); 21 for (int a=1;a<=n;a++) 22 { 23 int c=s[a]-‘a‘; 24 sum[c]++; //字母s[a]的前綴和 25 last[c]=a; //字母a的最後位置 26 //此時我們已經找到了最大的右端點以及相對應的字母(設為x),接下來需要做的就是找到一個左端點及其對應字母(設為y),使得這個區間的最大減最小最大。 27 //因此我們希望左端點前面的字母x盡量少,字母y盡量多。也就是兩者的數量差盡量小。(minv中存的東西) 28 for (int b=0;b<26;b++) 29 if (b!=a && sum[b]) ans=max(ans,max(sum[c]-sum[b]-minv[c][b]-(last[b]==p[c][b]),sum[b]-sum[c]-minv[b][c]-(last[b]==p[b][c]))); 30 for (int b=0;b<26;b++) 31 { 32 if (sum[c]-sum[b]<minv[c][b]) minv[c][b]=sum[c]-sum[b],p[c][b]=a; 33 if (sum[b]-sum[c]<minv[b][c]) minv[b][c]=sum[b]-sum[c],p[b][c]=a; 34 } 35 } 36 printf("%d\n",ans); 37 38 return 0; 39 }a100
第二題是計算幾何,鑒於這種題不是很常考,就不深研究了。簡單來說,這道題的本質就是判斷兩條線段是否相交(可以用叉積)。把題面和我自己的代碼放出來。
1 #include <iostream> 2 #include <cmath> 3 #include <cstring> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <algorithm> 7 using namespace std; 8 int Hx,Hy,Yx,Yy; 9 int stx,edx,sty,edy; 10 int w[2][2],m[2][2]; 11 double f(int x) 12 { 13 return ((edy-sty)/(edx-stx))*x+((edx*sty)-(sty*edy))/(edx-stx);//y=kx+b。聽說用ax+by+c更好,但還沒學過,不會用。 14 } 15 double g(int x) 16 { 17 return ((w[2][2]-w[1][2])/(w[2][1]-w[1][1]))*x+((w[2][1]*w[1][2])-(w[1][2]*w[2][2]))/(w[2][1]-w[1][1]); //同上 18 } 19 int main() 20 { 21 22 freopen("b.in","r",stdin); 23 freopen("b.out","w",stdout); 24 25 scanf("%d%d%d%d",&Hx,&Hy,&Yx,&Yy); 26 for(int i=1;i<=2;i++) scanf("%d%d",&w[i][1],&w[i][2]); 27 for(int i=1;i<=2;i++) scanf("%d%d",&m[i][1],&m[i][2]); 28 if(Hx<=Yx) {stx=Hx;edx=Yx;sty=Hy;edy=Yy;} 29 else {stx=Yx;edx=Hx;sty=Yy;edy=Hy;} 30 bool ok=true; 31 if(w[1][1]==w[2][1]) //特判分母為零的情況 32 { 33 if(sty>=w[1][2] && edy<=w[2][2]) ok=false; 34 else if(sty<=w[1][2] && edy>=w[2][2]) ok=false; 35 } 36 else //判斷兩者連線是否穿過墻 37 { 38 for(int x=stx;x<=edx;x++) 39 { 40 if(x<w[1][1] || x>w[2][1]) continue; 41 if(f(x)==g(x)) {ok=false;break;} 42 } 43 } 44 if(ok) printf("YES"); 45 else printf("NO"); 46 return 0; 47 }b
Part 2:今日專題——搜索
- 三類基本搜索:最優解問題,可行解問題,解數量問題
- 可以用STL中的set來判重。(依靠集合的互異性)
1 #include <iostream> 2 #include <set> 3 using namespace std; 4 struct rec{ 5 int map[4][4]; 6 }; 7 //rec&x中的&一定要寫,否則會先拷貝這兩個數再算,非常慢。加上之後不用拷貝,快一些。 8 //加const是為了保證在做<運算前後x、y的值不變(c++的要求,必須加) 9 //set必須要寫operator <,因為它的本質是紅黑樹 10 bool operator <(const rec&x,const rec&y){ 11 for(int a=0;a<4;a++) 12 for(int b=0;b<4;b++) 13 if(x.map[a][b]!=y.map[a][b]) return x.map[a][b]<y.map[a][b]; 14 return false; 15 } 16 ser<rec> se;set基本用法
- 通用剪枝:
- 最優解問題:超過當前解退出
- 解數量問題:重復性利用(記憶化搜索,即之間查到過的狀態拿來直接用)
- 排除不可能分支(比如上面例題2,只需考慮180以內的情況)
- 優先走更好的分支
- 隨機化(降低因搜索順序TLE的可能)
隨機化多說幾句。有個dalao第二題用隨機也過了好多點:
當然隨機化不是指這個。平時我們搜索都習慣按照一些規律,比如從左到右,從上到下等等。出題人可能會卡這種順序,導致TLE。所以我們隨機順序,就不會被卡了:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<time.h> 4 int main() 5 { 6 int i; 7 srand((int)time(NULL)); //每次執行種子不同,生成不同的隨機數 8 for(i=0; i<10; i++) 9 { 10 printf("%d\n", rand()); //因為執行太快,不到一秒鐘,10個隨機數是相通的,但是每次執行是不同的 11 } 12 return 0; 13 }
- 卡時(這是我個人覺得今天收獲最大的一點) (P.s:只用於最優解問題)
我們知道,如果一個程序的時間限制是1s,那麽當它運行到1s的時候就已經TLE了。卡時的意思就是在它時間超限之前先輸出當前答案,這樣有一定的幾率是對的(因為可能此時最優解已經找出)。
noip2012年的mayan遊戲就是這樣,從左往右同時從上往下只能得75分;從左往右從下往上可以得95分,從右往左則可以得100分。
1 #include<ctime> 2 3 void dfs(){ 4 //if(1000*(clock()-t)>=900*CLOCKS_PER_SEC) 5 if(clock()-t>=999) //假設1秒分為1000個時間單位,如果>=999則代表馬上就要超時了 (當然最好不要是999,1ms退出根本不可能,寫900左右比較好) 6 { 7 output solution; //輸出解 8 exit(0); //退出整個程序 9 } 10 } 11 12 int main(){ 13 t=clock(); //定義初始時間 14 }
- 雙向BFS
- 叠代加深搜索:1.枚舉深度;2.二分深度。
清北學堂Day1