1. 程式人生 > >luogu1941 飛揚的小鳥

luogu1941 飛揚的小鳥

條件 algorithm 代碼 現在 sin 每一個 pri 不同 忽略

題目大意

  遊戲界面是一個長為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\n%d\n", ReachT, Ans);
    return 0;
}

  

luogu1941 飛揚的小鳥