• 洛谷 P1941 飞扬的小鸟 【DP+众多特判】


    题目:

    为了简化问题,我们对游戏规则进行了简化和改编:

    1. 游戏界面是一个长为n ,高为 m 的二维平面,其中有k 个管道(忽略管道的宽度)。

    2. 小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。

    3. 小鸟每个单位时间沿横坐标方向右移的距离为1 ,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度X ,每个单位时间可以点击多次,效果叠加;

    如果不点击屏幕,小鸟就会下降一定高度Y 。小鸟位于横坐标方向不同位置时,上升的高度X 和下降的高度Y 可能互不相同。

    1. 小鸟高度等于0 或者小鸟碰到管道时,游戏失败。小鸟高度为 m 时,无法再上升。

    现在,请你判断是否可以完成游戏。如果可以 ,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙。

    分析:

    这题显然是DP,用f[i][j]表示小鸟飞到横坐标 i ,纵坐标 j 时所需要点击的最少次数。可以分以下三类状态转移:

    1、这一秒的状态是由上一秒上跳得到的:(注意:这是一个完全背包问题,因此在没有压维时,需要注意细节(红色))

    f[i][j]=min(f[i][j],f[i-1][j-hop[i]]+1);

    f[i][j]=min(f[i][j],f[i][j-hop[i]]+1);

    2、这一秒的状态是由上一秒上跳撞到天花板而不再上升得到的:(可以附在上一个状态中经j==m特判处理)

    f[i][j]=min(f[i][j],f[i-1][p]);

    f[i][j]=min(f[i][j],f[i][p]);       (m-hop[i]<=p<=m)从这些点开始不论点几次,小鸟总在天花板上

    3、这一秒的状态是由上一秒下掉得到的:(注意:一秒只能掉一次,因此掉是0/1背包

    f[i][j]=min(f[i][j],f[i-1][j+drop[i]]);  

    最后,再处理在管道中的那些不合法的点:

    f[i][j]=inf; (j<=tube[i].low) 或 (j>=tube[i].high)

    还要注意一定要先处理上升再处理下降,因为上升用到了f[i][j-hop[i]],有可能被下降时更新过,一个鸟不能在一秒内既上升又下降。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define inf 999999999
    using namespace std;
    struct point
    {
        int low,high;
    }tube[10001];
    int m,n,k;
    int x[10001],y[10001];
    int f[10001][1001];
    
    int main()
    {
        cin>>n>>m>>k;
        for(int i=0;i<n;i++)
        {
            cin>>x[i]>>y[i];
            tube[i].high=m+1;
            tube[i].low=0;         
        }
        tube[n].high=m+1;
        tube[n].low=0;
        for(int i=1;i<=k;i++)
        {
            int p;
            cin>>p;
            cin>>tube[p].low>>tube[p].high;        
        }
        memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++)
            for(int j=0;j<=m;j++)
                f[i][j]=inf;              //init();
        f[0][0]=inf;
        
        for(int i=1;i<=n;i++)
        {
            int hh=x[i-1],dd=y[i-1];
            int ll=tube[i].low,tt=tube[i].high;
            //UP
            for(int j=hh;j<=m;j++)
            {
                f[i][j]=min(f[i][j],f[i-1][j-hh]+1);
                f[i][j]=min(f[i][j],f[i][j-hh]+1);
                if(j==m)
                    for(int p=m-hh;p<=m;p++)
                    {
                        f[i][m]=min(f[i][m],f[i-1][p]+1);
                        f[i][m]=min(f[i][m],f[i][p]+1);
                    }
            }
            //DOWN
            for(int j=ll+1;j<tt;j++)
                if(j+dd<=m)
                    f[i][j]=min(f[i][j],f[i-1][j+dd]);
            for(int j=tt;j<=m;j++)
                f[i][j]=inf;
            for(int j=1;j<=ll;j++)
                f[i][j]=inf;
        }
        int ans=inf,cnt=k;
        for(int i=n;i>=1;i--)
        {
            for(int j=tube[i].low+1;j<tube[i].high;j++)
                ans=min(ans,f[i][j]);
            if(ans<inf) break;
            if(tube[i].high<=m) cnt--; 
        }
        if(cnt==k)
            cout<<1<<endl<<ans;
        else
            cout<<0<<endl<<cnt;
        return 0;
    }
  • 相关阅读:
    libTIFF 图像读取与保存
    MarkDown写作之嵌入LaTeX和HTML
    R语言学习(一)前言
    Multi-Byte Character Set & Unicode Character Set
    C/C++ ShellExecuteEx调用exe可执行文件
    C/C++中相对路径与绝对路径以及斜杠与反斜杠的区别
    观察者模式
    责任链模式
    桥接模式
    void及void指针含义的深刻解析
  • 原文地址:https://www.cnblogs.com/linda-fcj/p/7206243.html
Copyright © 2020-2023  润新知