标签归档:状态决策

青蛙过河

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

题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

对于30%的数据,L <= 10000;
对于全部的数据,L <= 10^9。

【输入格式 Input Format】
输入的第一行有一个正整数L(1 <= L <= 10^9),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1 <= S <= T <= 10,1 <= M <= 100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。

【输出格式 Output Format】

输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。

【算法描述】
转某牛人的解题报告!!!!
这道题在没看数据规模之前以为是一道简单的DP,但是数据开到十亿,无论在时间还是空间复杂度都过大,所以就要进行优化了。

解一:

简单方法:预期得分30。简单动态规划,f[i]代表青蛙跳到i点时所可能踩到的最少石子数,所以有f[i]=min{f[k]+map[i]}(i-s≤k≤i-t),其中map[i]代表i上是否有石子,有是1,否则0。算法复杂度O(n^2)。

解二:

改进方法:预期得分100。我们会发现,虽然桥很长,但上面最多只有100个石子,想到能否用石子DP,而应该是不行的。那能否基于第一种方法?由于石子排布非常的疏,我们还会发现,如果两个石子相隔甚远,那他们中间的f[i]大部分将会是同一个数,能否把两个石子的距离缩短,使之还与原来等效?要是行的话怎么缩?王乃岩同学考试时做了一个方法能够过全部数据,用的滚动数组存储,下面列出了他的程序。我自己也写了个程序,和他不尽相同:我令L=stone[i]-stone[i-1](stone[i]代表按坐标由小到大顺序排列的石块坐标),当L能够被t整除时(L%t==0),令k=t;当L不能被t整除时(L%t!=0),令k=L%t。然后令k为k+t,最后判断如果k>L,那么map[]数组中stone[i]和stone[i-1]两石头的距离就被等效成为L(也就是没变);如果k<=L,那么map[]数组中stone[i]和stone[i-1]两石头的距离就被等效成为k,可以看出来,这样处理完,两石子最大间距为2*t,大大的缩短了数组,再按解一进行DP,就可以通过了。

【代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <stdio.h>
#include <string.h>
long stone[101];
int map[100001];
int f[100001];
long L;
int S, T, M; 
void quickSort(int l, int r)
{
	int i , j;
	long temp;
	i = l;
	j = r;
	temp = stone[i];
	while (i < j)
	{
		while (i < j && stone[j] > temp)
			j--;
		if (i < j)
		{
			stone[i] = stone[j];
			i++;
		}
		while (i < j && stone[i] < temp)                                                                        
			i++;
		if (i < j)
		{
			stone[j] = stone[i];
			j--;
		}
	}
	stone[i] = temp;
	if (i - 1 > l) quickSort(l, i - 1);
	if (i + 1 < r) quickSort(i + 1, r);
}
int main()
{
	int i, j;
	long l, k, p = 0, min;
	scanf("%ld%d%d%d", &L, &S, &T, &M);
	for (i = 1; i <= M; i++)
		scanf("%ld", &stone[i]);
	memset(map, 0, sizeof(int)*100001);
	memset(f, 0, sizeof(int)*100001);
	quickSort(1, M);
	stone[0] = 0;
	p = 0;
	for (i = 1; i <= M; i++)
	{
		l = stone[i] - stone[i - 1];
		if (l % T == 0) 
			k = T;
		else
			k = l % T;
		k = k + T;
		if (l < k)
			k = l;
		p = p + k;
		map[p] = 1;
	}
	for (i = 1; i <= p + T; i++)
	{
		min = 1000;
		for (j = i - T; j <= i - S; j++)
			if ( j >= 0 && f[j] < min)
				min = f[j];
		f[i] = min + map[i];
	}
	min = 1000;
	for (i = p + 1; i <= p + T; i++)
		if (f[i] < min)
			min = f[i];
	printf("%d\n", min);
	return 0;
}