今天小模拟了一下天梯赛的考试,这些题不是真题,只是模拟题,不过在网上都搜得到。
比如团体程序设计天梯赛-练习集L1-043 阅览室 (20 分)_牛客网等等。
如果是这样可以宣告打铁了。

关于时间
那么我先说说对于时间怎么分配是合适的。
笔者的水平理论极限值是前12题拿满+剩下3题的暴力。
而天梯赛限时三个小时,要干掉15个题(实际上写完14个都算发挥超常了),对于时间的把控程度不亚于考一张理综。
那么我们练的就要是熟练度,还有debug的能力。我觉得真正对考试起到决定性作用的是 L1 6-8的速度和 L2 四个题的前后顺序,这些题处理的如何直接决定着你能不能按照你的水平完美发挥出来,而不留空题。
L1
这里宛如数学的前8个选择(老高考12单选模式),基本上是平推的,但是也大有可能在这里出问题。我做完L1-4用时15min,做完L1-8用时1h25min。可以说接近崩盘了。
L1-6 检查密码 考点:字符串处理

好,这是字符串处理+ifelse语句。
我犯下的错误是:把<写成<=,导致处理到字符串结尾,也就是'\0'处。
cpp
#include<iostream>
#include<iomanip>
#include<cmath>
#define i64 long long
using namespace std;
string s;
i64 n=0;
bool num=0,word=0,oth=0;
int main(){
cin>>n;
cin.ignore();
while(n--){
num=0,word=0,oth=0;
getline(cin,s);
//cout<<s<<' '<<s.length()<<endl;
if(s.length()<6){
cout<<"Your password is tai duan le.";
}
else{
for(i64 i=0;i<s.length();i++){
if(s[i]<='9'&&s[i]>='0'){
num=1;
}
if((s[i]<='z'&&s[i]>='a')||(s[i]<='Z'&&s[i]>='A')){
word=1;
}
if(!(s[i]<='z'&&s[i]>='a') && !(s[i]<='Z'&&s[i]>='A') && !(s[i]<='9'&&s[i]>='0') && !(s[i]=='.') ){
//cout<<"乱"<<s[i]<<i<<endl;
oth=1;
}
}
//cout<<num<<' '<<word<<' '<<oth<<endl;
if(oth==1) cout<<"Your password is tai luan le.";
else{
if(word==1&&num==0) cout<<"Your password needs shu zi.";
else if(word==0&&num==1) cout<<"Your password needs zi mu.";
else cout<<"Your password is wan mei.";
}
}
if(n!=0) cout<<endl;
}
return 0;
}
L1-7 谷歌的招聘 考点:字符串处理、素数判断



这一题简而言之就是从字符串截取一个特定长度看是否为质数。
好,字符串截取问题,犯下了如下错误:
1.灾难性地忘记了s.substr(),纯手写函数
2.最后一个遍历的位置=len-k,而不是len-k-1。比如k=5,len-k的位置往后就可以截取到
s[len-5]s[len-4]s[len-3]s[len-2]s[len-1]。而不会到达len本身这个'\0'处,也不会漏一个字符。
3.不知道怎么输出前导0,好在最后想明白了。
最后19/20.
cpp
#include<iostream>
#include<iomanip>
#include<cmath>
#define i64 long long
using namespace std;
string s;
bool flag=0;
i64 n,k,len;
i64 fun(i64 pos);
bool isprime(i64 num);
void mycout(i64 num);
int main(){
cin>>len>>k;
cin>>s;
for(i64 i=0;i<=len-k;i++){
if(isprime(fun(i))){
mycout(fun(i));
flag=1;
break;
}
}
if(!flag) cout<<404<<endl;
return 0;
}
i64 fun(i64 pos){
i64 num=0;
for(i64 i=pos;i<pos+k;i++){
num=num*10+(s[i]-'0');
}
return num;
}
bool isprime(i64 num){
if(num==1) return 0;
for(i64 i=2;i<=sqrt(num);i++){
if(num%i==0)
return 0;
}
return 1;
}
void mycout(i64 num){
for(i64 i=1;i<=k;i++){
cout<<num/(i64)pow(10,k-i)%10;
}
return ;
}
/*
6 4
200236
*/
L1-8 阅览室 考点:字符串处理、标志位灵活运用

