vijos p1002(青蛙过河(过河))(80—100分)

 

算法:DP。

如果数据不是很大的话我们就可以直接DP了...

但是数据有 10^9,所以普通的DP根本不能AC.

我记得一个月以前我做了这道题目,普通地DP,然后只能过三个点。

数组不能开这么大,就算能开这么大也会超时。

这里有个路径压缩的优化方法。。。。(纯属装B....)

首先我们先分析一下数据规模,我们发现呢,L<=10^9. 而石子的个数m<=100.

这就意味着平均两个石子中间有10^7 的路是没有石子的。

也就是说,两个石子中间我们进行了很多没有必要的DP。

我们可以稍微把中间给缩减一下。

我们通过一个运算求得一次最短可跳长度(S)到一次最长可跳长度(T)中的每一个数的最小公倍数的值为k。

假如青蛙可以跳到i点,那么很容易地,他一定能够跳到第k+i点。

设有两个石子,ss[i] 和 ss[i+1].  ss表示石子的坐标。

如果我们有 ss[i+1]-ss[i]>k 的话,因为中间我们走的地方都是没有石子的,我们可以把他们之间的距离缩短一下,缩短成 ss[i+1]-ss[i]-a*k.(如果他们之差是k的倍数,a=(差 div k)-1,

如果不是,a=差 div k)。这样我们就把它们的距离缩短了,但是这个过程不会影响最终的结果。

同理,将每一个点都进行路径压缩,之后就可以像普通DP一样了(PS.看了网上的题解,据说k=100可以应对s<>t的所有情况,我试过的确如此,但是不知道如何证明,所以我还是用了最

小公倍数的方法)。

代码:

 1 program p1002;
 2 var
 3         s,t,t1,i,j,k,l,m,n,a,b,ans:longint;
 4         f:array[-100..120000]of longint;
 5         ss:array[0..11000]of longint;
 6         boo:array[0..11000]of boolean;
 7         d:array[0..10000]of longint;
 8 function min(a,b:longint):longint;
 9 begin
10         if a<b then exit(a)
11         else exit(b);
12 end;
13 begin
14         assign(input,'p1002.in');
15         reset(input);
16         readln(l);
17         read(s,t,n);
18         a:=s;
19         for i:=s+1 to t do
20                 begin
21                 b:=i;
22                 j:=0;
23                 repeat
24                 inc(j);
25                 until(a mod j<>0)or(b mod j<>0);
26                 dec(j);
27                 a:=j*(a div j)*(b div j);
28                 end;
29         k:=a;
30         if k=t then k:=100;
31         for i:=1 to n do
32                 read(ss[i]);
33         for i:=2 to n do
34                 for j:=1 to i-1 do
35                         if ss[i]<ss[j] then
36                                 begin
37                                 t1:=ss[i];
38                                 ss[i]:=ss[j];
39                                 ss[j]:=t1;
40                                 end;
41         for i:=1 to n do
42                 begin
43                 if ss[i]-ss[i-1]>k then
44                         begin
45                         if(ss[i]-ss[i-1]) mod k<>0 then
46                         a:=(ss[i]-ss[i-1])div k
47                         else a:=(ss[i]-ss[i-1])div k-1;
48                         for j:=i to n do
49                         ss[j]:=ss[j]-a*k;
50                         l:=l-a*k;
51                         end;
52                 end;
53         if l-ss[n]>k then
54                 begin
55                 if (l-ss[n]) mod k<>0 then
56                 a:=(l-ss[n])div k
57                 else a:=(l-ss[n])div k-1;
58                 l:=l-a*k;
59                 end;
60         filldword(f,sizeof(f) div 4,maxlongint div 2);
61         fillchar(d,sizeof(d),0);
62         for i:=1 to n do
63                 d[ss[i]]:=1;
64         for i:=-20 to s-1 do f[i]:=-1;
65         f[0]:=0;
66         for i:=s to t do
67                 f[i]:=d[i];
68         for i:=s to l+t-1 do
69                 begin
70                 for j:=s to t do
71                         begin
72                         if f[i-j]<>-1 then
73                                 f[i]:=min(f[i],f[i-j]+d[i]);
74                         end;
75                 end;
76         ans:=maxlongint;
77         for i:=l downto l-s+1 do
78                  ans:=min(ans,f[i]);
79         while f[l]>=maxlongint div 2 do dec(l);
80         write(ans);
81         close(input);
82 end.        

 

posted @ 2012-12-08 21:05  改名字干什么  阅读(358)  评论(0编辑  收藏  举报