参考:《骗分导论-第7修订测试版》《骗分导论-第6修订测试版》《骗分导论-第5修订测试版》《骗分导论-第4修订测试版》《骗分导论-第3修订测试版》《骗分导论-第2修订测试版》《骗分导论-第1修订测试版》

作者:刘李赟果https://www.luogu.com.cn/user/677142,赵炳琪https://www.luogu.com.cn/user/677139,何弘毅https://www.luogu.com.cn/user/677138,畅林啸https://www.luogu.com.cn/user/677152

帮助-感谢:Aw顿顿の小窝https://www.luogu.com.cn/blog/LinearExpectation/,杨程皓https://www.luogu.com.cn/user/677367,李语桐https://www.luogu.com.cn/user/677140#practice

赞助团队(欢迎加入):

六李赟果团队https://www.luogu.com.cn/team/47520#memberXA强基班的附属品https://www.luogu.com.cn/team/47561Dreams_Promisehttps://www.luogu.com.cn/team/47663

大佐大队https://www.luogu.com.cn/team/47529首先我发现了几个事情:

一个人一定是他妈妈的儿子

我是我同桌的同桌,我同桌是我的同桌

第1章 绪论

在 OIer 中,有一句话广为流传:

“ 任何蒟蒻必须经过大量的刷题练习才能成为大牛乃至于神牛 ”“ 任何蒟蒻必须经过大量的刷题练习才能成为大牛乃至于神牛 ”

这就是著名的 lzn 定理。然而,我们这些蒟蒻们,没有经过那么多历练,却要和大牛们同场竞技,我们该怎么以弱胜强呢?答案就是:

骗分骗分

那么,骗分是什么呢?骗分就是用简单的程序(比标准算法简单很多,保证蒟蒻能轻松搞定的程序),尽可能多得骗取分数。

让我们走进这篇《骗分导论》,来学习骗分的技巧,来挑战神牛吧!

编者的话:

无论是 NOIp 还是 CSP 等比赛,骗分以及暴力都是极其有效的一种手段,尤其在你对题目所使用的算法不甚熟悉甚至于完全不能理解题意的时候,这往往能为你带来很多的分数。没有人知道你的程序是什么样的,所以你就算骗到了分也不会有人说你无耻,更何况这是极其常见的办法。

第2章 从无解出发

  • 2.1 无解情况

在很多题目中都有这句话:

“ 若无解,请输出 -1。”“ 若无解,请输出 -1。”

看到这句话时,骗分的蒟蒻们就欣喜若狂,因为——数据中必定会有无解的情况!那么,只要打出下面这句话:

printf("-1"); 就能得到 1010 分,甚至 2020 分,3030 分!

但是由于出题人常常会为了卡掉骗分的情况而使用多组数据测试,导致骗分失败,这时候我们就无能为力了。

但是依然举个例子(这道题目是个错题,数据不能卡掉为数众多的乱搞做法):

  • NOIP2012第4题:文化之旅
题目描述 Description有一位使者要游历各国,他每到一个国家,都能学到一种文化,但他不愿意学习任何一种文化超过一次
(即如果他学习了某种文化,则他就不能到达其他有这种文化的国家)。不同的国家可能有相同的文化。
不同文化的国家对其他文化的看法不同,有些文化会排斥外来文化(即如果他学习了某种文化
,则他不能到达排斥这种文化的其他国家)。现给定各个国家间的地理关系,各个国家的文化,每种文化对其他文化的看法,以及这位使者游历的起点和终点
(在起点和终点也会学习当地的文化),国家间的道路距离,试求从起点到终点最少需走多少路。输入描述 Input Description第一行为五个整数N,K,M,S,T,每两个整数之间用一个空格隔开,依次代表国家个数(国家编号为1到N),
文化种数(文化编号为1到K),道路的条数,以及起点和终点的编号(保证S不等于T);第二行为N个整数,每两个整数之间用一个空格隔开,其中第i个数Ci,表示国家i的文化为Ci。接下来的K行,每行K个整数,每两个整数之间用一个空格隔开,记第i行的第j个数为aij,aij= 1表示文化i排斥外来文化j(i等于j时表示排斥相同文化的外来人),
aij= 0表示不排斥(注意i排斥j并不保证j一定也排斥i)。接下来的M行,每行三个整数u,v,d,每两个整数之间用一个空格隔开,
表示国家u与国家v有一条距离为d的可双向通行的道路(保证u不等于v,两个国家之间可能有多条道路)。输出描述 Output Description输出只有一行,一个整数,
表示使者从起点国家到达终点国家最少需要走的距离数,如果无解则输出-1样例输入 Sample Input输入样例12 2 1 1 21 2
0 1
1 0
1 2 10
输入样例22 2 1 1 21 2
0 1
0 0
1 2 10
样例输出 Sample Output输出样例1-1
输出样例210
数据范围及提示 Data Size & Hint【输入输出样例1说明】由于到国家2必须要经过国家1,而国家2的文明却排斥国家1的文明,所以不可能到达国家2。【输入输出样例2说明】路线为1 -> 2。【数据范围】对于20%的数据,有2≤N≤8,K≤5;对于30%的数据,有2≤N≤10,K≤5;对于50%的数据,有2≤N≤20,K≤8;对于70%的数据,有2≤N≤100,K≤10;对于100%的数据,有2≤N≤100,1≤K≤100,1≤M≤N2,1≤ki≤K,1≤u,v≤N,1≤d≤1000,S≠T,1 ≤S, T≤N。

这道题看起来很复杂,但其中有振奋人心的一句话 “输出-1” ,只要打

printf("-1");

得 1010 分。

而这道题目的暴力其实也不难写,只要你对暴力(比如 Floyd DFS BFS 等一类暴力算法)有深入的了解,你就一定能够找准方向,得到 3030 到 6060 左右的部分分,而这也十分重要。

  • 2.2 样例——白送的分数

每道题目的后面,都有一组(*编者注:较难的题目甚至多组,可以帮助我们发现不同的特殊情况)“样例输入”和“样例输出”。它们的价值极大,不仅能初步帮你检验程序的对错(特别坑的样例除外),而且,如果你不会做这道题(这种情况蒟蒻们已经司空见惯了),你就可以直接输出样例!