输入样例:
3
1 S 08:10
2 S 08:35
1 E 10:00
2 E 13:16
0 S 17:00
0 S 17:00
3 E 08:10
1 S 08:20
2 S 09:00
1 E 09:20
0 E 17:00
输出样例:
2 196
0 0
1 60
这题本身就是传说中的恶心人的题了,我也确确实实在这上面花了不配它的时间。
但是天梯赛L1关必须打满80分,这一题就20分,所以不得不写。可惜,一写就是好久。
现在这题又要自己去写字符串处理函数。
读取出来的数还要传导出来到main函数本身这里。
然后就是时间判定逻辑,你必须有两个状态标志,第一个是记录时间,第二个是记录它之前的状态是否完善,不然可能读错数据。
最后需要四舍五入,用setprecision函数。
但是还是14/20。
cpp
#include<iostream>
#include<iomanip>
#include<cmath>
#define i64 long long
using namespace std;
string s;
i64 n,num,oop,tms;
i64 a[1005],cnt=0,ans=0;
bool jie[1005];
void myread();
void init();
int main(){
cin>>n;
cin.ignore();
while(n){
getline(cin,s);
myread();
if(num==0){
n--;
double res=0;
if(cnt>0){
res=ans*1.0/cnt;
}
cout<<cnt<<' '<<fixed<<setprecision(0)<<res<<endl;
cnt=0;
ans=0;
init();
continue;
}
if(oop==1) {
if(jie[num]==0){
a[num]=tms;
//cout<<ans<<' '<<num<<' '<<op<<' '<<tms<<endl;
jie[num]=1;
}
}
else if(oop==0){
if(jie[num]==1){
++cnt;
ans+=tms-a[num];
//cout<<ans<<' '<<num<<' '<<op<<' '<<tms<<endl;
a[num]=0;
}
}
}
return 0;
}
void myread(){
i64 nnum=0,pos=0;
for(i64 i=0;s[i]!=' ';i++){
nnum=nnum*10+s[i]-'0';
pos=i;
}
bool nnop;
if(s[pos+2]=='S') nnop=1;
else if(s[pos+2]=='E') nnop=0;
i64 hh=0,mm=0,ntms=0;
hh=(s[pos+4]-'0')*10+(s[pos+5]-'0');
mm=(s[pos+7]-'0')*10+(s[pos+8]-'0');
ntms=hh*60+mm;
//cout<<nnum<<' '<<nop<<' '<<ntms<<endl;
num=nnum;
oop=nnop;
tms=ntms;
return ;
}
void init(){
for(i64 i=1;i<=1000;i++){
a[i]=0;
jie[i]=0;
}
return ;
}
L2
做下来的感觉就是,L2的处理顺序完全决定着下限的高低。事实上目前见过的L2的所有题都是能暴力弄出来的,只是用时和得分平衡的问题。
L2-1 出栈序列的合法性 考点:栈、队列

它真的只是个栈模拟。。。
先把出栈数据压进一个队列q,然后把从1到n的整个入栈顺序给模拟出来,用stk[]和top记录。
如果本处是队首,那么该位置出栈、数据出队,这就是所谓的模拟出栈过程。
如果发现队列和栈没出干净,那就是不合法序列。
如果top超过限制m,就是栈太大,不合法。
花了25分钟。
cpp
#include<iostream>
#include<iomanip>
#include<cmath>
#include<queue>
#define i64 long long
using namespace std;
i64 m,n,k,nixu=0;
i64 a;
queue<i64> q;
int main(){
cin>>m>>n>>k;
while(k--){
i64 flag=1;
i64 stk[1005],top=0;
for(i64 i=1;i<=n;i++){
cin>>a;
q.push(a);
}
for(i64 i=1;i<=n;i++){
stk[++top]=i;
if(top>m){
flag=0;
//cout<<"NO"<<endl;
}
while(!q.empty()&&stk[top]==q.front()){
//cout<<stk[top]<<endl;
stk[top--]=0;
q.pop();
}
}
while(!q.empty()){
flag=0;
q.pop();
//cout<<"NO"<<endl;
}
while(top){
flag=0;
stk[top--];
//cout<<"NO"<<endl;
}
if(flag) cout<<"YES";
else cout<<"NO";
if(k!=0) cout<<endl;
}
return 0;
}
L2-2 抢红包 考点:结构体排序

