设dp[i]表示跳到了第i个点,需要的最小的步数。
所以复杂度O(L * T), 不行
注意到T最大是10, 所以dp[i]最多只由10项递推过来。
考虑上面的那个情况,如果两个相邻的石头距离大于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; }