[NOIp2014] 飞扬的小鸟

传送门:>Here<

很经典的题目,题意有些难描述,见原题。

解题思路

这是一个多阶段决策问题的模型,那么可以考虑$DP$。对于每一个位置$dp_{i,j}$,可以是下落到达的,从$dp_{i-1,j+y_i}$转移来。可以是跳上来的,从$dp_{i-1,j-k*x_i}(k \geq 1)$转移来。

这两个方程就是很标准的背包方程。前者是01背包,后者是完全背包。于是我们就想到了完全背包的优化。就在这里是个难点,题目考察了对完全背包优化的理解。

完全背包为什么可以不用枚举$k$?优化,其实就是尽量少作重复工作,尽量不做无用工作。在完全背包中,对于$dp_{i,j}$可以从$dp_{i-1,j-k*x_i}(k \geq 0)$转移而来,而$dp_{i,j-x_i}$是从$dp_{i-1,j-x_i-k*x_i}(k \geq 0)$转移而来的。这之间就有重复工作了。于是我们直接把$dp_{i,j-x_i}$继承下来,在加上这一轮的转移就行了。

这里是一样的。只不过$k$的取值不同,因此继承还是继承,只不过这一轮的转移改变了。而很多人完全背包的滚动版本打多了,忽略了完全背包的滚动版本也是有这一轮自己的转移的,只不过这一轮是继承$dp_{i-1,j}$,而因为滚动,这个值本身就在那里,代码里就不写了。

这道题还加进了状态不存在的情况。我们不能因为某一状态不存在就不去做了,因为后面的有可能要从这个位置转移。处理方法是一列做完后手动去除不合法状态。另外,天花板的情况需要暴力转移。

$Code$

/*DennyQi 2019*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
const int N = 10010;
const int P = 998244353;
const int INF = 0x3f3f3f3f;
inline int mul(const int& a, const int& b){ return 1ll*a*b%P; }
inline int add(const int& a, const int& b){ return (a+b>=P)?a+b-P:a+b; }
inline int sub(const int& a, const int& b){ return (a-b<0)?a-b+P:a-b; }
inline int read(){
    int x(0),w(1); char c = getchar();
    while(c^'-' && (c<'0' || c>'9')) c = getchar();
    if(c=='-') w = -1, c = getchar();
    while(c>='0' && c<='9') x = (x<<3)+(x<<1)+c-'0', c = getchar(); 
    return x*w;
}
int n,m,pp,ans1,ans2,p,cnt,x[N],y[N],l[N],h[N],cp[N],dp[N][1000];
int main(){
    freopen("file.in","r",stdin);
    n = read(), m = read(), pp = read();
    for(int i = 1; i <= n; ++i){
        l[i] = 0;
        h[i] = m+1;
    }
    for(int i = 1; i <= n; ++i){
        x[i] = read(), y[i] = read();
    }
    for(int i = 1; i <= pp; ++i){
        p = read();
        cp[p] = 1;
        l[p] = read();
        h[p] = read();
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j < m; ++j){
            dp[i][j] = INF;
            if(j-x[i] > 0){
                dp[i][j] = std::min(dp[i][j],std::min(dp[i][j-x[i]]+1,dp[i-1][j-x[i]]+1));
                // printf("%d,%d(%d) => %d,%d\n",i,j-x[i],dp[i][j-x[i]],i,j);
            }
        }
        for(int j = 1; j < m; ++j){
            if(j+y[i] <= m){
                dp[i][j] = std::min(dp[i][j],dp[i-1][j+y[i]]);
            }
        }
        dp[i][m] = INF;
        if(i == 1){
            dp[i][m] = 1;
        }else{
            for(int j = m; j >= 1; --j){
                dp[i][m] = std::min(dp[i][m],dp[i-1][j]+((j==m)?1:(int)std::ceil((m-j)*1.0/x[i])));
                // printf("%d+%d\n",dp[i-1][j],(j==m)?1:(int)std::ceil((m-j)*1.0/x[i]));
            }
        }
        for(int j = 1; j <= l[i]; ++j){
            dp[i][j] = INF;
        }
        for(int j = h[i]; j <= m; ++j){
            dp[i][j] = INF;
        }
        for(int j = 1; j <= m; ++j){
            // printf("dp[%d][%d] = %d\n",i,j,dp[i][j]);
            if(dp[i][j] < INF){
                ans2 = std::max(ans2,i);
            }
        }
    }
    if(ans2 < n){
        printf("0\n");
        for(int i = 1; i <= ans2; ++i){
            if(cp[i]){
                ++cnt;
            }
        }
        printf("%d\n",cnt);
    }else{
        printf("1\n");
        ans1 = INF;
        for(int i = 1; i <= m; ++i){
            ans1 = std::min(ans1,dp[n][i]);
        }
        printf("%d\n",ans1);
    }
    return 0;
}

 

posted @ 2018-07-08 10:38  DennyQi  阅读(133)  评论(0编辑  收藏  举报