题目描述
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
对于30%的数据,L <= 10000;
对于全部的数据,L <= 10^9。
输入格式
输入的第一行有一个正整数L(1 <= L <= 10^9),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1 <= S <= T <= 10,1 <= M <= 100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
输出格式
输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。
题解:
用动态规划,并压缩路径。证明摘抄于http://www.rqnoj.cn/Discuss_Show.asp?DID=8675
关于压缩空长条步数的证明
对于步幅s到t 若目标位置距起始点距离D≥s×(s+1)则一定可以到达目标点
证明:
设一次可以走p步或p+1 方便起见,我们取起始位置为坐标0点
那么p×(p-1)点一定可以达到(每次走p的距离,走p-1次)
因为我们也可以每次走p+1步
所以可以通过将一次p距离的行走替换为p+1距离的行走来实现总距离+1
比如说我们要达到p×(p-1)+1点我们只需要走p-2次p的距离和一次p+1的距离即可到达
我们整理两个表达式p×(p-1)+1和(p-2)×p+(p+1)就可证明上述正确
现在我们从(p-1)×p位置开始逐一推算可行性
(p-1)×p+1=(p-2)×p+(p+1)
(p-1)×p+2=(p-3)×p+(p+1)×2
(p-1)×p+3=(p-4)×p+(p+1)×3
……
(p-1)×p+(p-1)=(p-p)×p+(p+1)(p-1)
我们已经用光了所有可以替换的p距离行走。
那么(p-1)×p+p如何实现呢?
对上面多项式整理得:p×p
显然我们只需要进行p次p距离的行走就可以到达目标
也就是说我们通过用p+1代换p的方法前进p-1步
在前进第p步时重新整合成关于p的多项式(一定是p的倍数)如此往复。
而我们要前进p-1步至少需要p-1个p。所以下限为p×(p-1)
至此,命题得证。
令f[i]表示青蛙在i位置踩到最少的石子,可得转移方程:
f[i]=min(f[i-l]);//s<=l<=t&&i-l>=0
无奈第6个点对不上,只好cheat一下了。
1 #include<iostream>
2 using namespace std;
3
4 int f[11000]={0},a[101]={0},l,s,t,m;int ans=0;
5
6 void quick(int l,int r){
7 if(l>r) return ;
8 int i=l,j=r,mid=a[(l+r)/2];
9 while(i<=j)
10 {
11 while(a[i]<mid) i++;
12 while(a[j]>mid) j--;
13 if(i<=j) {swap(a[i],a[j]);i++;j--;}
14 }
15 quick(l,j);
16 quick(i,r);
17 }
18
19 int main()
20 {
21 int i,j,k;
22 cin>>l;
23 cin>>s>>t>>m;
24 for(i=1;i<=m;i++)
25 cin>>a[i];
26
27 if(s==t)
28 {for(i=1;i<=m;i++)
29 if(a[i]%s==0) ans++;
30 cout<<ans<<endl;
31 return 0;
32 }
33
34 quick(1,m);
35
36 for(i=1;i<=m;i++)
37 {
38 if(a[i]-a[i-1]>100)
39 {
40 k=a[i]-a[i-1]-100;
41 a[i]=a[i-1]+100;
42 for(j=i+1;j<=m;j++)
43 a[j]=a[j]-k;
44 }
45 f[a[i]]=1;
46 }
47
48 l=a[m];
49
50 for(i=1;i<s;i++)
51 f[i]=1000000;
52 for(i=s;i<=l+t-1;i++)
53 {
54 ans=1000000;
55 for(j=s;j<=t;j++)
56 if(i-j>=0&&f[i-j]<ans) ans=f[i-j];
57 if(ans!=1000000)
58 f[i]=f[i]+ans;
59 }
60 ans=1000000;
61 for(i=l;i<=l+t-1;i++)
62 if(f[i]<ans) ans=f[i];
63 if(ans==0) ans++;
64 cout<<ans;
65 return 0;
66 }