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;
    }
    
  • 相关阅读:
    C++primer习题3.13
    Indesign技巧
    《转载》虚函数在对象中的内存布局
    C++new失败后如何处理
    sizeof的用法
    转载 C++中虚继承防止二义性
    字符串反转
    回文写法
    C++术语
    QT+VS2008
  • 原文地址:https://www.cnblogs.com/Akaina/p/11814066.html
Copyright © 2011-2022 走看看