题目:
游戏界面是一个长为,高为的二维平面,其中有个管道(忽略管道的宽度)。
小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
小鸟每个单位时间沿横坐标方向右移的距离为1,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度X,每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度Y。小鸟位于横坐标方向不同位置时,上升的高度X和下降的高度Y可能互不相同。
小鸟高度等于0或者小鸟碰到管道时,游戏失败。
小鸟高度为m时,无法再上升。现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙。
做法:
很明显是一个。
表示鸟飞到第列,高度为位置需要的最少步数。
转移:
①不操作:
②操作次:
我们发现,第2种转移如果枚举转移的话复杂度太高了,需要做一点优化。
设表示从转移过来的最少步数。
则第2种转移可以改写成:
而其实的求解也是一个线性过程,考虑到比较特殊(不只能转移到。也可以)。所以的转移这样写能避免对的特殊讨论:
本题细节挺多的。好题O(∩_∩)O。
代码:
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false), cin.tie(0) #define debug(a) cout << #a ": " << a << endl using namespace std; typedef long long ll; inline int read(void){ char ch = getchar(); int ans = 0; while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) ans = ans*10 + ch-'0', ch = getchar(); return ans; } const int N = 1e4 + 7; const int M = 1010; const int inf = 0x3f3f3f3f; int n, m, k, x[N], y[N], dp[N][M], mrk[N], pre[N]; pair<int,int> can[N]; int main(void){ n = read(), m = read(), k = read(); for (int i = 0; i < n; ++i) x[i] = read(), y[i] = read(); for (int i = 0; i < k; ++i){ int p = read(), d = read(), h = read(); mrk[p] = 1, can[p] = make_pair(d+1, h-1); } for (int i = 0; i <= n; ++i) if (!mrk[i]) can[i] = make_pair(1, m); memset(dp, inf, sizeof dp); for (int i = 1; i <= m; ++i) dp[0][i] = 0; for (int i = 1; i <= n; ++i){ for (int j = 1; j <= m; ++j) pre[j] = inf; for (int j = 1; j <= m; ++j){ pre[min(j+x[i-1], m)] = min(pre[min(j+x[i-1], m)], min(pre[j]+1, dp[i-1][j]+1)); } for (int j = can[i].first; j <= can[i].second; ++j){ if (j+y[i-1] <= m) dp[i][j] = min(dp[i][j], dp[i-1][j+y[i-1]]); dp[i][j] = min(dp[i][j], pre[j]); } } int ans = inf; for (int i = 1; i <= m; ++i) ans = min(ans, dp[n][i]); if (ans != inf){ printf("1\n%d\n", ans); }else{ int cnt = 0; ans = 0; for (int i = 0; i < n; ++i) if (mrk[i]){ int ok = 0; for (int j = can[i].first; j <= can[i].second && !ok; ++j) ok |= (dp[i][j] != inf); if (!ok) break; ans = ++cnt; } printf("0\n%d\n", ans); } return 0; }