NOIp 2005 过河

NOIp 2005 过河

线性dp,但是范围很变态. = =

30%的方程很容易想到:
[状态] f[i]表示从0到i点最少踩到的石子数, stone[i]表示i点有无石子;
[方程] f[i] = max{f[i-j] + stone[i]} (S<=j<=T)
[初始化] f[0] = 0, 其他赋值为无限大.

在实现的时候需要把方程变化为 f[i+j] = max{f[i] + stone[i+j]},不然在[1,S]会出现很诡异的结果.
 
由于m远远小于l,整个序列上的石子非常稀疏.所以可以减少状态.但是目前对此还是有些不解.
某种思路是,将石子间距大[1,2,..,9,10]=2520的逐次减至小于2520.注意要在收尾增加0和l两个"石子",这样计算新的l较为方便.


 1 #include < stdio.h >
 2 #include < iostream >
 3 using   namespace  std;
 4 #define  MAXN 1000000;
 5 bool  L[ 200000 =   {0} ;
 6 int  f[ 200000 =   {0} , stone[ 110 =   {0} ;
 7 int  min( int  x,  int  y) {return x < y ? x : y;}
 8 void  swap( int  x,  int  y) {
 9    int k = stone[x];
10    stone[x] = stone[y];
11    stone[y] = k;
12}

13 int  main() {
14    int l, S, T, M, i, j;
15    scanf("%d%d%d%d"&l, &S, &T, &M);
16    for (i = 1; i <= M; i++) scanf("%d"&stone[i]);
17    stone[0= 0;
18    for (i = 1; i < M; i++)
19        for (j = i+1; j <= M; j++)
20            if (stone[i] > stone[j]) swap(i, j);
21    stone[++M] = l;
22    for (i = 1; i <= M; i++){
23        while (stone[i] - stone[i-1> 2520) stone[i] -= 2520;
24        if (i != M) L[stone[i]] = 1;
25    }

26    l = stone[M--];
27    for (i = 0; i <= l; i++) f[i] = MAXN; f[0= 0;
28    for (i = 0; i < l; i++)
29        for (j = S; j <= T; j++){
30            int k = i+j;
31            if (i + j >= l) k = l;
32            f[k] = min(f[k], f[i]+L[i]);
33        }

34    printf("%d\n", f[l]);
35}

你可能感兴趣的:(NOIp 2005 过河)