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;
}
推荐阅读
- 小马过河
- noip2005|noip2005 过河 (数论+动态规划)
- 过河
- [计蒜客(蓝桥杯省赛)]马踏过河卒
- 洛谷|过河(dp+路径压缩)
- 算法|趣味算法-青蛙过河
- 路人过河
- 数学——离散概率|例题10-16 过河 UVa12230
- M - 青蛙过河
- NOIP2005 过河