输入样例:
10
3 2 22 10 58 8 125
5 1 345 3 211 5 233 7 13 8 101
1 7 8800
2 1 1000 2 1000
2 4 250 10 320
6 5 11 9 22 8 33 7 44 10 55 4 2
1 3 8800
2 1 23 2 123
1 8 250
4 2 121 4 516 7 112 9 10
输出样例:
1 11.63
2 3.63
8 3.63
3 2.11
7 1.69
6 -1.67
9 -2.18
10 -3.26
5 -3.26
4 -12.32
注意元角分的转换,注意排序顺序是收入------收到红包个数------个人编号。
所以就是道简单结构体排序,理清楚题意,定义好结构体即可。
花了25分钟,感觉这类题练熟来得写进20分钟。
cpp
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#define i64 long long
using namespace std;
i64 n,k,ni,pi,a[10005],c[10005];
struct node{
i64 num,mon,rec;
}b[10005];
bool cmp(struct node x,struct node y){
if(x.mon==y.mon){
if(x.rec==y.rec){
return x.num<y.num;
}
return x.rec>y.rec;
}
return x.mon>y.mon;
}
int main(){
cin>>n;
for(i64 i=1;i<=n;i++){
cin>>k;
for(i64 j=1;j<=k;j++){
cin>>ni>>pi;
c[ni]++;
a[ni]+=pi;
a[i]-=pi;
}
}
for(i64 i=1;i<=n;i++){
b[i].mon=a[i];
b[i].num=i;
b[i].rec=c[i];
}
sort(b+1,b+n+1,cmp);
for(i64 i=1;i<=n;i++){
cout<<b[i].num<<' '<<fixed<<setprecision(2)<<b[i].mon*1.0/100;
cout<<endl;
}
return 0;
}
L2-3 二叉搜索树的2层节点统计 考点:数组模拟建树、二叉搜索树

如果学过数据结构,题意就很简单,模拟实现一个二叉排序树就行了。
不过一句"行了",恰恰是最不行的~~
数组怎么模拟树?首先定义好节点四元素:双亲、数值、左子、右子,写到后面发现要统计深度。
struct node{
i64 num,prt,lson,rson,depth;
}a[1005];
接着初始化。
然后就是实现二叉搜索树,也是我犯大错的地方。
号外:写到这里我突然想明白为什么会挂掉两个点了!
咳咳,二叉搜索树是不完美二叉树,它的树状肯定是千奇百怪,但是要能够精准定位到节点,那就来模拟一次:
搜索拐弯有两种情况:数值更小,或者节点是叶子。
向右搜索,直到拐弯;向左搜索,直到拐弯;再向右,再向左;循环往复。(我漏掉了外面的大层循环。)
给一个测试点,这个树是一条蜿蜒的河。
7
50 60 70 65 61 63 62
然后就是条件判断和统计,这些比较繁琐但不难。
喜提AC。
cpp
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#define i64 long long
using namespace std;
i64 n,x;
struct node{
i64 num,prt,lson,rson,depth;
}a[1005];
void dfs(i64 x);
int main(){
cin>>n;
for(i64 i=1;i<=n;i++){
cin>>x;
a[i].num=x;
a[i].prt=-1;
a[i].lson=-1;
a[i].rson=-1;
a[i].depth=1;
}
for(i64 i=2;i<=n;i++){
i64 nxt=i,lst=1;
while(1) {
//先向右
if(a[nxt].num>a[lst].num){
if(a[lst].rson!=-1){
lst=a[lst].rson;
}else{
break;
}
}else if(a[nxt].num<=a[lst].num){
if(a[lst].lson!=-1){
lst=a[lst].lson;
}else{
break;
}
}
else break;
}
if(a[nxt].num<=a[lst].num){
a[nxt].prt=lst;
a[lst].lson=nxt;
a[nxt].depth=a[lst].depth+1;
//cout<<666<<endl;
}else{
a[nxt].prt=lst;
a[lst].rson=nxt;
a[nxt].depth=a[lst].depth+1;
//cout<<777<<endl;
}
//cout<<a[nxt].num<<' '<<a[nxt].prt<<' '<<a[nxt].rson<<' '<<a[nxt].lson<<endl;
//cout<<a[lst].num<<' '<<a[lst].prt<<' '<<a[lst].rson<<' '<<a[lst].lson<<endl;
}
//dfs(1);
i64 mx=0,cnt=0;
for(i64 i=1;i<=n;i++){
mx=max(mx,a[i].depth);
}
for(i64 i=1;i<=n;i++){
if(a[i].depth==mx-1||a[i].depth==mx)
++cnt;
}
cout<<cnt<<endl;
return 0;
}
void dfs(i64 x){
cout<<a[x].num<<' '<<a[x].depth<<endl;
if(a[x].lson!=-1) dfs(a[x].lson);
if(a[x].rson!=-1) dfs(a[x].rson);
return ;
}
/*
9
25 30 42 16 20 20 35 -5 28
7
50 55 60 57 56 58 59
*/
L2-4 秀恩爱分得快 考点:矩阵表示法

