CCF血泪史

    技术2026-09-02  3

    1.201909-1小明种苹果

    这个题一定要注意,大数组不能直接定义在main函数中,要定义在main函数之外作为全局变量,全局变量在静态存储区分配内存,局部变量是在栈上分配内存空间的。(c语言程序在运行时会动态创建一个堆栈段,里面存放着调用栈,保存着函数的调用关系和局部变量。)如果数组太大,可能会造成栈溢出。

    #include <iostream> using namespace std; long long a[1001][1001]; int main() { int n,m; cin>>n>>m; long long T=0,K=0,P=-1,sum=0,num=0; for(int i=0;i<n;i++) { num=0; for(int j=0;j<m+1;j++) { cin>>a[i][j]; if(j) num-=a[i][j]; } sum+=a[i][0]-num; if(P<num) { P=num; K=i+1; } } cout<<sum<<' '<<K<<' '<<P; return 0; }

    不定义成全局变量还可以用new来动态开辟二维数组,new是从自由存储区为对象动态分配内存的,也不会造成栈溢出。

    #include <iostream> using namespace std; int main() { int n,m; cin>>n>>m; long long **a=new long long *[1001]; for(int i=0;i<1001;i++) a[i]=new long long[1001]; long long T=0,K=0,P=-1,sum=0,num=0; for(int i=0;i<n;i++) { num=0; for(int j=0;j<m+1;j++) { cin>>a[i][j]; if(j) num-=a[i][j]; } sum+=a[i][0]-num; if(P<num) { P=num; K=i+1; } } cout<<sum<<' '<<K<<' '<<P; return 0; }

    2.201903-1小中大

    1.看错题。这个题真的一定要看清楚题目啊!题中说的是对于可能出现的分数,四舍五入保留一位小数,注意四舍五入保留一位小数中四舍五入的是第二位小数!例如:1.85四舍五入就是1.9,而本身只有一位小数的如1.5四舍五入得2应该描述成:四舍五入到个位!而这道题本来最多就是两个数得和是个奇数,除以2得到的是有一位小数的浮点数,不存在要四舍五入的,直接输出其除出来得到的浮点数即可。 2.当输入的数据为偶数个时,中位数是最中间两个数的和的平均值,当这两个数的和是奇数的时候,如果直接这么写:

    med=(double)(a[n/2-1]+a[n/2])/2; cout<<med;//输出中位数

    那么当med这个数据很大的时候,可能会以科学计数法的形式输出中位数,导致答案错误,例: 采用cout的输出或者printf输出都可

    #include <iostream> #include <algorithm> #include <iomanip> using namespace std; int main() { int n,judge=0,max,min; double med; cin>>n; int a[n]; for(int i=0;i<n;i++) cin>>a[i]; sort(a,a+n); max=a[n-1]; min=a[0]; if(n%2) med=a[(n-1)/2]; else if((a[n/2-1]+a[n/2])%2) { med=(double)(a[n/2-1]+a[n/2])/2; cout<<max<<' '<<setiosflags(ios::fixed)<<setprecision(1)<<med<<' '<<min; return 0; } else med=(a[n/2-1]+a[n/2])/2; cout<<max<<' '<<setiosflags(ios::fixed)<<setprecision(0)<<med<<' '<<min; return 0; }

    采用printf的输出:一定要表明输出几位小数,直接用printf("%f",med);输出将会默认输出六位小数,例:

    #include <iostream> #include <algorithm> using namespace std; int main() { int n,judge=0,max,min,med; cin>>n; int a[n]; for(int i=0;i<n;i++) cin>>a[i]; sort(a,a+n); max=a[n-1]; min=a[0]; if(n%2) med=a[(n-1)/2]; else if((a[n/2-1]+a[n/2])%2) { printf("%d %.1f %d", max, (double)(a[n/2-1]+a[n/2])/2, min);//注意一定要写成%.1f,否则将默认输出6位小数 return 0; } else med=(a[n/2-1]+a[n/2])/2; printf("%d %d %d",max,med,min); //或者将med定义double类型,printf("%d %0.f %d",max,med,min); return 0; }

    3.201903-2二十四点

    这道题如果直接暴力解法,将会有64种情况,也可以按照四则运算乘除法优先运算,自左至右依次运算的四则运算法则进行计算。直接暴力解法就是代码稍微长一点,但是运用四则法则进行计算,首先写这个算法就得写一段时间,然后改bug改到心态要崩了,所以如果情况没那么多的时候,可以直接运用暴力解法,可能要更快,但是准确性会更高,把每种情况都测试一遍就ok了。下面给出四则运算方式的代码:

    #include<bits/stdc++.h> using namespace std; int main() { int n,a[4],res;//res记录24点运算结果,a数组的元素为输入的4个数字 cin>>n; char ch[4],b[4];//ch是符号,为防止+-被读入数字中,先用b数组记录数字,然后再转化为数字的a数组 vector<int>prior;//prior记录x/的位置 for(int i=0;i<n;i++) { for(int j=0;j<7;j++) { if(j%2) //先找到x或者/,记录个数以及它们的位置,0、1、3都好办,2个还要分情况是连乘还是用加号分开的 { cin>>ch[j/2]; if(ch[j/2]=='x'||ch[j/2]=='/') { prior.push_back(j/2); } } else { cin>>b[j/2];//为了防止+或者-被读入数字中 a[j/2]=(int)(b[j/2]-'0'); } } if(prior.empty()) //全都是+- { res=a[0]; for(int j=0;j<3;j++) { if(ch[j]=='+') res+=a[j+1]; else res-=a[j+1]; } } else if(prior.size()==3)//全都是*/ { res=a[0]; for(int j=0;j<3;j++) { if(ch[j]=='x') res*=a[j+1]; else res/=a[j+1]; } } else if(prior.size()==1) //只有一个乘 ,出现a-b*c+d怎么办 ? { res=a[prior[0]]; if(ch[prior[0]]=='x') res*=a[prior[0]+1]; else res/=a[prior[0]+1]; if(prior[0])//如果不是在第一个符号位为*/ { if(ch[prior[0]-1]=='-') res=-res; res+=a[0]; for(int j=0;j<3;j++) { if(j!=prior[0]&&j!=(prior[0]-1))//当j不是*/的位置时 { if(ch[j]=='+') res+=a[j+1]; else res-=a[j+1]; } } } else//是在第一个符号位为*/ { for(int j=1;j<3;j++) { if(ch[j]=='+') res+=a[j+1]; else res-=a[j+1]; } } } else if(prior[1]-prior[0]==1) //连乘,出现a-b*c*d怎么办 { res=a[prior[0]]; for(int j=prior[0];j<=prior[1];j++) { if(ch[j]=='x') res*=a[j+1]; else res/=a[j+1]; } if(prior[0]) //a-bxcxd { if(ch[0]=='-') res=a[0]-res; else res+=a[0]; } else//axbxc-d { if(ch[2]=='-') res-=a[3]; else res+=a[3]; } } else//分开*然后相加 { if(ch[2]=='x') res=a[2]*a[3]; else res=a[2]/a[3]; if(ch[1]=='-') res=-res; if(ch[0]=='x') res+=a[0]*a[1]; else res+=a[0]/a[1]; } if(res==24) cout<<"Yes"<<endl; else cout<<"No"<<endl; prior.clear(); } return 0; }

    4.201812-2小明放学

    一定一定要注意各个数据的范围!!!k<=1e6,t<=1e6 ,n<=1e5,如果使用int类型的数据会有数据溢出的可能 !!!一定要看清楚这一题给出的是小明出发的时候,某个路段的红绿灯的剩余秒数,所以要求得小明到达该路段时,该路段是什么颜色的灯以及该灯现在已经亮了多少秒、还剩多少秒,假设红灯亮起的时刻记为0,如果红黄绿灯的时长依次为:30,3,30,红绿灯按:红灯、绿灯、黄灯的顺序循环变换,那么如果给出的第k组数据是3,10,那么在小明出发的时刻,说明其在绿灯阶段,则开始时,该路段的时刻记为30(红灯亮的时间)+30(绿灯亮的时间)-10(绿灯还剩余的时间)=50s,加入经过前k-1个路段花费了ts,那么在到底第k个路段的时候,此时第k个路段的红绿灯状态应该是:令a=(t+50)%(63),看a处在[0,30],[31,60],[61,63)的哪一个时间段,这三个时间段分别对应红灯、绿灯、黄灯,注意如果处在黄灯的时间段,那么等完了黄灯之后还得等红灯,还必须加上红灯段的时间。代码: #include<bits/stdc++.h> using namespace std; int main() {//注意这个题的数据范围:k<=1e6,t<=1e6 ,n<=1e5,如果使用int类型的数据会有数据溢出的可能 long long a[3],n,k,t,sum=0,time=0,med=0,res=0;//sum表示时间的总和,med是一个中间媒介作为工具,res为最终结果 cin>>a[0]>>a[2]>>a[1];//a0、a1、a2分别表示红灯、绿灯、黄灯的时间 sum=a[0]+a[1]+a[2]; cin>>n; while(n--) { cin>>k>>t; if(!k) res+=t;//k=0直接加时 else { if(k==2) k=3;//由于红绿灯是按照红灯、绿灯、黄灯交替,而题目输入的顺序是1红灯,2黄灯,3绿灯,顺序不同,所以对k的顺序作修改,使其与红绿灯变换顺序一致 else if(k==3) k=2; med=res-t; for(long long c=0;c<k;c++) med+=a[c]; med%=sum;//med表示实时时间段 if(med<=a[0]) res+=a[0]-med;//表示其在红灯段 else if(med>=a[0]+a[1]) res+=sum-med+a[0];//其在黄灯段 } } cout<<res; return 0; }

    5.201809-1卖菜

    这个题一定要检查输出数据的组数,必须与商店的数量一致,有边界样例n=2,注意有两个商店时,不要只算完第一个商店然后第二个商店就不管了,第二个商店也必须要输出相对应的值。 注意边界样例的测试!,代码:

    #include<bits/stdc++.h> using namespace std; int main() { int n; cin>>n; int a[n]; for(int i=0;i<n;i++) cin>>a[i]; cout<<(a[0]+a[1])/2<<' '; if(n>2) { for(int i=1;i<n-1;i++) { cout<<(a[i-1]+a[i]+a[i+1])/3<<' '; } } cout<<(a[n-2]+a[n-1])/2; return 0; }

    6.201809-2买菜

    这道题一定要注意时间段和时刻的差别,比如给出的数据中有[13,14],[14,15]这两个时间段,虽然14时刻有重叠,但是实际上两个时间段并不重叠,不能被计入聊天时间种,代码:

    #include <bits/stdc++.h> using namespace std; int a[1000002]={0}; int main() { int n,s,e,res=0; cin>>n; for(int i=0;i<n;i++) { cin>>s>>e; for(int j=s;j<=e;j++) a[j]=s; } for(int i=0;i<n;i++) { cin>>s>>e; for(int j=s;j<e;j++) { if(a[j]&&(a[j]==a[j+1])) //说明是在一个连续的时间段,除去了 [13 14] [14 15]同时装车的时候将14时刻也计入到答案中 { res++; // cout<<"J:"<<j<<' '<<j+1<<endl; } } } cout<<res; return 0; }

    7.201803-1跳一跳

    一定一定一定要认真读题!把题目条件写在程序的注释里或者本子上!自己过一遍!题目的意思是如果跳到了中心,上一次得分为1分或者这是游戏的第一次跳跃,得分才为2分,而不是一开始只要跳上去了不管有没有跳到中心就有2分!代码:

    #include<bits/stdc++.h> using namespace std; int main() { int n,bef,res=0,judge=0,begin=1;//judge表示跳到了2而且是否一直为2,bef表示上一次的得分 while(scanf("%d",&n)&&n) { if(begin) { bef=n; begin=0; res+=bef; if(n==2) judge=1; } else { if(n==1) { bef=1; judge=0; res++; } else if(n==2) { if(judge) bef+=2; else bef=2; res+=bef; judge=1; } } } cout<<res; return 0; }

    8.201803-2碰撞的小球

    一定要注意给定的每个小球的位置不一定是按照顺序来给的,有可能是乱序,必须对小球的位置按照从小到大的顺序排序去做,将小球按照其位置排序后,即使小球撞墙、互相碰撞,其位置的顺序也不会改变,比如有3个球,位置在中间的球无论怎么撞也不可能到三个球的最左边或者最右边,所以最后求出了按照位置的相对顺序排列的小球的位置后,再将其按照编号排序,输出其位置即可。代码:

    #include <bits/stdc++.h> using namespace std; struct ball { int pos,judge=0,shunxu;//judge为0 表示向右运动,为1表示向左运动 }; int cmp1(ball a,ball b) { return a.pos <b.pos ; } int cmp2(ball a,ball b) { return a.shunxu <b.shunxu ; } int main() { int n,L,t; cin>>n>>L>>t; ball b[n]; for(int i=0;i<n;i++) { cin>>b[i].pos; b[i].shunxu=i; } sort(b,b+n,cmp1);//按照位置排序 while(t--) { for(int i=0;i<n;i++) { if(!b[i].judge)//向右运动 { b[i].pos++; } else b[i].pos--; } for(int i=0;i<n-1;i++) { if(b[i+1].pos==b[i].pos&&!b[i].judge&&b[i+1].judge) { b[i].judge=1; b[i+1].judge=0; } if(b[i].pos==L) b[i].judge=1; else if(b[i].pos==0) b[i].judge=0; if(b[i+1].pos==L) b[i+1].judge=1; else if(b[i+1].pos==0) b[i+1].judge=0; } // for(int i=0;i<n;i++) cout<<b[i].pos <<' '; cout<<endl; } sort(b,b+n,cmp2); for(int i=0;i<n;i++) cout<<b[i].pos <<' '; return 0; }

    9.201712-2游戏

    一定一定一定要注意数组越界的问题! ,具体看代码注释:

    #include<bits/stdc++.h> using namespace std; int main() { int n,k,count=0,j=0; cin>>n>>k; vector<int>p; for(int i=0;i<n;i++) p.push_back(i+1); while(p.size()>1) { count++; if((count%k==0)||(count%10==k)) //该处小朋友被淘汰 删除元素 { p.erase(p.begin()+j);//删除元素后,j的值不改变,如果此时删除的是最后一个元素,也就是说j=p.size()-1,设此时p.size()的大小为m,那么删掉了一个元素之后p.size()的值变为m-1,若接下来经历的一次while循环,要接着淘汰一个小朋友,比如本题的7 3样例,12 13是连着的,报了12和13的数小朋友都要被淘汰,此时j的值不变,仍然是m-1,那么运行p.erase(p.begin()+j);的时候就会导致数组越界 } else { j++; if(j>=p.size()) j=0; } } cout<<p[0]; return 0; }

    所以,应该在每次的while循环之后都要判断一次j>=p.size(),因为if else语句中的if{}和else{}要么会改变j的值,要么改变p.size()的值。代码:

    #include<bits/stdc++.h> using namespace std; int main() { int n,k,count=0,j=0; cin>>n>>k; vector<int>p; for(int i=0;i<n;i++) p.push_back(i+1); while(p.size()>1) { // if(j>=p.size()) j=0;//该语句放这儿也行 count++; if((count%k==0)||(count%10==k)) //该处小朋友被淘汰 删除元素 { p.erase(p.begin()+j); } else { j++; // if(j>=p.size()) j=0;//语句不能放这里, 有可能在p删除了一个元素之后p.size()减小,但是j的值不变,导致可能造成数组越界,所以这条语句只能放在j++的else语句的外面,不能放里面 } if(j>=p.size()) j=0;//有可能在p删除了一个元素之后p.size()减小,但是j的值不变,导致可能造成数组越界,所以这条语句只能放在j++的else语句的外面,不能放里面 // cout<<"count:"<<count<<"j:"<<j<<' '; // for(int i=0;i<p.size();i++) cout<<p[i]<<' ';cout<<endl; } cout<<p[0]; return 0; }

    10.201703-2排队

    注意insert函数的使用:

    vector<int>a(100); a.insert(a.begin()+i,p);

    是把p插入到a[i]的位置,即a[i]=p,a[i]之后的元素全都往后移,而不是在a[i]之后插入p,即a[i+1]=p,a[i+1]之后的元素全都往后移。代码:

    #include<bits/stdc++.h> using namespace std; int main() { int n,m,p,q,pos; scanf("%d%d",&n,&m); vector<int>a(n); for(int i=0;i<m;i++) { scanf("%d%d",&p,&q); for(int j=0;j<n;j++) { if(a[j]==p) { pos=j;//找到学号为p的同学的位置 if(q>0) { a.insert(a.begin()+j+q+1,p);//往后移q个位置,因为该元素还未删除且位置在其要插入位置的前面,再插入一个与该元素相等的值,应该是插入到a[j+q+1]的位置 a.erase(a.begin()+j); } else { a.insert(a.begin()+j+q,p); a.erase(a.begin()+j+1); } break; } } // for(int i=0;i<n;i++) printf("%d ",a[i]); } for(int i=0;i<n;i++) printf("%d ",a[i]); return 0; }

    11.202006-2

    这一题一定一定一定要注意时间复杂度,不要乱用二重循环,当数据过大时极有可能超时! 原代码:

    #include<bits/stdc++.h> using namespace std; struct dex { long long pos; int val; }; int main() { int n,a,b,vp,vl; long long res=0; cin>>n>>a>>b; dex u[a]; for(int i=0;i<a;i++) { cin>>u[i].pos >>u[i].val; } for(int i=0;i<b;i++) { cin>>vp >>vl; for(int j=0;j<a;j++) { if(u[j].pos>vp) break; if(u[j].pos ==vp) { res+=vl*u[j].val; break; } } } cout<<res; return 0; }

    上述代码的时间复杂度是O(n2)的,而a b的最大范围是5* 10 5 ,a*b最大可达2.5 * 1011 ,一秒钟运行2 *107次,极易超时,只能得30分。所以应该将两组数据并到一组里按照位置的大小进行排序,位置相等的前后两个dex的值val相乘加入结果res中,代码:

    #include<bits/stdc++.h> using namespace std; struct dex { long long pos; int val; }; int cmp(dex a,dex b) { return a.pos <b.pos; } int main() { int n,a,b; long long res=0; cin>>n>>a>>b; dex u[a+b]; for(int i=0;i<a+b;i++) { cin>>u[i].pos >>u[i].val; } sort(u,u+a+b,cmp); for(int i=0;i<a+b-1;i++) { if(u[i].pos ==u[i+1].pos ) res+=u[i].val *u[i+1].val ; } cout<<res; return 0; }

    此算法的时间复杂度:sort函数O(nlgn),最后一个for循环O(n),a+b的最大值是1*106,不会超时。

    12. 注意:一定一定要注意边界位置!比如第一个单词或者最后一个单词,以及最后一个句子!

    #include<bits/stdc++.h> using namespace std; int main() { char c; vector<string>word; string med=""; while(scanf("%c",&c)!=EOF) { if(c=='\n')//这个句子结束了 { word.push_back(med);//一个句子的最后一个单词一定要加入进来!! med=""; for(int i=word.size()-1;i>=0;i--) cout<<word[i]<<' '; cout<<endl; word.clear(); } else if(c==' ')//这个单词结束了 { word.push_back(med); med=""; } else { med+=c; } } //一定要考虑最后一个句子是否能够输出,非常有可能最后一个输出的字符不是换行符,这个时候最后一个句子不会进行输出,所以要在最后对最后这个句子进行输出!!! word.push_back(med); med=""; for(int i=word.size()-1;i>=0;i--) cout<<word[i]<<' '; cout<<endl; word.clear(); return 0; }
    Processed: 0.013, SQL: 9