清北学堂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 }
a

其中有/**/标志的,是最开始没想全面的地方。

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基本用法
  • 通用剪枝:
  1. 最优解问题:超过当前解退出
  2. 解数量问题:重复性利用(记忆化搜索,即之间查到过的状态拿来直接用)
  3. 排除不可能分支(比如上面例题2,只需考虑180以内的情况)
  4. 优先走更好的分支
  5. 随机化(降低因搜索顺序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的主要内容,如果未能解决你的问题,请参考以下文章

2017国庆 清北学堂 北京综合强化班 Day1

2016.10.29 清北学堂NOIP冲刺班Day1 AM 考试总结

P2327 [SCOI2005]扫雷 [2017年5月计划 清北学堂51精英班Day1]

洛谷P1080 [NOIP2012提高组D1T2]国王游戏 [2017年5月计划 清北学堂51精英班Day1]

暑假清北学堂集训笔记

计划2017年5月计划