输入样例 1:
10 4
4 -1 2 -3 4
4 2 -3 -5 -6
3 2 4 -5
3 -6 0 2
-3 2
输出样例 1:
-3 2
2 -5
2 -6
输入样例 2:
4 4
4 -1 2 -3 0
2 0 -3
2 2 -3
2 -1 2
-3 2
输出样例 2:
-3 2
目前这么长一坨代码15/25,写到超过时间半小时。
题目读了就能懂,模拟干就完了。
cpp
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#define i64 long long
using namespace std;
i64 n,m,k,x[2005],a,b;
double s[2005][2005]={0};
double qin=0;
bool cmp(i64 x,i64 y);
int main(){
cin>>n>>m;
for(i64 i=1;i<=m;i++){
cin>>k;
qin=1.0/k;
for(i64 j=1;j<=k;j++){
cin>>x[j];
}
for(i64 j=1;j<=k-1;j++){
for(i64 t=j+1;t<=k;t++){
//cout<<x[j]<<' '<<x[t]<<' '<<qin<<endl;
s[x[j]+1000][x[t]+1000]+=qin;
s[x[t]+1000][x[j]+1000]+=qin;
}
}
}
cin>>a>>b;
a+=1000;
b+=1000;
i64 c=min(a,b);
i64 d=max(a,b);
double mx=0;
bool f1=0,f2=0;
i64 stk1[1005],top1=0;
i64 stk2[1005],top2=0;
for(i64 i=1000;i<=2000;i++){
if(s[c][i]!=0){
mx=max(mx,s[c][i]);
}
}
for(i64 i=1000;i<=2000;i++){
if(s[c][i]==mx){
stk1[++top1]=i;
if(i==d) f1=1;
}
}
mx=0;
for(i64 i=0;i<=1000;i++){
if(s[d][i]!=0){
mx=max(mx,s[d][i]);
}
}
for(i64 i=0;i<=1000;i++){
if(s[d][i]==mx){
stk2[++top2]=i;
if(i==c) f2=1;
}
}
sort(stk2+1,stk2+top2+1,cmp);
if(f1&&f2){
cout<<a-1000<<' '<<b-1000<<endl;
}else{
//c必定女 d必定男
if(c==a){//a女
for(i64 i=1;i<=top1;i++){
cout<<c-1000<<' '<<stk1[i]-1000<<endl;
}
for(i64 i=1;i<=top2;i++){
cout<<d-1000<<' '<<stk2[i]-1000<<endl;
}
}else if(c==b){//a男
for(i64 i=1;i<=top2;i++){
cout<<d-1000<<' '<<stk2[i]-1000<<endl;
}
for(i64 i=1;i<=top1;i++){
cout<<c-1000<<' '<<stk1[i]-1000<<endl;
}
}
}
return 0;
}
bool cmp(i64 x,i64 y){
return x>y;
}
番外 L3-3的思考
然后这次的3-3是解析几何算面积,我本来想先动手这个的,但是发现越做要考虑的情况就越多,所以放弃了。
联想到我上一次CSP就是求把第五题暴力出来,结果越想条件越复杂,写了两小时没暴力出来。看第三题,我最后考试30分钟都快模拟完了,真的很简单,我回去完善一下写了75分。真的是被自己蠢哭了。
所以,题目这么给你牌是真得信它的道理的,不会真有人觉得暴力骗分是件简单活?
