[NOIP2014TG] 洛谷 P1941 飞扬的小鸟

分析题意,可以发现:按屏幕的过程类似于“取”与“不取”,柱子则是一些“限制条件”和“范围”。

思路

首先明确:

1、一秒可以无限点上升键(完全背包),并且一旦上升就不会下降Y。

​ 一秒只能下降一次,不用点(01背包)。

2、碰到地面会死(处理下降时),碰到天花板不会死(处理上升时)

(f[i][j])为在第i列时,所处高度为i可行的最少点击屏幕数。

分段进行状态转移:(手打公式好累啊)

( f[i][j]=min (f[i][j-X[i]]+1,f[i-1][j-X[i]]+1)quad(X[i]+1leq jleq m+X[i]))

//可以按一次(01),也可以按两次以上(完全)。碰到天花板不会死,所以还要更新([m+1,m+X[i]])的状态。

(f[i][j]=min(f[i][j],f[i][m])quad (m+1leq jleq m+X[i]))

//([m+1,m+X[i]])范围内的状态效果与(f[i][m])(正好跳到天花板)相同。如果正好跳到天花板更优则更新。

( f[i][j]=min(f[i][j],f[i-1][j-Y[i])quad (1leq jleq m-Y[i]))

//01背包,从(Y[i])单位长度上方掉落。

$ Inf quad (jleq L[i],H[i]leq j)$

//到不了的,碰到柱子的设为(Inf)

代码

#include<iostream>
#include<iomanip>
#include<cmath>
using namespace std;
const int maxn=10005,maxm=2005;
int f[maxn][maxm],L[maxn],H[maxn],X[maxn],Y[maxn];
int n,m,k,p,cnt,ans,temp;
bool flag;

int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
        cin>>X[i]>>Y[i],L[i]=0,H[i]=m+1;//没有柱子也要设置上下界
    for(int i=1;i<=k;i++)
        cin>>p,cin>>L[p]>>H[p];
    for(int i=1;i<maxn;i++)
     	for(int j=1;j<maxm;j++)
     		f[i][j]=114514;
     //输入,设初值
    for(int i=1;i<=n;i++)
    {
     	for(int j=X[i]+1;j<=m+X[i];j++)
            f[i][j]=min(f[i-1][j-X[i]]+1,f[i][j-X[i]]+1);//可以按一次(01),也可以按两次以上(完全)。碰到天花板不会死,所以还要更新[m+1,m+X[i]]的状态。
        for(int j=m+1;j<=m+X[i];j++)
            f[i][m]=min(f[i][j],f[i][m]);//[m+1,m+X[i]]范围内的状态效果与f[i][m](正好跳到天花板)相同。如果正好跳到天花板更优则更新。
        for(int j=1;j<=m-Y[i];j++)
         	f[i][j]=min(f[i][j],f[i-1][j+Y[i]]);//01背包,从$Y[i]$单位长度上方掉落。
        
        for(int j=1;j<=L[i];j++) f[i][j]=114514;
        for(int j=H[i];j<=m;j++) f[i][j]=114514;
        //到不了的,碰到柱子的设为Inf
    }
    ans=114514;
    for(int i=1;i<=m;i++)
        ans=min(ans,f[n][i]);
    if(ans<114514)
        cout<<1<<"
"<<ans;
    else {
        cout<<"0
";
        cnt=0;
        for(int i=n;i>=0;i--)
        {
            for(int j=1;j<=m;j++)
                if(f[i][j]<114514)
                {
                    flag=1;
                    break;//从后往前搜,一旦有一处能到达,就说明这之前的柱子都能通过
                }
            if(flag)
            {
                cnt=i;
                break;
            }
        }
        for(int i=1;i<=cnt;i++)
            if(H[i]<=m) temp++;//统计柱子数
        cout<<temp;
    }
    return 0;
}

注:数据范围允许使用【数据删除】作为Inf使用


原文地址:https://www.cnblogs.com/ehznehc/p/10985000.html