[codevs1105][COJ0183][NOIP2005]过河

[codevs1105][COJ0183][NOIP2005]过河

试题描述

在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度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
", 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
", f[p+1]);
	
	return 0;
}
原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6165697.html