第1章 绪论
在Oier中,有一句话广为流传:
t{任何蒟蒻必须经过大量的刷题练习才能成为大牛乃至于神牛}任何蒟蒻必须经过大量的刷题练习才能成为大牛乃至于神牛≺
这就是著名的lzn定理。然而,我们这些蒟蒻们,没有经过那么多历练,却要和大牛们同场竞技,我们该怎么以弱胜强呢?答案就是: 骗分
那么,骗分是什么呢?骗分就是用简单的程序(比标准算法简单很多,保证蒟蒻能轻松搞定的程序),尽可能多得骗取分数。
让我们走进这本 《新版骗分导论》 ,来学习骗分的技巧,来挑战神牛吧!
第2章 从无解出发
2.1 无解情况
在很多题目中都有这句话:
“若无解,请输出-1.”“若无解,请输出-1.”≺
printf("-1");
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
输入样例1
2 2 1 1 2
1 2
0 1
1 0
1 2 10
输入样例2
2 2 1 1 2
1 2
0 1
0 0
1 2 10
样例输出 Sample Output
输出样例1
-1
输出样例2
10
数据范围及提示 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”);
2.2 样例——白送的分数
每道题目的后面,都有一组“样例输入”和“样例输出”。它们的价值极大,不仅能初步帮你检验程序的对错(特别坑的样例除外),而且,如果你不会做这道题(这种情况蒟蒻们已经司空见惯了),你就可以直接输出样例!
例如美国的USACO,它的题目有一个规则,就是第一组数据必须是样例。那么,只要你输出所有的样例,你就能得到100分(满分1000)!这是相当可观的分数了。
现在,你已经掌握了最基础的骗分技巧。只要你会基本的输入输出语句,你就能实现这些骗分方法。那么,如果你有一定的基础,请看下一章——我将教你怎样用简单方法骗取部分分数。
第3章 “艰苦朴素永不忘”
本章的标题来源于《学习雷锋好榜样》的一句歌词,但我不是想教导你们学习雷锋精神,而是学习骗分!
看到“朴素”两个字了吗?它们代表了一类算法,主要有模拟和DFS。下面我就来介绍它们在骗分中的应用。
3.1 模拟
所谓模拟,就是用计算机程序来模拟实际的事件。例如NOIP2012的“寻宝”,就是写一个程序来模拟小明上藏宝塔的动作。
较繁的模拟就不叫骗分了,我这里也不讨论这个问题。
模拟主要可以应用在骗高级数据结构题上的分,例如线段树。下面举一个例子来说明一下。
排 队(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 6
3
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);
}
3.2 万能钥匙——DFS
DFS是图论中的重要算法,但我们看来,图论神马的都是浮云,关键就是如何骗分。下面引出本书的第2条定理:
\≻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 Input
70 3
71 100
69 1
1 2
样例输出 Sample Output
3
数据范围及提示 Data Size & Hint
对于30%的数据,M<=10;
对于全部的数据,M<=100。
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);
}
第4章 骗分的关键——猜想
4.1 听天由命
如果你觉得你的人品很好,可以试试这一招——输出随机数。
先看一下代码:
#include
#include
//以上两个头文件必须加
srand(time(NULL));
//输出随机数前执行此语句
printf(“%d”,rand()%X);
//输出一个0~X-1的随机整数。
这种方法适用于输出一个整数(或判断是否)的题目中,答案的范围越小越好。让老天决定你的得分吧。
据说,在NOIP2013中,有人最后一题不会,愤然打了个随机数,结果得了70分啊!!
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.out
2 1 1
1 2
1 no
【样例范围】
对于80%的数据,N≤100。
对于100%的数据,N≤1000,N,Q≤M≤2000 。
你发现问题了吗?那么多座桥,炸一座就破坏岛屿的联系,可能性微乎其微(除非特别设计数据)。那么,我们的骗分策略就出来了:对于所有询问,输出yes.果然,此算法效果不错,得80分。
4.3 小数据杀手——打表
我认识一个人,他在某老师家上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 Input
3
样例输出 Sample Output
5
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]);
最后,编者告诉大家一个秘密:在OI考试中最后难的题数据最后的几个,他们往往不会创造新的数据,此时就可以直接输出最后几个数据了。