而你甚至可以手造特殊数据来进行输出(由于特殊数据是非常可能考到的一个坑点),而正常考试的时候那样例和自己写的 gen 来对拍也不错。

例如美国的 USACO (*编者注:美国经典算法题库),它的题目有一个规则,就是第一组数据必须是样例。那么,只要你输出所有的样例,你就能得到 100100 分(满分 10001000 )!这是相当可观的分数了,在比赛中相当于可以让我们的奖项认证提高一级左右的等级。

现在,你已经掌握了最基础的骗分技巧。只要你会基本的输入输出语句,你就能实现这些骗分方法。但是这依然不够,在绝大多数比赛之中,这样是不能拿到高分的。那么,如果你有一定的基础,请看下一章——我将教你怎样用简单方法骗取部分分数。

第3章 “艰苦朴素永不忘”

本章的标题来源于《学习雷锋好榜样》的一句歌词,但我不是想教导你们学习雷锋精神,而是学习骗分!

看到“朴素”两个字了吗?它们代表了一类算法,主要有模拟DFS。下面我就来介绍它们在骗分中的应用。

  • 3.1 模拟

所谓模拟,就是用计算机程序来模拟实际的事件。

例如NOIP2012的“寻宝”,就是写一个程序来模拟小明上藏宝塔的动作。

较繁的模拟就不叫骗分了(*编者注:如猪国杀、鸭棋),我这里也不讨论这个问题,但是简单的对于题目进行模拟,往往能拿到 1010 分左右的分数,如果对于模拟有特别设置部分分或者数据随机,往往得分在一个很可观的情况下。

模拟主要可以应用在骗高级数据结构题上的分,例如线段树(但是线段树基础也要会),而如果你打算用单纯的模拟来做可持久化文艺平衡树套KDT的话,这边建议您洗洗睡呢。下面举一个例子来说明一下:

排 队(USACO 2007 January Silver)

【问题描述】每天,农夫约翰的N(1≤N≤50000)头奶牛总是按同一顺序排好队,有一天,约翰决定让一些牛玩一场飞盘游戏
(Ultimate Frisbee),他决定在队列里选择一群位置连续的奶牛进行比赛,
为了避免比赛结果过于悬殊,要求挑出的奶牛身高不要相差太大。约翰准备了Q(1≤Q≤200000)组奶牛选择,并告诉你所有奶牛的身高Hi(1≤  Hi ≤106)。
他想知道每组里最高的奶牛和最矮的奶牛身高差是多少。注意:在最大的数据上,输入输出将占据大部分时间。【输入】第一行,两个用空格隔开的整数N和Q。第2到第N+1行,每行一个整数,第i+1行表示第i头奶牛的身高Hi第N+2到第N+Q+1行,每行两个用空格隔开的整数A和B
,表示选择从A到B的所有牛(1 ≤ A ≤ B ≤ N)【输出】共Q行,每行一个整数,代表每个询问的答案。输入样例    输出样例6 3
1
7
3
4
2
5
1 5
4 6
2 2         63
0

对于这个例子,大牛们可以写个线段树,而我们蒟蒻,就模拟吧。

附模拟程序(*编者注:核心程序):

for(int i=1;i<=q;i++){scanf("%d%d",&a,&b);int min=INT_MAX,max=INT_MIN;for(int i=a;i<=b;i++){if(h[i]<min)min=h[i];if(h[i]>max)max=h[i];}printf("%d\n",max-min);
}

程序简洁明了,并且能高效骗分,也就是在保证正确性的情况下可以拿到相当高的部分分。本程序得 5050 分。

  • 3.2 万能钥匙——DFS

DFS是图论中的重要算法。

但我们看来,图论神马的都是浮云,关键就是如何骗分。下面引出本书的第2条定理:

“ DFS是万能的。”“ DFS是万能的。”

这对于你的骗分是至关重要的。比如说,一些动态规划(*编者注:极其重要算法之一)题,可以DFS;数学题,可以DFS;剪枝的题,更能DFS。下面以一道老题为例,解释一下DFS骗分。(*编者注:DFS,即深度优先搜索,常常被称为暴力,其核心思想是将所有可能全部遍历一遍,以递归形式出现,但是容易导致超时)

例题:

NOIP2003:采药


