[codevs1105][COJ0183][NOIP2005]过河
[codevs1105][COJ0183][NOIP2005]过河
试题描述
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
输入
输入第一行有一个正整数L(1<=L<=109),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1<=S<=T<=10,1<=M<=100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
输出
输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。
输入示例
10 2 3 5 2 3 5 6 7
输出示例
2
数据规模及约定
对于30%的数据,L<=10000;
对于全部的数据,L<=109。
题解
L 比较小时,可以直接 dp:设 f(i) 表示到达位置 i 时最少踩过的石子数目。正解与它做法一样,只是发现许多节点是不需要考虑的,所以我们可以忽略它们。暴力找一下 S, T 分别取 1~10 时的最大不可表数,发现只有 71,那么当相邻两个石子间距离超过 71 时,我们就可以将这个距离变成它对 71 取模再加上 2 倍的 71,最后暴力 dp 一下就好了。注意判断 S = T 的情况。
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 27280 int L, S, T, n, A[maxn], f[maxn]; bool has[maxn]; void up(int& a, int b) { if(a < 0) a = b; else a = min(a, b); return ; } int main() { L = read(); S = read(); T = read(); n = read(); for(int i = 1; i <= n; i++) A[i] = read(); sort(A + 1, A + n + 1); if(S == T) { int cnt = 0; for(int i = 1; i <= n; i++) if(A[i] % S == 0) cnt++; return printf("%d\n", cnt), 0; } int p = 0; for(int i = 1; i <= n; i++) if(A[i] - A[i-1] <= 71) p += A[i] - A[i-1], has[p] = 1; else p += (A[i] - A[i-1]) % 71 + 142, has[p] = 1; memset(f, -1, sizeof(f)); f[0] = 0; for(int i = 0; i <= p + 1; i++) if(f[i] >= 0) for(int j = S; j <= T; j++) { int tmp = (i + j <= p + 1) ? i + j : p + 1; up(f[tmp], f[i] + has[tmp]); } printf("%d\n", f[p+1]); return 0; }