飞扬的小鸟(NOIP2014)

版权声明:本文为博主原创文章,转载请注明出处:http://blog.csdn.net/cggwz https://blog.csdn.net/cggwz/article/details/82025805

御坂美琴超电磁炮洛谷传送服务

白井黑子vijos传送服务

不能让老婆累着了,所以把另一个交给黑子。
秀老婆!!秀老婆!!(第二个不是)
这里写图片描述

这里写图片描述

知识点

动态规划

动态规划、滚动数组

基本思路

用f[i%2][j]表示到达第i列高度为j的点最少需要跳几次
读入,记录柱子
以横坐标为枚举对象,开始递推
初始化下一列的数组(因为用的是滚动数组)
枚举当前列可达的点
分向上跳和向下落更新
输出

详细解释

状态设计

用f[i%2][j]表示到达第i列高度为j的点最少需要跳几次

读入,记录柱子

用两个数组去保存每个柱子的上限和下限
用数组vis[i]去保存坐标为i的柱子的下标(我们的上限和下限是按顺序存放的)
如果没有,就是-1
(这里也提供一个不同的保存方法,就是不用vis,对于没有柱子的地方,把上限和下限设置为最大值和0,判断的时候可能会简单一点)

以横坐标为枚举对象

我们这里递推以横坐标为对象,而且我们可以看出来,一列的状态,只与前面一列相关,与再往前的就不相关了,所以我们可以采用滚动数组来做。用i%2表示当前列,(i+1)%2表示下一列。
注意,我们要在每次枚举之初设置一个标志变量flag,默认为0
如果扫描到当前列有可达点,就变为1
因为我们有可能会遇到死在半路的情况。

初始化下一列数组

我们采取的策略是用当前列去更新下一列,所以我们要将下一列的数组全部赋值为最大值,便于我们更新。

枚举当前列可达的点

因为我们是由当前列上的点跳到下一列,所以一定要可达才能跳。
能跳的条件就是小于我们初始化的最大值

分向上跳和向下落更新

我们先说向下跳,因为这个很简单。
只要判断别砸到柱子上就好了。
下面我们再看向上跳。
值得注意的是我们向上跳,可以跳多次,不是只能跳一次
我们需要判断在不在柱子之间。
然后结束条件是撞到天花板,或者砸到柱子的上沿
这样就可以了。
如果有flag==0的情况,那就用endx记录结束的列即可。

输出

判断有没有提前结束,然后分情况输出就好了。

难度点评

不算很难,代码写起来似乎复杂一些,不过还是很水。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int f[2][1005];
int n,m;
int k;
int vis[10005];
int L[10005],H[10005];
int X[10005],Y[10005];
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<n;i++){
        scanf("%d%d",&X[i],&Y[i]);
    }
    memset(vis,-1,sizeof(vis));
    for(int i=0;i<k;i++){
        int in;
        scanf("%d%d%d",&in,&L[i],&H[i]);
        vis[in]=i;
    }
    memset(f,0x3f,sizeof(f));
    for(int i=1;i<=m;i++){
        f[0][i]=0;
    }
    int endx=-1,flag=0;
    for(int i=0;i<n;i++){
        flag=0;
        for(int j=1;j<=m;j++){
            f[(i+1)%2][j]=0x3f3f3f3f;    //初始化
        } 
        for(int j=m;j>=1;j--){
            if(f[i%2][j]<0x3f3f3f3f){     
                flag=1;
                int h=j+X[i];
                if(h>m){
                    if(vis[i+1]==-1){
                        f[(i+1)%2][m]=min(f[(i+1)%2][m],f[i%2][j]+1);
                    }
                }

                if(X[i])while(h<=m){
                    if(vis[i+1]!=-1){
                        if(h>L[vis[i+1]]&&h<H[vis[i+1]]){
                            if(f[(i+1)%2][h]<=f[i%2][j]+(h-j)/X[i]){
                                break;
                            }else{
                                f[(i+1)%2][h]=f[i%2][j]+(h-j)/X[i];
                            }
                        }else if(h>=H[vis[i+1]]){
                            break;
                        }
                    }else{
                        if(f[(i+1)%2][h]<=f[i%2][j]+(h-j)/X[i]){
                            break;
                        }else{
                            f[(i+1)%2][h]=f[i%2][j]+(h-j)/X[i];
                        }
                    }
                    if(h==m){
                        break;
                    }
                    h+=X[i];
                    if(h>m){
                        if(vis[i+1]==-1){
                            f[(i+1)%2][m]=min(f[(i+1)%2][m],f[i%2][j]+(h-j)/X[i]);
                            break; 
                        }

                    }
                }
                if(j-Y[i]>=1){
                    if(vis[i+1]!=-1){
                        if(j-Y[i]>L[vis[i+1]]&&j-Y[i]<H[vis[i+1]]){
                            f[(i+1)%2][j-Y[i]]=min(f[(i+1)%2][j-Y[i]],f[i%2][j]);
                        }
                    }else{
                        f[(i+1)%2][j-Y[i]]=min(f[(i+1)%2][j-Y[i]],f[i%2][j]);
                    }
                }

            }

        }
        if(flag==0){
            endx=i-1;
            break;
        }
    }
    if(endx>-1){
        int num=0;
        for(int i=0;i<=endx;i++){
            if(vis[i]>-1){
                num++;
            }
        }
        printf("0\n%d",num);
    }else{
        int ans=0x3f3f3f3f;
        for(int i=1;i<=m;i++){
            ans=min(ans,f[n%2][i]);
        }
        printf("1\n%d",ans);
    }
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/cggwz/article/details/82025805