夜雨聆风学习资料网

ARTICLE · 1179552

CSP复赛指南(4) |模拟题:复赛最容易拿满的基础大题

CSP复赛指南(4) |模拟题:复赛最容易拿满的基础大题

  很多同学觉得算法题必须掌握高级数据结构、DP、图论。但 CSP-J/S 复赛里,模拟题永远是第一道大题,是保底拿分的核心!模拟题没有复杂算法,核心一句话:严格按照题目描述,一步一步用代码复现题目过程。难点不在于算法,而在于:细心、边界处理、读题、代码实现能力。很多人不是不会做,而是读漏条件、写错变量、漏掉特殊样例,白白丢分。

什么是模拟题?

模拟题 = 把题目文字描述的现实过程,翻译成代码。 不需要你想巧妙公式,题目怎么说,代码就怎么做。 ✅ 特点:

  • 算法难度低,只用到循环、数组、字符串
  • 代码量偏大,细节极多
  • 坑点大多藏在边界、特殊输入、顺序问题
  • 复赛第一题高频出场,是拿基础分的必争之地

❌ 模拟题最常见丢分原因:

  1. 读题跳字,漏掉隐藏条件
  2. 变量混淆,下标从 0 还是从 1 搞混
  3. 没有单独测试样例,直接提交
  4. 忽略极端数据(空输入、最大值、循环结束条件)
  5. 中途修改代码,引入新 bug

做题口诀:先读题,画流程;小样例,手推演;写代码,分步测


【例题1】铺地毯 (难度:普及-)
题目连接:https://www.luogu.com.cn/problem/P1003
题目大意:

为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯。一共有 n 张地毯,编号从 1 到 n。现在将这些地毯按照编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上。

地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的编号。注意:在矩形地毯边界和四个顶点上的点也算被地毯覆盖。

输入共 n + 2 行。

第一行,一个整数 n,表示总共有 n张地毯。

接下来的 n 行中,第 i+1行表示编号 i 的地毯的信息,包含四个整数 a ,b ,g ,k,每两个整数之间用一个空格隔开,分别表示铺设地毯的左下角的坐标 (a, b) 以及地毯在 x 轴和 y 轴方向的长度。

第 n + 2 行包含两个整数 x 和 y,表示所求的地面的点的坐标 (x, y)。

【解题思路】
很多新手会想:开二维数组模拟整张地图。❌ 大坑! 
坐标范围很大,直接开数组会内存爆炸,不能暴力存整张图。
正确模拟思路: 地毯是按顺序铺,后面覆盖前面。 所以我们倒序遍历地毯:从最后铺的第 n 张往第一张检查。 只要查询点 (a,b) 在当前地毯范围内,直接输出当前地毯编号,结束程序。 如果全部地毯检查完都不满足,输出 -1。
【参考代码】
#include<iostream>using namespace std;const int MAXN = 10005;int x[MAXN], y[MAXN], g[MAXN], k[MAXN];intmain(){    int n;    cin >> n;    for(int i = 1; i <= n; i++)    {        cin >> x[i] >> y[i] >> g[i] >> k[i];    }    int a,b;    cin >> a >> b;    // 倒序查找,找到第一个覆盖点的地毯    int ans = -1;    for(int i = n; i >= 1; i--)    {        if(a >= x[i] && a <= x[i]+g[i] && b >= y[i] && b <= y[i]+k[i])        {            ans = i;            break;        }    }    cout << ans << endl;    return 0;}

【例题2】龙虎斗 (难度:普及-)
链接:https://www.luogu.com.cn/problem/P5016

【题目大意】

轩轩和凯凯正在玩一款叫龙虎斗的游戏。游戏有 n 个战场,编号 (1 ~ n),每个战场有一个初始兵力 (s_i)。龙在第 m 号战场,龙的势力:所有编号 ≤ m 的战场兵力总和;虎在第 m 号战场,虎的势力:所有编号 ≥ m 的战场兵力总和。

现在召唤一个神兵,兵力为 p。神兵可以放到任意一个战场(包括 m 号战场)。要求:放置之后,龙势力与虎势力差值的绝对值尽可能小。如果有多个战场满足最优解,选择编号最小的战场。

输入:第一行:整数 n第二行:n 个整数 (s_1,s_2...s_n)第三行:两个整数 (m,p)

输出:一个整数,神兵放置的战场编号。

【题目分析】

1.读入战场兵力,先执行强制操作:(c[p_1] += s_1)。第一支神兵固定放好,这一步不能忘。   
2.分别计算初始龙气势 ql、虎气势 qh
   - 龙气势 ql:遍历 (i=1) 到 (m-1),ql += c[i]*(m-i)
   - 虎气势 qh:遍历 (i=m+1) 到 n,qh += c[i]*(i-m)   
3.初始候选位置:默认放在 m 号战场,此时差值 (cha=abs(ql-qh))。
4.分两种情况枚举:
   - 情况①:qh > ql(虎更强):只能枚举 m 左边所有战场 (i<m)。
   放到 i 之后,新龙气势 = (ql + s_2  *  (m-i)),虎气势不变。
   计算新差值,如果比当前cha更小,更新最小差值,记录位置 i。
   - 情况②:ql >= qh(龙更强或相等):只能枚举 m 右边所有战场 (i>m)。
   放到 i 之后,新虎气势 = (qh + s_2 * (i-m)),龙气势不变。
   计算新差值,如果比当前cha更小,更新最小差值,记录位置 i。
