noip2014飞扬的小鸟

飞扬的小鸟

题目链接

题意:

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

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

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

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

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

Mkx99e.png

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

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

数据范围:

\(5\le n\le10000,5\le m\le1000\)

\(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\n", 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\n", 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\n"); else printf("0\n");
    if(cnt == n) printf("%d\n", ans); else printf("%d\n", tot);
    return 0;
}
posted @ 2019-11-07 18:17  Akaina  阅读(130)  评论(0编辑  收藏  举报