题目描述 Description辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。
为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。
医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,
采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。
如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”如果你是辰辰,你能完成这个任务吗?输入描述 Input Description输入第一行有两个整数T(1<=T<=1000)和M(1<=M<=100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。
接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。输出描述 Output Description输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。样例输入 Sample Input70 3
71 100
69 1
1 2
样例输出 Sample Output3
数据范围及提示 Data Size & Hint对于30%的数据,M<=10;对于全部的数据,M<=100。

这题的方法很简单。我们瞄准 30%30% 的数据来做,可以用 DFS 枚举方案,然后模拟计算出最优解。附一个大致的代码:

void DFS(int d,int c){if(d==n){if(c>ans)ans=c; return;}DFS(d+1,c+w[i]);DFS(d+1,c);
}

这里附一下编者的重要提醒:

  • 搜索是算法的基础,往往编写难度低且容易查错。
  • 暴力搜索可以帮助我们拿到一些正解难度较高题目的部分分。
  • 暴力普适性强用处广,通过暴力获得骗分亦是考场上不错的选择。

OI 界有句话叫做“暴力打满”,即一次比赛拿到所有的暴力的分,考虑到在 NOIp/CSP 中暴力分较多,况且常数优化和暴力的一些剪枝也可以拿到更多分(而且像普及组 T1/T2 以及提高组的 Day1 T1 撰写正解难度并不高),拿到省一其实希望很大。

但是暴力也要找准方向,纯粹的枚举是愚蠢的,我们要根据题目状况设定特定的暴力策略

DFSDFS 深度优先搜索

深度优先搜索(缩写 DFS)是对一个连通图进行遍历的算法。

其思想是从一个顶点 �0V0​ 开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节点,反复进行上述过程,从另一条路开始走到底,这样就可以较为有序地遍历所有的节点,这种尽量往深处走的概念即是深度优先的概念。

这样可以按顺序遍历所有的点,时间复杂度较高,所以称为暴力搜索,我们通常采用递归的形式编写程序,这样的搜索可以通过剪枝优化,从而使重复的搜索减少。

【例题】P1596 [USACO10OCT]Lake Counting S

void dfs(int x,int y){a[x][y]='.';int dx,dy;for(int i=-1;i<=1;i++)for(int j=-1;j<=1;j++){dx=x+i;dy=y+j;if(dx>=0&&dx<=n&&dy>=0&&dy<m&&a[dx][dy]=='W')dfs(dx,dy);}return;
} 

有一些基础的题目在洛谷没有,所以我创建并改良了几道类模板题供大家练手,详见此处。

BFSBFS 广度优先搜索

我们首先访问顶点 ��vi​,然后访问 ��vi​ 的所有未被访问的邻接点 �1,�2,…,��w1​,w2​,…,wk​。依次从这些邻接点出发,访问它们的所有未被访问的邻接点; 依此类推,直到图中所有访问过的顶点的邻接点都被访问。

这时候我们需要利用队列来保证我们访问节点的顺序,此时我们的效率比深搜大大提高,原因是我们能够省去很多的重复步骤,程序架构如下:

void bfs(){定义队列 q;q.push(初始状态);while(队不空){x(y)=队头状态信息;出队;if(下一步可行)下一步;}
}

【例题】P1451 求细胞数量

struct grid{int x,y;
};
queue<struct grid> q;
void BFS(){cnt++;while(!q.empty()){int x=q.front().x;int y=q.front().y;vis[x][y]=1; q.pop();for(int i=0;i<4;i++){int xx=x+dx[i],yy=y+dy[i];if(yy<m&&xx<n&&xx>=0&&yy>=0){if(!vis[xx][yy]&&p[xx][yy]!='0'){q.push({xx,yy});}} }}return;
}

【例题】AT1350 深さ優先探索

struct grid{int x,y;
};
bool check(int x,int y){return (!vis[x][y]&&x>=1&&x<=n&&y>=1&&y<=m);
}
void bfs(){queue<grid>q;q.push({x,y});while(!q.empty()){int xx=q.front().x,yy=q.front().y;q.pop();vis[xx][yy]=1;if(check(xx+1,yy))q.push({xx+1,yy});if(check(xx,yy+1))q.push({xx,yy+1});if(check(xx-1,yy))q.push({xx-1,yy});if(check(xx,yy-1))q.push({xx,yy-1});}
}

优化:剪枝/记忆化搜索

常言道:记搜+剪枝可以解决一切问题

记忆化搜索实际上是递归来实现的,但是递归的过程中有许多的结果是被反复计算的,这样会大大降低算法的执行效率。

记忆化搜索:

而记忆化搜索是在递归的过程中,将已经计算出来的结果保存起来,当之后的计算用到的时候直接取出结果,避免重复运算,因此极大的提高了算法的效率。

剪枝:

  • 可行性剪枝

当目前状态和题意不符,并且由于题目可以推出,往后的所有情况和题意都不符,那么就可以进行剪枝,直接把这种情况及后续的所有情况判断不可行,直接返回。

  • 排除等效冗余

当几个字树具有完全相同的效果的时候,只选择其中一个搜索。

  • 最优性剪枝

在我们用搜索方法解决最优化问题的时候的一种常用剪枝。当搜索还未结束时,记录的状态已经比当前保存的最优解更劣,那么此方案一定无效,停止搜索并回溯即可。

  • 顺序剪枝

普遍来讲,搜索的顺序是不固定的,对一个问题来说,算法可以进入搜索树的任意的一个子节点。

但假如我们要搜索一个最小值,而非要从最大值存在的那个节点开搜,就可能存在搜索到最后才出解。我们从最小的节点开搜很可能马上就出解。这就是顺序剪枝的一个应用。一般来讲,有单调性存在的搜索问题可以和贪心思想结合,进行顺序剪枝。

此剪枝一定优先按照题目要求!

第4章 骗分的关键——猜想

  • 4.1 听天由命 如果你觉得你的人品很好,可以试试这一招——输出随机数。

先看一下代码:

#include<stdlib.h>
#include<time.h>
//以上两个头文件必须加
srand(time(NULL));
//输出随机数前执行此语句
printf("%d",rand()%X);
//输出一个0~X-1的随机整数。

这种方法适用于输出一个整数(或判断是否)的题目中,答案的范围越小越好。让老天决定你的得分吧。

据说,在NOIP2013中,有人最后一题不会,愤然打了个随机数,结果得了70分啊!!(*编者注:不要过于相信你的人品,可能多次评测,大概率会爆零,尤其对于数据量大的情况)

但是,如果是数学题,不妨根据结果猜结论,如果你能根据样例推断式子的情况,就可能能够拿到全部的分!尤其是对于 10181018 的题目,如果不是 �(log⁡�)O(logn) 的神仙做法,就一定是 �(1)O(1) 的结论题——对于输入量较少的情况,可以尝试暴力打出前 1515 项的表,观察规律后就可能拿到满分!

  • 4.2 猜测答案

有些时候,问题的答案可能很有特点:对于大多数情况,答案是一样的。这时,骗分就该出手了。你需要做的,就是发掘出这个答案,然后直接输出。

有时,你需要运用第3章中学到的知识,先写出朴素算法,然后造一些数据,可能就会发现规律。

例如,本班月赛中有一道题:

炸毁计划【问题描述】皇军侵占了通往招远的黄金要道。为了保护渤海通道的安全,使得黄金能够顺利地运送到敌后战略总指挥地延安,
从而购买战需武器,所以我们要通过你的程序确定这条战略走廊是否安全。已知我们有N座小岛,只有使得每一个小岛都能与其他任意一个小岛联通才能保证走廊的安全。
每个小岛之间只能通过若干双向联通的桥保持联系,
已知有M座桥(Ai,Bi)表示第i座桥连接了Ai与Bi这两座城市。现在,敌人的炸药只能炸毁其中一座桥,
请问在仅仅炸毁这一座桥的情况下,能否保证所有岛屿安全,都能联通起来。现在给出Q个询问Ci,其中Ci表示桥梁编号,桥梁的编号按照输入顺序编号。
每个询问表示在仅仅炸毁第Ci座桥的情况下能否保证所有岛屿安全。如果可以,在输出文件当中,
对应输入顺序输出yes,否则输出no(输出为半角英文单词,区分大小写,
默认为小写,不含任何小写符号,每行输出一个空格,忽略文末空格)。【输入格式】第一行 三个整数N,M,Q,分别表示岛屿的个数,桥梁的个数和询问的个数。第二行到第M+1行 每行两个整数。第i+1行有两个整数Ai Bi表示这个桥梁的属性。第M+2行 有Q个整数Ci表示查询。【输出格式】Q行,表示查询结果。【样例】destroy.in    destroy.out2 1 1
1 2
1             no【样例范围】对于80%的数据,N≤100。对于100%的数据,N≤1000,N,Q≤M≤2000 。

你发现问题了吗?那么多座桥,炸一座就破坏岛屿的联系,可能性微乎其微(除非特别设计数据)。那么,我们的骗分策略就出来了:对于所有询问,输出yes.果然,此算法效果不错,得 8080 分。

尤其是输出 yes 或者 no 的题目,往往会有多测,但是如果考虑随机数据的情况,那么也能计算出大概的分值——除非构造数据如“一堆随机数据外加一个手造的 no”,那就没有办法了。

但是猜测答案依然重要。

  • 4.3 寻找规律

首先声明:本节讲的规律不是正当的算法规律,而是数据的特点。

某些题目会给你很多样例,你就可以观察他们的特点了。有时,数据中的某一个(或几个)数,能通过简单的关系直接算出答案。

只要你找到了规律,在很多情况下你都能得到可观的分数。

这样的题目大多出现在 NOI 或更高等级的比赛中,本人蒟蒻一个,就不举例了。传说某人去省选时专门琢磨数据的规律,结果有一题得了 3030 分。

  • 4.4 小数据杀手——打表

我认识一个人,他在某老师家上C语言家教,老师每讲一题,他都喊一句:“打表行吗?”

他真的是打表的忠实粉丝。表虽然不能乱打,但还是很有用的。

先看一个例子:

NOIP2003 栈题目描述 Description栈是计算机中经典的数据结构,简单的说,
栈就是限制在一端进行插入删除操作的线性表。栈有两种最重要的操作,即pop(从栈顶弹出一个元素)和push(将一个元素进栈)。栈的重要性不言自明,任何一门数据结构的课程都会介绍栈。
宁宁同学在复习栈的基本概念时,想到了一个书上没有讲过的问题,
而他自己无法给出答案,所以需要你的帮忙宁宁考虑的是这样一个问题:一个操作数序列,从1,2,一直到n(图示为1到3的情况),栈A的深度大于n。现在可以进行两种操作,1.将一个数,从操作数序列的头端移到栈的头端(对应数据结构栈的push操作)2. 将一个数,从栈的头端移到输出序列的尾端(对应数据结构栈的pop操作)使用这两种操作,由一个操作数序列就可以得到一系列的输出序列,
下图所示为由1 2 3生成序列2 3 1的过程。(原始状态如上图所示) 。你的程序将对给定的n,
计算并输出由操作数序列1,2,…,n经过操作可能得到的输出序列的总数。输入描述 Input Description输入文件只含一个整数n(1≤n≤18)输出描述 Output Description输出文件只有一行,即可能输出序列的总数目样例输入 Sample Input3
样例输出 Sample Output5

这题看似复杂,但数据范围太小,�≤18N≤18。所以,骗分程序就好写了:

int a[18]={1,2,5,14,42,132,429,1430,4862,16796,58786,208012,742900,2674440,9694845,35357670,129644790,477638700};scanf("%d",&n);printf("%d",ans[n-1]);

测试结果不言而喻,AC 了。

即使现在并不会有这么良心的题目,如果你对准 30%30% 的部分分打表,也是很可能拿到高分的!但是一定注意一些特别的情况,比如程序长度限制之类的。

学完这一章,你已基本掌握了骗分技巧。下面的内容涉及一点算法知识,难度有所增加。蒟蒻中的蒟蒻可以止步于此了。

第5章 做贪心的人

  • 5.1 贪心的算法

给你一堆纸币,让你挑一张,相信你一定会挑面值最大的。其实,这就是贪心算法。(*编者注:如果你有足够时间,可以列表格写动规对拍,测验哪个程序的正确率高)

贪心算法是个复杂的问题,但你不用管那么多。我们只关心骗分。给你一个问题,让你从一些东西中选出一些,你就可以使用贪心的方法,尽量挑好的。

举个例子:这是我们的市队选拔的一道题。

2. 有趣的问题

【问题描述】2013 年的NOIP 结束后, Smart 发现自己又被题目碾压了,心里非常地不爽,于是暗下决心疯狂地刷数学题目,
做到天昏地暗、废寝忘食,准备在今年的中考中大展身手。有一天,他在做题时发现了一个有趣的问题:给定n 个二元组(ai, bi) i),记函数:y=100*sigma(ai)/sigma(bi);将函数y 的值四舍五入取整。现将n 个二元组去掉其中的k 个计算一个新的y 值(也四舍五入取整),
均能满足:y <= z ,求出最小的z值。Smart 想让你帮他一起找出最小的z值。【输入格式】输入包含多组测试数据。每组测试数据第一行两个整数:n和k;第二行为n 个数:a1 a2 …… an;第三行为;n 个数: b1 b2 …… bn。输入数据当n、k 均为0 时结束。【输出格式】对于每组测试数据输出一行,即找出的最小的冘值。注意:为避免精度四舍五入出现误差,测试点保证每个函数值与最终结果的差值至少为0.001 。【样例】math.in3 1
5 0 1
5 1 6
4 2
1 2 7 95 6 7 90 0
math. out83
100
【数据范围】对于40% 的数据: n≤20;对于70% 的数据: n≤1000;对于100% 的数据: n≤10000,ai,bi 都在int 范围内。

