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

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

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;
}


你可能感兴趣的:(sicily)