算法:DP+压缩
本题难的不是DP方程,而是难在了压缩路径上面,首先,DP方程是很好想出的,每次跳的范围只能在[S,T]之间,所以到某一个位置时,只要从前面的[S,T]这个位置找一个最小值转移过来就行了,在判断一下这个点有没有石子,有就+1,否则直接转移。
但是一看数据范围,10^9,好吧,裸方法可以去使了……
仔细观察题目数据不难看出虽然总长度是10^9,但是石子最多却只有100个,这说明了什么?说明在这条路上石子出现是很稀疏的,这就可以用到了压缩路径的办法。
经过一系列的数学证明可以得出,当两个石子之间的距离大于100之后,两个石子之间的空隙跟空隙为100的最优值没有差别(因为S和T的范围也很小),这样就把一个极大的空隙变成了一个在100以内的空隙。也就是相当于我们把独木桥变短了。
本题难的不是DP方程,而是难在了压缩路径上面,首先,DP方程是很好想出的,每次跳的范围只能在[S,T]之间,所以到某一个位置时,只要从前面的[S,T]这个位置找一个最小值转移过来就行了,在判断一下这个点有没有石子,有就+1,否则直接转移。
但是一看数据范围,10^9,好吧,裸方法可以去使了……
仔细观察题目数据不难看出虽然总长度是10^9,但是石子最多却只有100个,这说明了什么?说明在这条路上石子出现是很稀疏的,这就可以用到了压缩路径的办法。
经过一系列的数学证明可以得出,当两个石子之间的距离大于100之后,两个石子之间的空隙跟空隙为100的最优值没有差别(因为S和T的范围也很小),这样就把一个极大的空隙变成了一个在100以内的空隙。也就是相当于我们把独木桥变短了。
最后还有一种情况就是S=T时,这时每次能跳的情况只有一种,就从起点累计一遍,mod S=0就把ans+1,输出ans。
program P1059;
const
maxn=100;
maxm=10000;
var
l,s,t,m:longint;
stone:array [0..maxn] of longint;{stone[i]表示第i颗石子的位置。}
a,f:array [0..maxm] of longint;{a[i]表示压缩之后的i位置上有没有石子,有就是1,没有就是0。f[i]表示走到当前这个点最少踩到多少个石子。}
procedure doit;
var
tans,i:longint;
begin
tans:=0;
for i:=1 to m do if stone[i] mod s=0 then inc(tans);
writeln(tans);
halt;
end;
procedure sort;{把石子的位置排一下序,因为石子数小于等于100,因此选择排序就可以了。}
var
i,j,temp:longint;
begin
for i:=1 to m-1 do
begin
for j:=i+1 to m do
begin
if stone[i]>stone[j] then
begin
temp:=stone[i];
stone[i]:=stone[j];
stone[j]:=temp;
end;
end;
end;
end;
procedure init;
var
i:longint;
begin
readln(l);
readln(s,t,m);
for i:=1 to m do read(stone[i]);
if s=t then doit;
end;
procedure main_1;
var
i,t,last:longint;
begin
last:=0;
sort;
for i:=1 to m do
begin
if stone[i]-stone[i-1]>100 then t:=100 else t:=stone[i]-stone[i-1];{如果小于100,就不能再压缩路径了。}
a[last+t]:=1;{last+t即是当前石子的位置。}
inc(last,t);{同时last也进行更新。}
end;
if l-a[m]>100 then t:=100 else t:=l-a[m];{处理独木桥总长度和最后一颗石子之间的位置关系。}
l:=last+t;{同时把独木桥的长度也缩短了。}
end;
function min(x,y:longint):longint;
begin
if x<y then exit(x) else exit(y);
end;
procedure main_2;
var
i,j,ans:longint;
begin
fillchar(f,sizeof(f),100);
f[0]:=0;
for i:=s to l+t-1 do{最后一次跳的位置距离终点最近也就1个单位长度,所以最后一个位置应该是l+t-1。而第一次跳最近是到s位置。}
begin
for j:=t downto s do{t是一个大数,让一个数减大数,所得即为小数,因此从小往大求。}
begin
if i-j>=0 then
f[i]:=min(f[i],f[i-j]+a[i]);{从i-j位置跳到i位置,看i位置上有没石子,然后打擂台进行更新。}
end;
end;
ans:=maxlongint;
for i:=l to l+t-1 do if f[i]<ans then ans:=f[i];{搜索最小值,输出。}
writeln(ans);
end;
begin
assign(input,'P1059.in'); reset(input);
assign(output,'P1059.out'); rewrite(output);
init;
main_1;
main_2;
close(input); close(output);
end.