在做算法类竞赛的题目的时候,容易想到一个朴素的能保证完全正确的算法,但是会超时。而高效的算法又不能保证完全写对。这时候可以自己写一个朴素的算法、一个数据生成程序和一个文件比较程序进行验证高效算法的正确性。
在Windows下,fc命令提供了比较文件的功能,虽然批处理不如Linux下的bash等强大,但也足以写个自动比较的程序了。
下面以求逆序对为例。题目类似于POJ3067,排序后求逆序对。数据量N=300000。容易想到类似于冒泡排序的枚举逆序对,先写出来。命名为standard.pas
const
MAXN = 500000;
type
car = record x,v:longint end;
var
a: array[0..MAXN] of car;
n : longint;
i, j : longint;
sum : longint;
procedure swap(var a, b:car);
var
temp:car;
begin
temp := a;
a := b;
b := temp;
end;
begin
readln(n);
for i:=1 to n do
readln(a[i].x , a[i].v);
for i:=1 to n-1 do
for j:=i+1 to n do
if a[i].x > a[j].x then
swap(a[i], a[j]);
for i:=1 to n-1 do
for j:=i+1 to n do
if a[i].v > a[j].v then
inc(sum);
writeln(sum);
end.
用归并排序等高级的算法实现NLogN的程序,才能保证过掉这题。命名为题目要求的overtaking.pas。程序是用了归并排序排好序,再把这个归并排序复制一下改一下变成求逆序对的统计过程。
const
MAXN = 500000;
type
car = record x,v:longint end;
var
temp, a: array[0..MAXN] of car;
n : longint;
i : longint;
sum : int64;
procedure swap(var a, b:car);
var
temp:car;
begin
temp := a;
a := b;
b := temp;
end;
procedure qsort(l, r:longint);
var
i, j, m:longint;
begin
i:=l;j:=r;m:=a[(l+r)div 2].x;
repeat
while (a[i].x < m) do inc(i);
while (a[j].x > m) do dec(j);
if i <= j then
begin
swap(a[i],a[j]);
inc(i);
dec(j);
end;
until i > j;
if l < j then
qsort(l, j);
if i < r then
qsort(i, r);
end;
procedure merge(left, mid, right :longint);
var
i, j, p, k:longint;
begin
k := left;
i := left;
j := mid + 1;
while (i <= mid) and (j <= right) do
begin
if a[i].x <= a[j].x then
begin
temp[k] := a[i];
inc(i);
inc(k);
end
else begin
temp[k] := a[j];
inc(j);
inc(k);
end;
end;
for p := i to mid do
begin
temp[k] := a[p];
inc(k);
end;
for p := j to right do
begin
temp[k] := a[p];
inc(k);
end;
for i:= left to right do
a[i] := temp[i];
end;
procedure merge_sort(left, right:longint);
var
mid : longint;
begin
if left >= right then
exit;
mid := (left + right) shr 1;
merge_sort(left, mid);
merge_sort(mid+1, right);
merge(left, mid ,right);
end;
procedure merge2(left, mid, right :longint);
var
i, j, p, k:longint;
begin
k := left;
i := left;
j := mid + 1;
while (i <= mid) and (j <= right) do
begin
if a[i].v <= a[j].v then
begin
temp[k] := a[i];
inc(i);
inc(k);
end
else begin
inc(sum, mid - i + 1);
temp[k] := a[j];
inc(j);
inc(k);
end;
end;
for p := i to mid do
begin
temp[k] := a[p];
inc(k);
end;
for p := j to right do
begin
temp[k] := a[p];
inc(k);
end;
for i:= left to right do
a[i] := temp[i];
end;
procedure merge_sort2(left, right:longint);
var
mid : longint;
begin
if left >= right then
exit;
mid := (left + right) shr 1;
merge_sort2(left, mid);
merge_sort2(mid+1, right);
merge2(left, mid ,right);
end;
procedure select_sort;
var
i, j:longint;
begin
for i:=1 to n-1 do
for j:=i+1 to n do
if a[i].x > a[j].x then
swap(a[i], a[j]);
end;
begin
readln(n);
for i:=1 to n do
begin
readln(a[i].x, a[i].v);
end;
merge_sort(1, n);
merge_sort2(1, n);
writeln(sum);
end.
然后写一个数据生成器。此题造数据很简单,生成随机数就行了。
const
INF = 199303170;
MAXN = 3000;
var
i, n:longint;
begin
randomize;
n := random(MAXN);
writeln(n);
for i:=1 to n do
writeln(random(INF),'',random(INF));
end.
然后写一个批处理对拍,自动将这三个程序的输入输出从屏幕、键盘重定向到文件,然后比较standard.exe与overtaking生成的数据。
:loop
make.exe > data.txt
standard.exe < data.txt > std.txt
overtaking.exe < data.txt > ans.txt
fc std.txt ans.txt
if errorlevel 1 goto end
goto loop
:end
命令行界面会不断提示找不到文件差异,然后就可以放心提交了,当然前提是你朴素的算法写对了。