noip2005提高组 过河

题目描述
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上
的一串整点: 0,1…,L (其中 L 是桥的长度)。坐标为 0 的点表示桥的起点,坐标为 L 的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是 S 到 T 之间的任意正整数(包括 S,T )。
当青蛙跳到或跳过坐标为 L 的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度 L ,青蛙跳跃的距离范围 S,T ,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

已知信息:
独木桥的长度,青蛙一次最多能够跳过的距离x∈[S, T], 石子的数量及每个石子的位置

求青蛙最少要跳过多少石子

因为点的坐标可能很大,所以不能用下标来表示石子,首先要对坐标进行离散。
由题目可以知道一个很重要的信息:
青蛙不是一定要跳到石头上!!!而是说青蛙跳完整段独木桥,要求踩到的石子尽可能的少,就是说在青蛙跳过独木桥时,可能会有不得不踩到的石子,我们要求这个数量尽可能的少
这样我们首先可以设:
f[i]表示在点i时跳过的最小的石头的数量,这样为了跳到i点,青蛙就必须在[i - s, i - t]区间内的点起跳,所以我们要在这个区间内取最小的更新当前位置
为了方便起见,我们将这个区间内有石头的点用flag数组标为1,这样就有
f[i] = min{f[i - j] + flag[i]}(i - s <= j <= i - t)
但是10^9的点告诉我们,我们显然不能随便去搜,而且,我们数组也不能随便开。
但是我们能够注意到,所有的操作,其实只和[s, t]有关,我们实际用到的区间长度只有[s, t]这一段
我们想,当两点的距离d大于t时,我们发现实际上中间有k段长达t的距离是不必要的,这些距离对于我们的青蛙是否会跳到石头上是没有影响的,这样我们可以把这k段距离通过%t来%掉。
这样也就是说对于两个点x1, x2,之间的距离为d,我们假设由x1跳到x2,实际等价于x1跳d%t的距离到x2
根据这个思路,我们想我们可以先计算出我们这样搞最后所需要的总共的距离的和
但是要注意的是,对于两个点x1, x2,这之间的距离为d%t,但是总距离时我们还需要在单独加上t,因为剩下的d%t,我们知道这个距离是肯定小于t的,这样可能导致我们一些情况的缺失(意会一下就好)
这样我们最后总的最多需要的长度近似等于2 * t *m,最后也就是说我们把10^9的长度近似压缩成了2 * t * m
这样我们就可以很愉快的DP了
需要注意的是,我们并不一定会一定跳在桥的重点,而是只要跳过桥就好,所以最后我们的答案要在一个范围内取min值

//当然,我做不到大佬们那么严谨的证明,建议不要看我的题解

 

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const int maxn = 21000;
 5 int l, s, t, m;
 6 int a[maxn], f[maxn];
 7 int vis[maxn];
 8 int len = 0;
 9 
10 inline int read() {
11     int x = 0, y = 1;
12     char ch = getchar();
13     while(!isdigit(ch)) {
14         if(ch == '-') y = -1;
15         ch = getchar();
16     }
17     while(isdigit(ch)) {
18         x = (x << 1) + (x << 3) + ch - '0';
19         ch = getchar();
20     }
21     return x * y;
22 }
23 
24 int main() {
25     memset(f, 0x3f3f, sizeof(f));
26     memset(vis, 0, sizeof(vis));
27     l = read();
28     s = read(), t = read(), m = read();
29     for(int i = 1; i <= m; ++i)
30         a[i] = read();
31     a[m + 1] = l;
32     sort(a + 1, a + m + 2);
33     for(int i = 1; i <= m + 1; ++i) {
34         if(a[i] - a[i - 1] >= t)
35             len += (a[i] - a[i - 1]) % t + t;
36         else len += a[i] - a[i - 1];
37         vis[len] = 1;
38     }
39     vis[len] = 0;
40     f[0] = 0;
41     for(int i = s; i <= len + t - 1; ++i)
42         for(int j = s; j <= t; ++j)    
43             f[i] = min(f[i], f[i - j] + vis[i]);
44     int ans = 0x3f3f3f;
45     for(int i = len; i <= len + t - 1; ++i)
46         ans = min(ans, f[i]);
47     cout << ans << '\n';
48     return 0;
49 }
View Code

 

posted @ 2018-07-04 13:40  YuWenjue  阅读(358)  评论(0编辑  收藏  举报