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

    题目大意

      游戏界面是一个长为n ,高为 m 的二维平面,其中有k 个管道(忽略管道的宽度)。小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。小鸟每个单位时间沿横坐标方向右移的距离为1 ,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度X ,每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度Y 。小鸟位于横坐标方向不同位置时,上升的高度X 和下降的高度Y 可能互不相同。小鸟高度等于0 或者小鸟碰到管道时,游戏失败。小鸟高度为 m 时,无法再上升。现在,请你判断是否可以完成游戏。如果可以 ,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙。

    题解

      本题最简单的动规便是定义$f(i,j)$为第$i$个时间,高度为$j$时最少的上升步数,转移为UpdMin$f(i+1,j-Y_i),f(i,j)+1$,UpdMin$f(i+1,j+kX_i),f(i,j)+k$。然而这样时间复杂度为$O(nm^2)$,不能AC。我们想想怎么消掉那个$k$呢?我们可以这样想:对于每一个$j$,在当前时刻$t$时,我们先点屏幕一次,小鸟移到下一个时间$t'$,随后你就可以随便点屏幕让鸟在时刻$t'$中不断地上升,来更新其它高度的动规值了。这可以看作在$t'$时,把高度作为背包容量,物品体积为$X_t$,价值为$1$,动规值为$f(t',j)$的、每种物品至少选一个的完全背包了。

      注意:

    1. 先由X更新,再由Y更新,因为由X更新时,必须要满足连续地点屏幕的条件,而如果Y在前,可能会导致先不点屏幕让小鸟下降,然后再点屏幕让小鸟在$t'$时上升,不合题意。
    2. 我们只要在刷表时只由合法状态往后更新即可,以后的j如果在管道内,我们也不用管。一方面节省代码量,另一方面完全背包更新时也会需要飞到管道里中的答案。
    3. 对于点屏幕卡在屏幕上端的情况,由越界的情况往下刷即可。
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    #define UpdateMin(x, y) x = min(x, y)
    const int MAX_LEN = 10010, MAX_H = 1010 * 2, MAX_COL = MAX_LEN, INF = 0x3f3f3f3f;
    int TotLen, TotH, TotCol;
    int RiseLen[MAX_LEN], DecLen[MAX_LEN];
    int High[MAX_LEN], Low[MAX_LEN];
    bool ReachT;
    int Ans;
    
    void DP()
    {
        static int F[2][MAX_COL];
        memset(F[0], 0, sizeof(F[0]));
        int passCnt = 0;
        for (int i = 0; i < TotLen; i++)
        {                
            bool noAns = true;
            int top = min(TotH, High[i] - 1), top2 = min(TotH, High[i + 1] - 1);
            for (int h = Low[i] + 1; h <= min(TotH, High[i] - 1); h++)
            {
                if (F[i & 1][h] < INF)
                {
                    noAns = false;
                    break;
                }
            }
            if (noAns)
            {
                Ans = passCnt;
                return;
            }
            if (High[i] < INF)
                passCnt++;
            memset(F[i + 1 & 1], INF, sizeof(F[i + 1 & 1]));
            for (int h = Low[i] + 1; h <= top; h++)
                UpdateMin(F[i + 1 & 1][h + RiseLen[i]], F[i & 1][h] + 1);
            for (int h = 1; h <= top2; h++)
                UpdateMin(F[i + 1 & 1][h + RiseLen[i]], F[i + 1 & 1][h] + 1);
            for (int h = max(Low[i] + 1, DecLen[i] + 1); h <= top; h++)
                UpdateMin(F[i + 1 & 1][h - DecLen[i]], F[i & 1][h]);
            if (High[i + 1] == INF)
                for (int dh = 1; dh <= RiseLen[i]; dh++)
                    UpdateMin(F[i + 1 & 1][TotH], F[i + 1 & 1][TotH + dh]);
        }
        Ans = INF;
        for (int h = 1; h <= TotH; h++)
            UpdateMin(Ans, F[TotLen & 1][h]);
        if (Ans == INF)
            Ans = passCnt;
        else
            ReachT = true;
    }
    
    int main()
    {
        scanf("%d%d%d", &TotLen, &TotH, &TotCol);
        for (int i = 0; i < TotLen; i++)
            scanf("%d%d", RiseLen + i, DecLen + i);
        memset(High, INF, sizeof(High));
        for (int i = 0; i < TotCol; i++)
        {
            int p, low, high;
            scanf("%d%d%d", &p, &low, &high);
            High[p] = high;
            Low[p] = low;
        }
        DP();
        printf("%d
    %d
    ", ReachT, Ans);
        return 0;
    }
    

      

  • 相关阅读:
    Hadoop week review
    [Q&A] VS 连接 SQLServer 时未找到或无法访问服务器
    [System] CentOS虚拟机系统克隆后的网络配置
    [Tool] csdn客户端开发(非官方版)
    [MySQL] Win7 下修改 MySQL 5.5 默认编码格式
    [Q&A] MySQL Error 1050(42S01): Table already exist
    [Matlab] libsvmmat 安装
    [SL] Silverlight + WCF Demo项目
    [Q&A] 在证书存储区中找不到清单签名证书
    谷歌浏览器当手机浏览器
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9734289.html
Copyright © 2011-2022 走看看