• 【算法学习笔记】71.动态规划 双重条件 SJTU OJ 1124 我把助教团的平均智商拉低了


    这个题第一眼以为只是一个简单的序列型DP问题,于是快速的写下了下面的代码。

    #include <iostream>
    using namespace std;
     
    /*
     看着只是简单的序列DP..不知道有没有坑
     DP[k]表示的是有前k个活动可以选择时的答案
     */
     
    struct state
    {
        int health;//体力 永远大于0
        int wisdom;//智力 最后大于70
        int product;//乘积
        void calc(){
            product = health * wisdom;
        }
        
    };
     
    struct act
    {
        int dh;
        int dw;
    };
     
    state dp[400+5];
    act acts[400+5];
    int n;//一共的活动数目
     
    void init(){
        cin>>n;
        cin>>dp[0].health>>dp[0].wisdom;
        dp[0].calc();
        for (int i = 1; i <= n; ++i)
        {
            cin>>acts[i].dh>>acts[i].dw;
        }
    }
     
    void build(){
        for (int i = 1; i <= n; ++i)
        {
            //
            int h = dp[i-1].health + acts[i].dh;
            int w = dp[i-1].wisdom + acts[i].dw;
            
            if( h > 0 ){
                if( h * w >= dp[i-1].product){
                    dp[i].health = h;
                    dp[i].wisdom = w;
                    dp[i].calc();
                }else{
                    dp[i] = dp[i-1];
                }
            }else{
                dp[i] = dp[i-1];
            }
        }
        if(dp[n].wisdom > 70 and dp[n].health > 0)
            cout<<dp[n].product<<endl;
        else
            cout<<"Death"<<endl;
    }
    int main(int argc, char const *argv[])
    {
        init();
        build();
        return 0;
    }
    View Code

    这个代码在是求和的时候是可以的,因为前面和最大的情况,+dh +dw仍然也是最大的。

    但是在乘积的时候就不然了,例子不好举。但是很明显 当达到积最大有两种情况时,这两种选哪个作为dp值在不知道下一个活动时是无法确定的。

    所以就想到,确定一个变量,让一维的DP化为二维。

    dp[i][j]表示在前i个活动可以选择时,体力值为j时,最大的智力值是多少。

    所以

    dp[i][j] =
    max {(dp[i-1][j-acts[i].h] + acts[i].w) //选上, (dp[i-1][j]) //不选}
                           

    这里有一个细节要注意就是,初始状态是1000,那么数组第二维要开到32000+50才行。(。。最后才发现是这个地方错了 还不报RE真是可恶。。)

    还有一个非常重要的细节就是 智力值在过程中是可以为负数的,所以我们初始化时要把智力值全部设置为-INF才可以,这样我们就可以根据智力值与-INF的关系知道当前处理的状态的是经过一系列过程转化来的智力值,还是根本没有办法达到的智力值。(此处也可以在搞一个bool型的二维数组来记录这两种状态,也不麻烦而且更美观)

    另外还有一个地方可以优化 就是内层循环的次数,我们可以采取迭代递增的方式,让curMax始终是当前可能的最大的体力值,但是实际上没什么太大的效果。

    struct act
    {
        int dh;
        int dw;
    };
     
    // state dp[400+5];
    int dp[400+5][32000+50]={0};
    act acts[500+5];
    int n;//一共的活动数目
     
    void init(){
        cin>>n;
        for (int i = 0; i <= n; ++i)
        {
            cin>>acts[i].dh>>acts[i].dw;
        }//注意acts[0]存储的是初始情况
        
    }
     
    void build(){
        //初始化为负无穷 以来判断智力值是转化来的 还是根本没有被利用
        memset(dp,-50000,sizeof(dp));
        
        dp[0][acts[0].dh] = acts[0].dw;//初始化
        int curMax = 31000+50;//固定的curMax 注意31000 = 30000+1000 
    
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 0; j <= curMax; ++j)
            {
    
                if(j >= acts[i].dh and dp[i-1][j-acts[i].dh] > -50000){
                    //体力值大于0 且 智力值是有效的
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-acts[i].dh]+acts[i].dw);
                }
                else
                    dp[i][j] = dp[i-1][j];
            }
        }
        int res = 0; 
        //找到最大的
        for (int j = 1; j <= curMax; ++j)
        {
            if(dp[n][j] > 70){ 
                res = max(res,dp[n][j] * j);
            }
        }
        if(res)
            cout<<res<<endl;
        else
            cout<<"Death"<<endl;
    }
     
    int main(int argc, char const *argv[])
    {
        init();
        build();
        return 0;
    }
  • 相关阅读:
    inner join和join
    Java输入输出流
    数据库基础——并发控制
    逻辑题
    数据库基础——数据库设计
    JDBC
    XmlHttpRequest
    servlet乱码
    Tomcat缺少服务
    poj2388---求奇数个数字的最中间的数
  • 原文地址:https://www.cnblogs.com/yuchenlin/p/sjtu_oj_1124.html
Copyright © 2020-2023  润新知