这题让人望而生畏,但我们有贪心的手段。每个二元组的 �a 值是乘到答案中的,所以 �a 越大越好,那么只要选择出最小的 �k 个去掉即可。代码就不写了,因为这个涉及到下一章的内容:排序。

此代码得 2020 分。

  • 5.2 贪心地得分

我们已经学了很多骗分方法,但他们中的大多效率并不高,一般能骗 10∼2010∼20 分。这不能满足我们的贪心。

然而,我们可以合成骗分的程序。举个最简单的例子,有些含有无解情况的题目,它们同样有样例。我们可以写这个程序(*编者注:此为伪代码):

if(是样例) printf(样例);
else printf("-1");

这样也许能变 1010 分为 2020 分,甚至更多。

当然,合并骗分方法时要注意,不要重复骗同一种情况,或漏考虑一些情况。

大量能骗分的问题都能用此法,大家可以试试用新方法骗2.1中的例子 “文化之旅”。


作者的贪心讲解:

浅析贪心算法

若不能展望出最优的未来,那不如贪心的做个精致的利己主义者。

1.01.0 贪心的原理

贪心算法的核心就是一个字:贪。总是做出当前看来最优的选择,因此可知,贪心算法不从整体去考虑,它做出的选择也是局部最优选择,从而达到全局优化选择。我们只关注目前的利益,从而达到利益最大化,尽管贪心算法不一定能得到最优解,面对相当数量的一部分问题,我们是可以得到正确的答案的。

