[noip 2014]飞扬的小鸟

飞扬的小鸟

题意:小鸟游戏,从最左边出发,到达最右边,假设,小鸟在(x,y),现在有两种走法,一种是点击K4,还有一种是保持不动,这两种情况分别可以让小鸟到(x+1,y+kX)。第二种情况是(x+1,y-Y)。地图中你有不可以到的地方,不能碰到水管和地面。现在求最小的点击次数,使得小鸟可以到达右边。(注意:不能超过最上面一行,点击以后,不会超过最上面一行)

题解:这道题刚开始看是最短路,可以用djstela去做,但是会超时。每个起点开始,发现会超时。仔细一看,发现可以用背包做。就是一个01背包套一个完全背包的问题。01背包就是往下掉,完全背包是可以往上点,两种背包套在一起。子状态dp(i,j)代表最少需要点击几次到达(i,j)这个位置,不能到达就设成正无穷大。转移方程dp(i,j)=min(dp(i-1,j+Y),dp(i,j-X)),把不能到的地方在设成正无穷。如果不能到达,就从后往前找,找到第一个可以到的列,就是最远的列far,然后记录一下哪些列有水管,输出[1, far]里的水管即可。

#include <iostream>
#include <cstring>
using namespace std;
int n, m, k;
int x[10010], y[10010];
int dp[10010][2010];
int low[10010], high[10010];
bool flag[10010];
int ans = 0x7f7f7f7f;
int read() {
    int ret = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-') {
            f = -1;
        }
        ch = getchar();
    }
    while (isdigit(ch)) {
        ret = ret * 10 + ch - '0';
        ch = getchar();
    }
    return ret * f;
}
int main() {
    n = read(), m = read(), k = read();
    for (int i = 1; i <= n; i++) {
        x[i] = read();
        y[i] = read();
    }
    for (int i = 1; i <= n; i++) {
        high[i] = m + 1;
        low[i] = 0;
    }
    for (int i = 1, p; i <= k; i++) {
        p = read(), low[p] = read(), high[p] = read();
        flag[p] = 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 - x[i]] + 1, dp[i - 1][j - x[i]] + 1);
        }
        for (int j = m + 1; j <= x[i] + m; j++) {
            dp[i][m] = min(dp[i][j], dp[i][m]);
        }
        for (int j = 1; j + y[i] <= m; 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] = 0x3f3f3f3f;
        }
        for (int j = high[i]; j <= m; j++) {
            dp[i][j] = 0x3f3f3f3f;
        }
    }
    for (int i = 1; i <= m; i++) {
        ans = min(ans, dp[n][i]);
    }
    if (ans < 0x3f3f3f3f) {
        cout << 1 << endl;
        cout << ans;
    } else {
        ans = 0;
        cout << 0 << endl;
        int far = 0;
        for (int i = n; i >= 0; i--) {
            for (int j = 1; j <= m; j++) {
                if (dp[i][j] < 0x3f3f3f3f) {
                    far = i;
                    break;
                }
            }
            if (far) break;
        }
        for (int i = far; i; i--) {
            if (flag[i]) {
                ans++;
            }
        }
        cout << ans;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zcr-blog/p/11826973.html