zoukankan      html  css  js  c++  java
  • [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
    ",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
    ",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
    ",i,j,dp[i][j]);
                if(dp[i][j] < INF){
                    ans2 = std::max(ans2,i);
                }
            }
        }
        if(ans2 < n){
            printf("0
    ");
            for(int i = 1; i <= ans2; ++i){
                if(cp[i]){
                    ++cnt;
                }
            }
            printf("%d
    ",cnt);
        }else{
            printf("1
    ");
            ans1 = INF;
            for(int i = 1; i <= m; ++i){
                ans1 = std::min(ans1,dp[n][i]);
            }
            printf("%d
    ",ans1);
        }
        return 0;
    }
  • 相关阅读:
    android WebView总结
    Java抓取网页数据(原网页+Javascript返回数据)
    Linux之旅(1): diff, patch和quilt (下)
    浅谈UML的概念和模型之UML九种图
    基于注解的Spring MVC
    Hibernate自增列保存失败的问题
    京东,你玩我?
    MySQL 通配符学习小结
    Java中怎样由枚举常量的ordinal值获得枚举常量对象
    HDU 4588 Count The Carries 计算二进制进位总数
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/9279395.html
Copyright © 2011-2022 走看看