题目链接:计蒜客 T2021 飞扬的小鸟
题目大意:
题解:
按照横坐标从左往右递推,设\(dp[i][j]\)为到达点\((i,j)\)所需的最小点击次数。
状态转移方程:
因为触顶之后不会再上升,所以\(dp[i][m] = min\{dp[i][k],m\leq k \leq m+x[i]\}\)。
如果所在的点有管道,则\(dp[i][j]=0\)。
#include <cstring>
#include <iostream>
using namespace std;
#define INF 0x3f3f3f3f
#define N 10010
#define M 2010
int n, m, p, x[N], y[N], low[N], high[N], dp[N][M], ans;
bool vis[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m >> p;
for (int i = 1; i <= n; ++i) {
cin >> x[i] >> y[i];
}
for (int i = 1; i <= n; ++i) {
low[i] = 1, high[i] = m;
}
for (int i = 1, k, l, h; i <= p; ++i) {
cin >> k >> l >> h;
vis[k] = true;
low[k] = l + 1;
high[k] = h - 1;
}
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= m; ++i) {
dp[0][i] = 0;
}
for (int i = 1; i <= n; ++i) {
for (int j = x[i] + 1; j <= m + x[i]; ++j) {
dp[i][j] = min(dp[i][j], min(dp[i - 1][j - x[i]] + 1, dp[i][j - x[i]] + 1));
}
for (int j = m + 1; j <= m + x[i]; ++j) {
dp[i][m] = min(dp[i][m], dp[i][j]);
}
for (int j = 1; j <= m - y[i]; ++j) {
dp[i][j] = min(dp[i][j], dp[i - 1][j + y[i]]);
}
for (int j = 1; j < low[i]; ++j) {
dp[i][j] = dp[0][0];
}
for (int j = high[i] + 1; j <= m; ++j) {
dp[i][j] = dp[0][0];
}
}
ans = INF;
for (int i = 1; i <= m; ++i) {
ans = min(ans, dp[n][i]);
}
if (ans < INF) {
cout << 1 << endl << ans << endl;
} else {
int i, j;
for (i = n; i >= 1; --i) {
for (j = 1; j <= m; ++j) {
if (dp[i][j] < INF) {
break;
}
}
if (j <= m) {
break;
}
}
ans = 0;
for (j = 1; j <= i; ++j) {
if (vis[j]) {
ans++;
}
}
cout << 0 << endl << ans << endl;
}
return 0;
}