题解 P1941 【飞扬的小鸟 】

· · 题解

让我们好好的仔细地看题

发现。小鸟一次单位时间内向上跳的次数是无限制的(除非飞到天花板)

那么,回去翻翻 背包九讲 你会发现:

上升其实就是一个完全背包问题

而背包容量就是当前小鸟飞到的高度。

这样一想,下降也很好解决了。

在一个单位时间里,小鸟最多只能下降一次。

那么,对应的背包问题就是01背包问题。

思路理顺了,让我们来推一推方程和状态吧

状态:f_{ij} 表示当前小鸟飞到(i,j)时的最少的跳跃次数

那么方程呢?

先来想如果是跳,

那么当前位置可能是

如果是下降

那么当前的位置可能是

如果是在顶层

就只要将向上跳时溢出m高度的状态与自己比较,取最小值即可

如果无法通过(即有柱子) 只要附一个极大值即可。(方便后续处理)

就差不多了吧

#include<bits/stdc++.h>
#define N 10005
using namespace std;
int n,m,k,x[N],y[N],low[N],high[N],f[N][2005];
bool flag[N];
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
    for (int i=1;i<=n;i++){
        high[i]=m;
        low[i]=1;
    }
    //用high和low表示最低能和最高能通过的地方.
    //开始什么地方都是能通过的,所以high初始为m,low初始为1.
    for (int i=1;i<=k;i++){
        int xx,yy,zz;scanf("%d%d%d",&xx,&yy,&zz);
        flag[xx]=1;
        low[xx]=yy+1;
        high[xx]=zz-1;
        //注意这里yy和zz要分别减一和加一.
    }
    memset(f,0x3f3f3f,sizeof(f));
    for (int i=1;i<=m;i++) f[0][i]=0;
    for (int i=1;i<=n;i++){
        for (int j=x[i]+1;j<=x[i]+m;j++) 
            f[i][j]=min(f[i-1][j-x[i]]+1,f[i][j-x[i]]+1);
        //上升
        for (int j=m+1;j<=x[i]+m;j++)
            f[i][m]=min(f[i][m],f[i][j]);
        //飞出了天花板的特殊情况
        for (int j=1;j<=m-y[i];j++)
            f[i][j]=min(f[i][j],f[i-1][j+y[i]]);
        //下降
        for (int j=1;j<low[i];j++)
            f[i][j]=0x3f3f3f;
        for (int j=high[i]+1;j<=m;j++)
            f[i][j]=0x3f3f3f;
        //处理无法通过的地方
    }
    int ans=0x3f3f3f;
    for (int i=1;i<=m;i++) ans=min(ans,f[n][i]);
    if (ans<0x3f3f3f){
        printf("1\n%d\n",ans);
        return 0;
    }
    //若能通过,直接输出答案
    int i,j;
    for (i=n;i>=1;i--){
        for (j=1;j<=m;j++)
            if (f[i][j]<0x3f3f3f) break;
        if (j<=m) break;
    }
    ans=0;
    for (j=1;j<=i;j++)
        if (flag[j]) ans++;
    printf("0\n%d\n",ans);
    //若不能通过,要统计再横坐标为几的时候挂了,将挂之前通过的柱子全部计入答案
    return 0;
}

如果喜欢,点个赞吧。

对了,注意f_{ij}中的j是枚举高度的,最高会枚举到max(x)+m

所以,数组开大点哦!!!