1.11.1 贪心的基本模式

从问题的某一个初始解,一步步推论,保证每一步都是最为优化的,出发逐步逼近给定的目标,以尽可能快的地求得更好的解。

当接下来的任意一步都无法达到最优时,贪心算法结束。

1.21.2 贪心算法的弊病

  1. 不能保证求得的最后解是最佳的;
  2. 不能用来求最大或最小解问题;
  3. 只能求满足某些约束条件的可行解的范围。

2.12.1 例题选讲

  • 注意,为了阅读效果,题目经过简写

1. 电池的寿命

有很多电池,使用寿命有所不同,需要两个电池。

比如:有的能使用 55 个小时,有的可能就只能使用 33 个小时,此时只能用 33 个小时,有一个电池剩下的电量无法使用,因为我们需要 22 枚电池同时使用。

现在已知电池的数量和电池能够使用的时间,请你找一种方案使得使用时间尽可能的长。

输入包含多组数据。每组数据包括两行。

第一行是一个整数 �(2≤�≤103)n(2≤n≤103) ,表示电池的数目,接下来一行是 �n 个正整数表示电池能使用的时间。

对每组数据输出一行,表示电池能使用的时间,保留到小数点后 11 位。

【输入样例】
2
3 5
3
3 3 5
【输出样例】
3.0
5.5

那么我们来分析一下两种情况。

  • 如果有一枚电池,电量比任何其他的电池之和都要大,很容易得到把其他电池用完就是最优解了。
  • 如果不存在这枚电池,性感理解一下,就是把最大的电池拿出来,将其他的电池一点一点消耗到和这枚电池一样,然后全部用掉。

综上所述:

  • 若存在大于总电量一半的电池,输出其余电池的总电量。
  • 反之输出全部的电量。

这样只需要 �(�)O(n) 的输入后就可以得出答案,仅需判断并求和即可轻松解决,思维难度一般,代码几乎没有实现难度。

有兴趣的同学可以打一下程序,但是洛谷上似乎没有这道题。

2. 【深基12.例1】部分背包问题

这道题也是一个很简单的贪心,其实,他所谓的背包问题,看似需要使用动态规划,但是,有一句话能够打破一切的谜团:

所有金币都可以随意分割。

那么显而易见的方法是:

  • 性价比排列,从最高性价比一直往下拿,拿到拿不动或者拿光了为止,是不是很简单?
  • 核心思想:能拿就全拿,拿不下能拿多少拿多少,贵的先拿。

3. 例题选讲

  • 货币使用问题:

    • 尽可能少用,那么我们就先拿面值最大的,依次往下走,最后拿光了即可。
  • 区间调度问题
    • 工作时间不能重叠,在可选工作中,每次都选取结束时间最早的作为选择,可以使工作量最大。

第6章 C++的福利

请 ������ 党们跳过本章,这不是你们的福利。请 Pascal 党们跳过本章,这不是你们的福利。

在 C++ 中,有一个好东西,名唤 STL ,被万千 Oier 们所崇拜,所喜爱。下面让我们走进 STL 。(*编者注:STL即为C++标准模板库,有很多内置函数和数据结构,十分方便)

  • 6.1 快速排序

快速排序是一个经典算法,也是 C++ 党的经典福利。他们有这样的代码:

#include<algorithm>//快速排序头文件
sort(a,a+n);//对一个下标从0开始存储,长度为n的数组升序排序

而事实上,一系列的 STL 内容如优先队列等也很重要,虽然常数大但是很好用啊。

  • 6.2 “如意金箍棒”

C++ 里有一种东西,叫 vector 容器(*编者注:类似于模拟数组,但是好用的多)。它好比如意金箍棒,可以随着元素的数量而改变大小。它其实就是数组,却比数组强得多。

下面看看它的几种操作:

#include<vector>//vector头文件
vector<int> V;//定义
V.push_back(x);//末尾增加一个元素x
V.pop_back();//末尾删除一个元素
V.size();//返回容器中的元素个数

它同样可以使用下标访问。(从 00 开始)

然后是编者编写的 STL 教程:

STL入门基础

今天我们介绍的 STL 内容都是较为常用的:

  • Stack 栈

  • Queue 队列

  • Deque 双向队列

  • vector 数组(?)

1.0 关于STL

STL是Standard Template Library的简称,中文名标准模板库,从根本上说,STL是一些“容器”的集合,这些“容器”有:

  • 容器

    • list
    • vector
    • set
    • map
    • stack
    • queue
    • deque
    • pair

这里借用一个 OI-Wiki 的图片:

同时,容器分类也很重要:

STL也是算法和其他一些组件的集合。其的目的是标准化组件,这样就不用手写(简单多了),可以使用现成的组件,今天我们只讲四个。

STL是C++的一部分,被组织为下面的 1313 个头文件:

#include<algorithm>
#include<deque>
#include<functional>
#include<iterator>
#include<vector>
#include<list>
#include<map>
#include<memory.h>
#include<numeric>
#include<queue>
#include<set>
#include<stack>
#include<utility>

