Vijos 1002 过河 dp + 思维

https://www.vijos.org/p/1002

设dp[i]表示跳到了第i个点,需要的最小的步数。

所以复杂度O(L * T), 不行

注意到T最大是10, 所以dp[i]最多只由10项递推过来。

Vijos 1002  过河  dp + 思维

考虑上面的那个情况,如果两个相邻的石头距离大于10,那么其实是没意义的,比如上面那两个空的10的位置,完全是可以省略的。因为它相当于从那个有石子的那10个转移过来。所以只要两个距离大于10的石子,就可以压缩距离是10即可。

但是这样做的话,有一个大前提,就是你必须保证每个位置都是可达的。否则,比如你只能去到18和19,你就不能把这两个位置压缩成10和11,因为很有可能10和11是不可达的状态,然后你这个石子就永远枚举不了。也破坏了整到题目的题意。

所以只有保证每个状态都是可达的,然后又是空的位置,才能省略。

最差情况是s == t,这个时候特判。

然后就是s + 1 == t,比如9、10的时候,可达的地方是:

0、9、10、18、19、20、27、28、29、30......观察到大于100后,全部状态都是可达的,所以这个时候就可以压缩了。

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;
const int maxn = 1e5 + 20;
int a[maxn], dp[maxn];
int f[maxn];
void work() {
    memset(dp, 0x3f, sizeof dp);
    int L, s, t, m;
    scanf("%d%d%d%d", &L, &s, &t, &m);
    for (int i = 1; i <= m; ++i) {
        scanf("%d", f + i);
    }
    sort(f + 1, f + 1 + m);
    int to = 0;
    for (int i = 1; i <= m; ++i) {
        if (f[i] - f[i - 1] > 100) {
            to += 100;
            a[to] = true;
        } else {
            to += f[i] - f[i - 1];
            a[to] = true;
        }
    }
    if (s == t) {
        int ans = 0;
        for (int i = 1; i <= m; ++i) {
            ans += f[i] % s == 0;
        }
        cout << ans << endl;
        return;
    }
    dp[0] = 0;
    for (int i = s; i <= maxn - 20; ++i) {
        int be = max(0, i - t), en = max(0, i - s);
        for (int j = be; j <= en; ++j) {
            dp[i] = min(dp[i], dp[j] + a[i]);
        }
    }
    int ans = inf;
    for (int i = maxn - 20; i <= maxn - 1; ++i) {
        ans = min(ans, dp[i]);
    }
    cout << ans << endl;
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    work();
    return 0;
}
View Code