【Pascal】再谈对拍程序的应用

绪言

  • 信息学竞赛中,高手往往能够有很高的正确率,这一方面取决于他们超前的代码能力(传说陈立杰大神在WC2013中,当场怒A平面图神题,代码500+行)。膜拜一下。

  • 当然了,除了超前的代码能力之外,他们还非常熟悉一种对拍技术,对他们(包括我)来说,对拍已经是家常便饭了。

  • 所以要成为高手的话,你也必须学会对拍。

  • 曾经已经写了一个简单的C++版对拍在,但没有独立引出。今天浅谈一下。本文使用Pascal语言,有些怀旧。

对拍的思想

当你费劲千辛万苦,想出了一个贪心或者动态规划的算法,写完了,样例过了,然后自己出出数据又觉得很麻烦,而且找错误的效率的太低。这个时候或许你的心里是这么想的,假如你有一个自动生成数据的程序,假如你还有一个标准的程序(不管其时间效率是否优秀),假如你还有一个能够控制对拍自动进行的程序,那么一切的一切似乎就很美好。但是,现实是残酷的,这些东西是不会凭空产生
的,都是要你自己写的。

总结起来,4个要点:
1、你自己的程序 2、标准程序 3、生成数据程序
4、控制上面三个程序自动进行对拍的程序

于是乎

原来你只要写一个程序,现在你要写4个程序,这对你的代码能力提出了更高的要求。原来你只要写对一个程序,而现在你要写对4个程序。。。瞬间感觉鸭梨好大o((⊙﹏⊙))o.。

对拍程序(第4个程序)介绍

@echo off //这个可有可无,写了的话就能不让每一句运行的语句显示在屏幕上
:loop // :loop 表示定义了一个循环
make.exe > std.in // make是生成数据的程序,大于符号表示把make的输出结果放到std.in文件里面,std.in存储的是我们自己产生的输入数据
std.exe < std.in > std.out // 小于符号表示将std.in里面的数据输入到std.exe这个程序里,大于表示将结果导出到std.out,std.out是标准程序产生的结果
my.exe < std.in > my.out // my.exe是你自己写的程序,my.out是你的程序的输出
fc my.out std.out // fc 表示比较两个输出文件,如果一样,errorlevel就是0,否则的话,就是1
if errorlevel 1 pause // 表示如果是1的话,就停止程序,因为我们需要去看是哪组输入数据导致了不一样的结果
goto loop //继续循环

无注释版:

@echo off
:loop
make.exe > std.in
std.exe < std.in > std.out
my.exe < std.in > my.out
fc my.out std.out
if errorlevel 1 pause
goto loop

注意,这些东西要写进一个*.bat的文件里面,*.bat是windows系统下的批处理程序,上面写语句都是批处理语句。

那里有一个可以计时的bat程序,在此不再赘述。

例题

拿一道比较经典的。
NOIP2001 数的划分
CodeVS-1039 一本通-1304 计蒜客-T2155 LibreOJ-10018

题目

  • 题目描述 Description
    将整数n分成k份,且每份不能为空,任意两种划分方案不能相同(不考虑顺序)。
    例如:n=7,k=3,下面三种划分方案被认为是相同的。
    1 1 5
    1 5 1
    5 1 1
    问有多少种不同的分法。
  • 输入描述 Input Description
    输入:n,k (6<n<=200,2<=k<=6)
  • 输出描述 Output Description
    输出:一个整数,即不同的分法。
  • 样例输入 Sample Input
    7 3
  • 样例输出 Sample Output
    4

标算

这是一题非常经典的动态规划题目,如果你想出了定义状态f[i][j]表示把i这个数分解成为j个数的方法总数(注意为了防止重复,同时也要求所有数是非递减的)。

那么 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + f [ i − j ] [ j ] f[i][j]=f[i-1][j-1]+f[i-j][j] f[i][j]=f[i1][j1]+f[ij][j]

如果从f[i-1][j-1]推得f[i][j],就是f[i][j]里面肯定存在把1分解出来的情况,如果从f[i-j][j]推得,就表示f[i][j]里面肯定没有1的情况,所以f[i][j]的方法总数就包含在这两个子状态的和里面了。

动归程序(第1个程序)

这个可以命名为my.pas

var f:array[0..200,0..6] of longint; i,j,n,k:longint;
begin
	read(n,k);
	fillchar(f,sizeof(f),0);
	f[0][0]:=1;
	for i:=1 to n do
	for j:=1 to k do begin
		f[i][j]:=f[i-1][j-1];
		if i>=j then f[i][j]:=f[i][j]+f[i-j][j];
	end;
	writeln(f[n][k]);
end.

搜索程序(第2个程序)

现在我们要用对拍来验证一个这个程序的正确性了,我们先写一个搜索程序。这个程序可以命名为std.pas。

(此处省略几行)

生成数据的程序(第3个程序)

接下来,我们还需要一个生成数据的程序,这个程序一定要根据题目的要求来生
成数据,一般数据的范围不要太大,这样方便你发现对拍不一致以后迅速找到错
误。程序名可以为make.pas。

程序如下:

var n,k:longint;
begin
	randomize(); //这个表示置随机种子,只有写了这句话,才能每次随机产生不同的数。
	n:=random(50)+1; //random(50)表示随机产生0-49之间的整数
	k:=random(6)+1;
	writeln(n,' ',k); //输出n和k的值,因为这个要作为输入
end.

最后一步

最后,把my.pas编译生成my.exestd.pas编译生成std.exemake.pas编译生成make.exe,把对拍程序命名为pai.bat

把这4个程序(my.exestd.exemake.exepai.bat放在同一个路径下),然后点击pai.bat就开始对拍了,如果发现不同的结果,对拍程序就会中止,此时,你就可以查看std.in里面那组数组了。

总结

  • 对拍是一种技术。

  • 对拍是一种能力。

  • 对拍不是万能的,但是对拍确实用处很大。

  • 熟能生巧,才能掌握好对拍技术,从此你离大牛又进了一步。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值