2.1 stack及其基本操作

栈是 OI 中常用的一种线性数据结构,请不要与程序运行时的系统栈/栈空间所混淆。

栈的基本性质是后进先出的原则进行的,因此栈通常被称为是后进先出(last in first out)表,简称 LIFO 表。

当我们要定义一个STL中的int类型栈:

stack<int>st;

这个时候我们希望能够压入一个数字:

st.push(1);

当我们希望弹出:

st.pop();

这一过程简洁明了。

让我们浏览一下他的功能:

//栈的内置函数:
st.empty()//如果栈为空返回true,否则返回false
st.size()//返回栈中元素的个数
st.pop()//删除栈顶元素但不返回其值
st.top()//返回栈顶的元素,但不删除该元素
st.push(num)//在栈顶压入新元素 ,参数num为要压入的元素

这时候,我们来看一下例题:

P1739 表达式括号匹配

基本思路如下:

  • 输入,如果输入的是右括号而栈顶是左括号,那就弹出
  • 输入,如果输入的是左括号而栈顶不为有括号,压入
  • 输入,如果输入是左括号而栈顶是有括号,输出NO
  • 全部运行完毕,若栈空,输出YES 反之输出NO

运用 STL 中的栈实现如下:

#include<bits/stdc++.h>
using namespace std;
stack<char>a;
char s;
int l;
int main(){while(cin>>s&&s!='@'){if(a.empty()&&s==')'){cout<<"NO"<<endl;return 0;}if(s=='(')a.push(s);if(s==')')a.pop();}if(a.empty())cout<<"YES"<<endl;else cout<<"NO"<<endl;return 0;
}

与此同时:

== 、!= 、< 、 <= 、 > 、 >= 可以按照字典序比较两个 stack 的值

这里也稍微讲一下手写:

  • 一个变量 ���top 作为栈顶标签。
  • pop 就 ���=���−1top=top−1
  • 反之 �����=���,���=���+1sttop​=num,top=top+1

其余可以自己思考如何做,在之后的文章不会再讨论手写做法,可以自行思考。

2.2 queue 队列及其基本操作

队列是一个先进先出的数据结构,这在 BFS 的时候会用到,BFS 的基本原理就是:

  • 将第一个节点放入队列
  • 只要队不空:
    • 将这个节点标记为已经走过并出队
    • 将与这个节点相关的所有节点都入队
    • 继续进入队头节点进行下一轮搜索

咳咳咳,跑题了,我们再继续讲队列。

STL当中的队列操作一栏:

//队列的基本操作:
q.empty()// 如果队列为空返回true,否则返回false
q.size() // 返回队列中元素的个数
q.pop()  //删除队列首元素但不返回其值
q.front()  // 返回队首元素的值,但不删除该元素
q.push(X) //在队尾压入新元素 ,X为要压入的元素
q.back() //返回队列尾元素的值,但不删除该元素

请注意当中的某些与栈不同的功能(说实话我觉得栈可以手写,队列建议还是用STL来实现)。

我们来看一下例题:

P1996 约瑟夫问题

思路:我们使用一个队列 �q 进行模拟,在读取总人数和出局数字后,把这些人一个个地压入队列尾部。

这样模拟基本没有思维难度,只要熟练使用 STL ,码量大大减少,可以有效提升程序的可读性。

不过,这里要说的是,STL的常数巨大,如果考场上比较卡常的题目建议手写,同时建议将不同的代码写成函数,调用的时候调试比较方便。

2.3 deque 双向队列

deque是双向开口的连续线性空间(动态将多个连续空间通过指针数组接合在一起),随时可以增加一段新的空间,所以数据想vector里面的分配,复制,释放操作不会发生。deque头尾两端分别做插入和删除操作都是常数时间

这里可以看到相关内置知识,我们只需要了解:

  • 这个双开口的数据结构可以帮助我们调控普通队列所不能调控的性质

2.4 vector 疑似数组

借用一下OI-Wiki的内容:

// 1. 创建空vector; 常数复杂度
vector<int> v0;
// 1+. 这句代码可以使得向vector中插入前3个元素时,保证常数时间复杂度
v0.reserve(3);
// 2. 创建一个初始空间为3的vector,其元素的默认值是0; 线性复杂度
vector<int> v1(3);
// 3. 创建一个初始空间为3的vector,其元素的默认值是2; 线性复杂度
vector<int> v2(3, 2);
// 4. 创建一个初始空间为3的vector,其元素的默认值是1,
// 并且使用v2的空间配置器; 线性复杂度
vector<int> v3(3, 1, v2.get_allocator());
// 5. 创建一个v2的拷贝vector v4, 其内容元素和v2一样; 线性复杂度
vector<int> v4(v2);
// 6. 创建一个v4的拷贝vector v5,其内容是{v4[1], v4[2]}; 线性复杂度
vector<int> v5(v4.begin() + 1, v4.begin() + 3);
// 7. 移动v2到新创建的vector v6,不发生拷贝; 常数复杂度; 需要 C++11
vector<int> v6(std::move(v2));  // 或者 v6 = std::move(v2);

我们使用 Vector 的原因,很大程度是因为他“能屈能伸”,也就是动态分配内存

这样很大程度上放置了对于内存估计不准的情况,而且大量的内置函数也比一个简简单单的数组要亲民多了。

大量的函数运用都可以简化代码,具体如下:

序列式容器 - OI Wiki

3.1 尾声

STL 的好处很多,动态空间和相关的函数都是考场切题的利器。但是,一定要注意,STL只是容器,不是万用的解题办法,与其想着使用STL来骗分(?)还不如认真复习算法,任何的优化都是基于一个坚实的基础上的!

第7章 “宁为玉碎,不为瓦全”

编者提醒:此篇内容仅供娱乐,切勿使用!!

至此,我已介绍完了我所知的骗分方法。如果上面的方法都不奏效,我也无能为力。但是,我还有最后一招——

有句古话说:“宁为玉碎,不为瓦全”。我们蒟蒻也应有这样的精神。骗不到分,就报复一下,卡评测以泄愤吧!

