zoukankan      html  css  js  c++  java
  • noip2014飞扬的小鸟

    飞扬的小鸟

    题目链接

    题意:

    一个二维平面,长为(n),宽为(m),其中有(k)个管道,小鸟从最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。

    小鸟每个单位时间沿着(x)轴向右移动一个单位距离,小鸟在从(i)移动到(i +1)时,玩家可以选择是否点击屏幕(可以点击多次),若不点击,小鸟将下降(y_i)单位距离,若点击(j)次,小鸟的上升距离为(j imes x_i)

    当小鸟碰到管壁或纵坐标等于(0)时,游戏结束。小鸟的高度可以不停上升,但最高不超过(m),(可以到达(m),游戏不会结束)。

    管道:并不是每个横坐标都对应一个管道,管道有一个上边沿高度和一个下边沿高度。

    这样一个图片便于理解题意:

    Mkx99e.png

    蓝色直线表示小鸟的飞行轨迹,红色直线表示管道。

    求是否可以完成游戏,如果可以,求最小点击次数,如果不可以,求最多可以通过多少个管道缝隙。

    数据范围:

    (5le nle10000,5le mle1000)

    (70\,\,points)

    这个(dp)方程除了跳到顶比较难处理以外,还是很容易想到的,

    但相对的,时间复杂度也很大,(O(nm^2))

    for(int i = 1; i <= n; i++)
    {
        for(int j = max(L[i], x[i - 1]); j <= min(P[i], m); j++)
        {
            for(int k = 1; j + k * x[i - 1] <= m; k++)
            {
                dp[i][j] = min(dp[i][j], dp[i - 1][j - x[i - 1]] + 1);
                if(j + Y[i - 1] <= m) dp[i][j] = min(dp[i][j], dp[i - 1][j + Y[i - 1]]);
            }
            if(j == m)
            {
                for(int k = m - x[i - 1]; k <= m; k++)
                {
                    dp[i][j] = min(dp[i][j], dp[i - 1][k] + 1);
                    dp[i][j] = min(dp[i][j], dp[i][k] + 1);
                }
            }
        }
    }
    

    (100 \,\,points)

    考虑如何把时间复杂度优化到(O(nm))

    首先枚举横坐标和纵坐标是难以优化的,

    考虑(x = 3)的情况(众所周知,找规律要从(3)开始找)

    (dp[3,10] = min(dp[2,7] + 1,dp[2,4]+2,dp[2,1] +3))

    (dp[3,7] = min(dp[2,4] + 1,dp[2,1] + 2))

    (dp[3,4]=min(dp[2,1] + 1))

    由以上的规律,我们很容易得出以下的一个式子:

    (dp[i][j] = min(dp[i - 1][j - x[i - 1]] + 1, dp[i][j - x[i - 1]] + 1))

    好吧,其实不用这么麻烦,可以用从上一次转移必须装物品的完全背包进行理解

    for(int i = 1; i <= n; i++)
    {
        for(int j = max(L[i], x[i - 1]); j <= min(P[i], m); j++)
        {
            dp[i][j] = min(dp[i][j], dp[i - 1][j - x[i - 1]] + 1);
            dp[i][j] = min(dp[i][j], dp[i][j - x[i - 1]] + 1);
            if(j == m)
            {
                for(int k = m - x[i - 1]; k <= m; k++)
                {
                    dp[i][j] = min(dp[i][j], dp[i - 1][k] + 1);
                    dp[i][j] = min(dp[i][j], dp[i][k] + 1);
                }
            }
        }
    }
    
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    #define maxn 10100
    int n, m, k;
    int X[maxn], Y[maxn], L[maxn], H[maxn];
    int dp[maxn][1010];
    int main()
    {
        memset(dp, 0x3f, sizeof(dp));
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 0; i < n; i++)
        {
            scanf("%d%d", &X[i], &Y[i]);
            L[i] = 0; H[i] = m + 1;
        } L[n] = 0; H[n] = m + 1;
        for(int i = 1; i <= k; i++)
        {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            L[x] = y; H[x] = z;
        }
        for(int i = 1; i <= m; i++) dp[0][i] = 0;
        for(int i = 0; i <= n; i++)
        {
            for(int j = 1; j <= m; j++)
            {
                if(j - X[i - 1] > L[i - 1] && j - X[i - 1] < H[i - 1])
                    dp[i][j] = min(dp[i - 1][j - X[i - 1]] + 1, dp[i][j]);
                if(j - X[i - 1] > 0)dp[i][j] = min(dp[i][j], dp[i][j - X[i - 1]] + 1);
     //           if(j + Y[i - 1] <= m) dp[i][j] = min(dp[i][j], dp[i - 1][j + Y[i - 1]]);
                if(j == m)
                {
                    for(int k = m - X[i - 1]; k <= m; k++)
                    {
                        if(k > L[i - 1] && k < H[i - 1])
                        dp[i][j] = min(dp[i][j], dp[i - 1][k] + 1);
                        dp[i][j] = min(dp[i][j], dp[i][k] + 1);
                    }
                }
            }
            for(int j = 1; j <= m - Y[i - 1]; j++)
            {
                if(j + Y[i - 1] > L[i - 1] && j + Y[i - 1] < H[i - 1])
                dp[i][j] = min(dp[i][j], dp[i - 1][j + Y[i - 1]]);
            }
        }
        int cnt = 0, ans = 0;
        for(int i = 0; i <= n; i++)
        {
     //       if(i == 1 || i == 2)
     //       printf("i = %d L  = %d H = %d
    ", i, L[i], H[i]);
            for(int j = L[i] + 1; j <= H[i] - 1; j++)
            {
       //         if(i == 1 || i == 2)
       //         printf("dp[%d][%d] = %d cnt = %d ans = %d
    ", i, j, dp[i][j], cnt, ans);
                if(dp[i][j] < 1e9)
                {
                    if(cnt < i)
                    {
                        ans = 1e9; cnt = i;
                        ans = min(ans, dp[i][j]);
                    }
                    else ans = min(ans, dp[i][j]);
                }
            }
        }
        int tot =  0;
        if(cnt != n)
        {
            for(int i = 0; i <= cnt; i++)
                if(H[i] != m + 1) tot++;
        }
        if(cnt == n) printf("1
    "); else printf("0
    ");
        if(cnt == n) printf("%d
    ", ans); else printf("%d
    ", tot);
        return 0;
    }
    
  • 相关阅读:
    LeetCode 842. Split Array into Fibonacci Sequence
    LeetCode 1087. Brace Expansion
    LeetCode 1219. Path with Maximum Gold
    LeetCode 1079. Letter Tile Possibilities
    LeetCode 1049. Last Stone Weight II
    LeetCode 1046. Last Stone Weight
    LeetCode 1139. Largest 1-Bordered Square
    LeetCode 764. Largest Plus Sign
    LeetCode 1105. Filling Bookcase Shelves
    LeetCode 1027. Longest Arithmetic Sequence
  • 原文地址:https://www.cnblogs.com/Akaina/p/11814066.html
Copyright © 2011-2022 走看看