Sicily 1148 过河


Constraints
Time Limit: 1 secs, Memory Limit: 32 MB
Description 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
Input 输入的第一行有一个正整数L(1 <= L <= 109),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1 <= S <= T <= 10,1 <= M <= 100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
Output 【Sicily 1148 过河】输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。
Sample Input

10 2 3 5 2 3 5 6 7

Sample Output
2



Solution

题目大意很简单,就是找到一种走法是的踩的时候最少。很朴素的想法就是直接搜索,穷尽每一种状态,然后剪枝。但是看一下数据规模就知道不可能。然后想了下这个问题有最优子结构,可以用dp做,状态转移很简单,就是dp(i)=min{dp(i-j) | s <= j <= t}。但是略微一思考就知道开10^9数组来记忆化搜索是不靠谱的,思考了很久,甚至想到最短路去了,也没想出来,自己知识贫瘠啊。然后搜索了一下,大神们说dp没问题,压缩一下状态就好了。于是我就开始简单学习下状态压缩DP,简单来说,状态压缩就是用更少的状态来表示更多的信息,在这道题目里面,因为石子很稀疏,而且maxStep和minStep都是比较小的,那就可以试着去找等价的状态去压缩状态数。
状态压缩是在不影响结果的前提下进行的,此时假设[S,T]=[4,5],第k个石头的坐标是x,第k-1个石头的坐标是y,那么从k和k-1个石头之间跳到第k个石头的位置及之后的可能坐标是x,(x+1),(x+2),(x+3),(x+4),(x+5),如果要进行状态压缩,那就必须确保进行状态压缩之后,这些可能的坐标都要可以取到。然后我们看看此时能从0跳到哪里呢?0->{4,5,8,9,10,12,13,14,15,16,17,18,19,20,21........}。观察可以看出,距离大于12之后都是可达的,也就是说12之后的状态都是一样的。然后,我们选择,当距离大于20的时候,都可以压缩成20并且不影响原有的可能状态,即要缩成S和T的最小公倍数。


另外注意特殊处理S==T的情况即可。



#include #include #include using namespace std; int L, S, T, M; int a[105], res[10500]; bool stone[10500]; int main() { memset(stone, 0, sizeof(stone)); memset(res, 0, sizeof(res)); scanf("%d%d%d%d", &L, &S, &T, &M); a[0] = 0; for (int i = 1; i <= M; ++i) scanf("%d", &a[i]); sort(a, a+M+1); a[M+1] = L; if (S == T) { int ans = 0; for (int i = 1; i <= M; ++i) if (a[i] % S == 0) ++ ans; printf("%d\n", ans); } else { int dis = 0, k = T*(T-1); for (int i = 1; i <= M+1; ++i) { int tmp = a[i] - dis - a[i-1]; if (tmp > k) dis += tmp - k; a[i] -= dis; stone[a[i]] = true; } stone[a[M+1]] = false; res[0] = 0; for (int i = 1; i <= a[M+1]+T-1; ++i) { res[i] = 110; for (int j = S; j <= T; ++j) if (i-j >= 0) res[i] = min(res[i], res[i-j]); if (stone[i]) ++res[i]; }int ans = res[a[M+1]]; for (int i = a[M+1]; i <= a[M+1]+T-1; ++i) ans = min(ans, res[i]); printf("%d\n", ans); }return 0; }




    推荐阅读