5.枚举是从小到大遍历,遇到相同差值不会更新,自动保留编号最小答案。

模拟核心:严格跟着游戏流程一步步模拟,气势不是兵力,是兵力乘以距离!
很多选手读题直接忽略距离系数,直接当成兵力求和,整题爆零。

【参考答案】

#include<cmath>#include<iostream>#include<algorithm>using namespace std;long long  n,c[100008],m,p1,s1,s2;intmain(){      cin>>n;    for(int i=1;i<=n;i++){        cin>>c[i];    }    cin>>m>>p1>>s1>>s2;    c[p1]+=s1;    long long ql=0,qh=0;    for(int i=1;i<m;i++){     //计算龙的初始气势值         ql+=c[i]*(m-i);    }    for(int i=m+1;i<=n;i++){  //计算虎的初始气势值         qh+=c[i]*(i-m);    }    //cout<<qh<<" "<<ql<<endl;    bool yeshu=false,yeslong=false;    if(qh>ql) yeshu=true;    //判断龙虎大小关系     else yeslong=true;    long long p=m,cha=abs(ql-qh);	if(yeshu==true){    	for(int i=1;i<m;i++){    		long long l2=ql+s2*(m-i);			if(cha>abs(qh-l2)){				cha=abs(qh-l2);				p=i;			}		}	}	else{    	for(int i=m+1;i<=n;i++){    		long long h2=qh+s2*(i-m);			if(cha>abs(ql-h2)){				cha=abs(ql-h2);				p=i;			}		}	}	cout<<p;	return 0;}

【例题3】乒乓球 (难度:普及-)

【题目大意】

华华记录乒乓球每一球的胜负。W代表华华拿到 1 分,L代表对手拿到 1 分,E代表记录结束。 需要分别按照 11 分制 和 21 分制,模拟所有对局的比分。

✅ 一局结束规则(核心!):

  1. 有一方 ≥ 获胜基础分(11 分制是 11,21 分制是 21)
  2. 双方分差 ≥ 2
    两个条件同时满足,本局立刻结束,输出本局比分,然后开启新一局(比分重置为 0:0)。 如果读到 E 结束时,这一局还没有打完,也要输出当前未结束的比分。

输入:一串由W、L组成的字符,以E结尾。 输出: 先输出 11 分制下每一局比分;空一行; 再输出 21 分制下每一局比分。

样例:WWWWWWWWWWWWWWWWWWWWWWLW11 分制:11:011:01:121 分制:21:02:1
【思路分析】

本题属于事件流程模拟,逐球读取,两套计分规则分开模拟。

  1. 读取字符,逐个处理每一分,遇到E停止。
  2. 写一个通用模拟函数,传入获胜门槛(11 或者 21),返回所有局的比分。避免写两份几乎一样的代码,减少 bug。
  3. 一局内维护两个变量:w华华分数,l对手分数。
    • 读到 W:w++;读到 L:l++
    • 判断本局是否结束:(w>=门槛 || l>=门槛) && abs(w-l)>=2
    • 如果本局结束:输出比分,重置 w,l=0,开启下一局
  4. 全部字符读完后,如果当前局还没结束,必须输出当前比分(很多选手漏掉这个,丢分)
  5. 11 分制全部输出完成后,输出一个空行,再输出 21 分制结果,输出格式坑点!

模拟核心:按顺序一分一分推演比赛进程,严格遵守比赛结束条件;两套规则共用一套模拟逻辑。

【参考答案】
#include<iostream>#include<cmath>using namespace std;char buf[1000005];int len = 0;// 根据数组模拟voidsimulate(int winScore){    int w = 0, l = 0;    for(int i = 0; i < len; i++)    {        char ch = buf[i];        if(ch == 'E') break;        if(ch == 'W') w++;        else if(ch == 'L') l++;        if( (w >= winScore || l >= winScore) && abs(w - l) >= 2 )        {            cout << w << ":" << l << endl;            w = 0;            l = 0;        }    }    cout << w << ":" << l << endl;}intmain(){    char ch;    // 第一步:读取全部字符,存入buf,跳过空格换行    while( cin>>ch && ch!='E' )    {        buf[len++] = ch;    }    simulate(11);    cout << endl;    simulate(21);    return 0;}

其他模拟题目:
比例简化:https://www.luogu.com.cn/problem/P2118
校门外的书:https://www.luogu.com.cn/problem/P1047
ISBN号:https://www.luogu.com.cn/problem/P1055
小苹果:https://www.luogu.com.cn/problem/P9748
总结:

在 CSP-J 复赛里,模拟题几乎固定是第一题,是整场考试的保底得分基石。

模拟题不考察复杂高级算法,不需要掌握搜索、DP、图论,只考验读题能力、细心程度和基础代码功底。只要认真读题、注意细节,绝大多数同学都有机会拿到满分。

复赛想要拿到省一 / 省二,首先就要把模拟题稳稳拿下。一旦模拟题丢大量分数,后面难题再想追回分数会非常困难;同时,模拟题也是建立考场信心的关键,顺利写完第一题,能让你更好地进入状态,继续完成后面的题目。

一句话概括:模拟题是复赛的基本盘,基本盘稳住,才有冲奖的机会。

相关学习资料