卡评测主要有两种方法:一是死循环,故意超时;二是进入终端,卡住编译器。

先介绍下第一种。代码很简单,请看:

while(1);

就是这短短一句话,就能卡住评测机长达 10�∼20�10s∼20s ,甚至更多!对于测试点多、时限长的题目,这是个不错的方法。

第二种方法也很简单,但危害性较大,建议不要在重要比赛中使用,否则可能让你追悔莫及。它就是:

#include<con>
//(windows系统中使用)
//或
#include</dev/console>
//(Linux系统中使用)

它非常强大,可以卡住评测系统,使其永远停止不了编译你的程序。唯一的解除方法是,工作人员强行关机,重启,重测。当然,我不保证他们不会气愤地把你的成绩变成 00 分。请慎用此方法。

第8章 实战演练

下面我们来做一些习题,练习骗分技巧。

我们来一起分析一下NOIP2013普及组的试题吧。

记数问题(NOIP普及组2013第一题)

(count.cpp/c/pas)

描述
试计算在区间 1 到 n 的所有整数中,数字 x(0 ≤ x ≤ 9)共出现了多少次?
例如,在 1 到 11 中,即在 1、2、3、4、5、6、7、8、9、10、11 中,数字 1 出现了 4 次。【输入】输入文件名为 count.in。输入共 1 行,包含 2 个整数 n、x,之间用一个空格隔开【输出】输出文件名为 count.out。输出共 1 行,包含一个整数,表示 x 出现的次数。【输入输出样例】count.in count.out11 1 4
限制
每个测试点1s。【数据说明】对于 100%的数据,1≤ n ≤ 1,000,000,0 ≤ x ≤ 9。

表达式求值(noip2013普及组第二题)

(expr.cpp/c/pas)

描述
给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值。【输入】输入文件为 expr.in。输入仅有一行,为需要你计算的表达式,表达式中只包含数字、加法运算符“+”和乘 ,
且没有括号,所有参与运算的数字均为 0 到 231-1 之间的整数。输入数据保 法运算符“\*”证这一行只有 0~ 9、+、\*这 12 种字符。【输出】输出文件名为 expr.out。输出只有一行,包含一个整数,表示这个表达式的值。注意:当答案长度多于 4 位时,请只输出最后 4 位,前导 0 不输出。【输入输出样例 1】expr.in expr.out1+1\*3+4 8【输入输出样例 2】expr.in expr.out1+1234567890\*1 7891【输入输出样例 3】expr.in expr.out1+1000000003\*1 4【输入输出样例说明】样例 1 计算的结果为 8,直接输出 8。样例 2 计算的结果为 1234567891,输出后 4 位,即 7891。样例 3 计算的结果为 1000000004,输出后 4 位,即 4。【数据范围】对于 30%的数据,0≤表达式中加法运算符和乘法运算符的总数≤100;对于 80%的数据,0≤表达式中加法运算符和乘法运算符的总数≤1000;对于 100%的数据,0≤表达式中加法运算符和乘法运算符的总数≤100000。

小朋友的数字(noip2013普及组第三题)

(number.cpp/c/pas)

描述
有 n 个小朋友排成一列。每个小朋友手上都有一个数字,这个数字可正可负。
规定每个小朋友的特征值等于排在他前面(包括他本人)的小朋友中连续若干个(最少有一个)
小朋友手上的数字之和的最大值。 作为这些小朋友的老师,你需要给每个小朋友一个分数,
分数是这样规定的:第一个小朋友的分数是他的特征值,
其它小朋友的分数为排在他前面的所有小朋友中(不包括他本人),小朋友分数加上其特征值的最大值。请计算所有小朋友分数的最大值,输出时保持最大值的符号,将其绝对值对 p 取模后输出。格式
【输入】输入文件为 number.in。第一行包含两个正整数 n、p,之间用一个空格隔开。第二行包含 n 个数,每两个整数之间用一个空格隔开,表示每个小朋友手上的数字。【输出】输出文件名为 number.out。输出只有一行,包含一个整数,表示最大分数对 p 取模的结果。【输入输出样例 1】number.in number.out5 997 211 2 3 4 5【输入输出样例说明】小朋友的特征值分别为 1、3、6、10、15,分数分别为 1、2、5、11、21,最大值 21对 997 的模是 21。【输入输出样例 2】第2/4页number.in number.out5 7 -1
-1 -1 -1 -1 -1【输入输出样例说明】小朋友的特征值分别为-1、-1、-1、-1、-1,分数分别为-1、-2、-2、-2、-2,最大值 -1 对 7 的模为-1,输出-1。【数据范围】对于 50%的数据,1 ≤ n ≤ 1,000,1 ≤ p ≤ 1,000所有数字的绝对值不超过 1000;99 对于 100%的数据,1 ≤ n ≤ 1,000,000, 1≤ p ≤ 10, 其他数字的绝对值均不超过 10。

车站分级(NOIP普及组2013第四题)

(level.cpp/c/pas)

描述
一条单向的铁路线上,依次有编号为 1, 2, ..., n 的 n 个火车站。每个火车站都有一个级别,最低为 1 级。
现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 x,
则始发站、终点站之间所有级别大于等于火车站 x 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)例如,下表是 5 趟车次的运行情况。其中,前 4 趟车次均满足要求,
而第 5 趟车次由于停靠了 3 号火车站(2 级)却未停靠途经的 6 号火车站(亦为 2 级)而不满足要求。现有 m 趟车次的运行情况(全部满足要求) ,试推算这 n 个火车站至少分为几个不同的 级别。【输入】输入文件为 level.in。第一行包含 2 个正整数 n, m,用一个空格隔开。第 i + 1 行(1 ≤ i ≤ m)中,首先是一个正整数 si(2 ≤ si ≤ n),表示第 i 趟车次有 si 个停靠站;接下来有 si 个正整数,表示所有停靠站的编号,从小到大排列。
每两个数之间用一个 空格隔开。输入保证所有的车次都满足要求。【输出】输出文件为 level.out。输出只有一行,包含一个正整数,即 n 个火车站最少划分的级别数。第3/4页【输入输出样例】【数据范围】对于 20%的数据,1 ≤ n, m ≤ 10; 对于 50%的数据,1 ≤ n, m ≤ 100; 对于 100%的数据,1 ≤ n, m ≤ 1000。第4/4页

