清北学堂Day1
Posted 小蒟蒻
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了清北学堂Day1相关的知识,希望对你有一定的参考价值。
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> 6 #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]; 21 a[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 } 30 int 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 }
第二题是计算几何,鉴于这种题不是很常考,就不深研究了。简单来说,这道题的本质就是判断两条线段是否相交(可以用叉积)。把题面和我自己的代码放出来。
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 }
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;
- 通用剪枝:
- 最优解问题:超过当前解退出
- 解数量问题:重复性利用(记忆化搜索,即之间查到过的状态拿来直接用)
- 排除不可能分支(比如上面例题2,只需考虑180以内的情况)
- 优先走更好的分支
- 随机化(降低因搜索顺序TLE的可能)
随机化多说几句。有个dalao第二题用随机也过了好多点:
当然随机化不是指这个。平时我们搜索都习惯按照一些规律,比如从左到右,从上到下等等。出题人可能会卡这种顺序,导致TLE。所以我们随机顺序,就不会被卡了:
1 //随机化顺序写法 2 #include <algorithm> 3 4 for(int a=1;a<=6;a++) z[a]=z; 5 6 random_shuffle(z+1,z+7); //把1到6随机打乱(即一定范围内的随机顺序)
(顺带补充:)
1 reverse(z+1,z+n+1) //把z[n]~z[1]做一个对称,即,z[1]与z[n]交换,z[2]与z[n-1]交换……
- 卡时(这是我个人觉得今天收获最大的一点) (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的主要内容,如果未能解决你的问题,请参考以下文章
2016.10.29 清北学堂NOIP冲刺班Day1 AM 考试总结
P2327 [SCOI2005]扫雷 [2017年5月计划 清北学堂51精英班Day1]