第 11 题,太弱了,不用骗,得 100100 分。

第 22 题,数据很大,但是可以直接输入一个数,输出它 mod104mod104 的值。得 1010 分。

第3题,是一道非常基础的 DP (*编者注:即为动态规划),但对于不知 DP 为何物的蒟蒻来说,就使用暴力算法(即 DFS )。得 2020 分。

第 44 题,我们可以寻找一下数据的规律,你会发现,在所有样例中, �M 值即为答案。所以直接输出 �M ,得 1010 分。

这样下来,一共得 140140 分,比一等分数线还高 2020 分!你的信心一定会得到鼓舞的。这就是骗分的神奇。

第9章 结语

骗分是蒟蒻的有力武器,可以在比赛中骗得大量分数。相信大家在这本书中收获了很多,希望本书能帮助你多得一些分。

但是,最后我还是要说一句:

“ 不骗分,是骗分的最高境界。”“ 不骗分,是骗分的最高境界。”

Thank for reading.Thank for reading.

骗分导论-第8修订测试版相关推荐

  1. 新版骗分导论 - 第7 版

    新版骗分导论第7版\quad\tiny\texttt{第 7 版}第 7 版 修订--Jerrycyx(CSDN,洛谷) 洛谷博客查看 前言 这是我在倒腾洛谷水贴时偶然翻到的一篇博客,自认为写得很好, ...

  2. 大神cyd的骗分导论

    新 版 骗 分 导 论 THE NEW GUIDE OF CHEATING IN INFORMATICS OLYMPIAD 蒟 蒻 的 宝 书 目录 第1章 绪论 第2章 从无解出发 2.1 无解情况 ...

  3. 新版骗分导论(最少骗到省级三等奖)

    第1章 绪论 在Oier中,有一句话广为流传: t{任何蒟蒻必须经过大量的刷题练习才能成为大牛乃至于神牛}任何蒟蒻必须经过大量的刷题练习才能成为大牛乃至于神牛≺ 这就是著名的lzn定理.然而,我们这些 ...

  4. “ 骗 ”分指南——对于蓝桥你不得不知的应试技巧(文末发送礼包)

    文章目录 前言 合理使用考试外的电脑工具--简称外挂 计算器 excel 常用的代码模板 辗转相除法求最大公约数 闰年 素数 排序--sort 函数库 暴力 万能钥匙--DFS 打表 最后 前言 蓝桥 ...

  5. 新 版 骗 分 导 论

    新 版 骗 分 导 论{蒟 蒻 的 宝 书} {目 录} 第1章 绪论 第2章 从无解出发 ↪ 2.1 无解情况 ↪ 2.2 样例--白送的分数 第3章 "艰苦朴素永不忘" ↪ 3 ...

  6. 等于x分之a的平方的导数_清华学霸丨手把手教你导数大题如何骗分(文理通用),家长为孩子收...

    文科和理科导数题差异不明显(大概就是理科有三题,文科考前两题这种难度差异),因此文科的同学也可以阅读此文章,对于导数过于难以理解的知识,跳过即可. Ⅰ.在解题之前 有几件事大家需要明白: 1.导数题作 ...

  7. CCF- CSP 202012-5星际旅行 80分骗分题解

    CCF- CSP 202012-5星际旅行 80分骗分题解 题目链接:202012-5星际旅行 思路: 题目条理比较清晰,对于这种第五题,我们在考试时尽可能拿更多的分(第5题拿满分我不敢奢望hhhh) ...

  8. 202112-3登机牌条码(40分骗分)

    题目信息: 骗分解读: 这个题主要是让我们计算编码后的码字序列,码字序列由两个部分组成,编码部分和校验部分.由于校验码实在是太麻烦了,对于骗分来说,我们可以不去看,所以我们只需要去考虑没有校验码的情况 ...

  9. 一条咸鱼试图骗分的故事

    乌龟棋tortoise --一道让人下意识开始骗分的题** 一个来自万年吃饱了撑的.没事闲得慌的.遇事绝对只会找人帮忙的.一旦自己解决问题绝对会出错的小明的故事_(¦3)∠)_ 小明过生日的时候,爸爸 ...

最新文章

  1. 我是这么使用axios的
  2. 几种替代MATLAB的工具,堪称完美!
  3. 教你如何不显示excel中#N/A
  4. java 以什么开头_判断字符串以什么开头
  5. jquery最快速入门文档
  6. re.containerbase.startinternal 子容器启动失败_Python项目容器化实践(二) Docker Machine和Docker Swarm...
  7. 会员编号生成规则_单据编号规则浅析
  8. Python里面search()和match()的区别?
  9. 【Docker】 安装 Docker
  10. SFB 项目经验-49-耗时5天修复某上市企业2000人邮箱数据库实录(五一巨献)
  11. 信息熵--决策树的建立算法ID3中使用
  12. JDK8与JDK7并存时切换的问题
  13. Scheme 语言 编程思想上
  14. HAL库开发—基于stm32的智能小家电
  15. 微信公众号基于 baidu API接口的图像识别
  16. C++的字符串输入方式
  17. 百度apollo源码学习(二)apollo中的工厂模式
  18. ANGULAR之中的$STATE、 $WATCH、 $SCOPE、 $ROOTSCOPE 分别是什么?
  19. java正则表达式下划线_用于标识符的Java正则表达式(字母,数字和下划线)
  20. CPA广告 CPS广告 CPC广告 CPM解析

热门文章

  1. 传阿里巴巴集团推迟上市至2015年底
  2. QE Phonon 用户指南解读
  3. 关联数据库中多张表_关联数据
  4. 第一次的博客 告诫自己及目标
  5. Excel快速美化图表,瞬间高大上不再单调,老板都喜欢的图表
  6. VS单步调试UE4打包的exe游戏
  7. linux下串口gps应用
  8. 链接文件转blob格式下载(浏览器)
  9. Q for Mortals2笔记 -- 造型和枚举
  10. Starting MySQL.. ERROR! The server quit without updating PID file (/usr/local/mysql/data/vm10-0-0-1