• $P2690 接苹果$


    (problem)

    看到这题 好几种方法
    1.搜索 大概有一部分分
    2.记忆化搜索 预计100pts
    3.DP 预计100pts
    4.滚动数组优化DP 预计100pts

    1.不带记忆化的搜索妥妥的超时
    2.记忆化搜索? 三个维度? 内存太大? 时间还是(O(玄学))的。
    3.DP 能过 但是可以适当优化
    4.滚动数组优化的DP

    主要是DP
    搜索太过于单调了。代码也比较好写。
    可能还需要剪枝。那么DP?
    

    不难想。 每次有两种。 一种是走。另一种是在原地。
    (dp[i][j])表示奶牛在第(i)分钟内移动(j)次能够接到的最多苹果
    所以可以推出来动态转移方程
    (dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]))
    上面的方程表示上一步的状态可能是不动的 可能是动的。
    这是一种推的方式 还有一种推的方式是反过来的。
    那就不说那么多了 往下。
    同时如果(a[i]==j%2+1)即奶牛在这一分钟可以见到苹果 那么把(dp[i][j]++)
    因为初始位置是1 所以(j%2+1)是奶牛当前位置。 如果当前位置=苹果位置 那么就可以增加苹果量。
    不难得出这一段代码

        for(register int i=1;i<=T;i++){
            for(register int j=0;j<=T and j<=w;j++){
                if(!j) dp[i][j]=dp[i-1][j];
                else dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]);
                if(a[i]==j%2+1) dp[i][j]++;
            }
        }
    

    所以求完之后 再根据题目要求 求出来最多的苹果数量。
    显然。(DP[T][w])不一定是最大值。
    因为有时候不移动比移动要好。
    那么往下走。

    得出代码

        LL ans=-0x7f;
        for(register int i=0;i<=w;i++) ans=max(ans,dp[T][i]);
        cout << ans << endl;
    

    那么基本程序就完成了。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    inline LL In() { LL res(0),f(1); register char c ;
        while(isspace(c=getchar())) ; c == '-'? f = -1 , c = getchar() : f = 1 ;
        while(res = (res << 1) + (res << 3) + (c & 15) , isdigit(c=getchar())) ; return res * f ;
    }
    int T;
    int w;
    int a[1<<10];
    LL dp[1<<10][1<<5];
    signed main () {
        T=In(); w=In();
        for(register int i=1;i<=T;i++) a[i]=In();
        for(register int i=1;i<=T;i++){
            for(register int j=0;j<=T and j<=w;j++){
                if(!j) dp[i][j]=dp[i-1][j];
                else dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]);
                if(a[i]==j%2+1) dp[i][j]++;
            }
        }
        LL ans=-0x7f;
        for(register int i=0;i<=w;i++) ans=max(ans,dp[T][i]);
        cout << ans << endl;
        return 0;
    }
    

    如果对滚动数组有兴趣的 往下看。

    滚动数组,顾名思义。就是滚动的数组。反复利用同一个数组的同一个下标变量。
    因为有时候你只需要上一个状态而不是要许许多多的状态。
    所以如果需要上一个状态的话 可以进行(i&1)的操作(即i%2)
    这样子会节省内存。滚动数组很有用的

    LL dp[1<<10][1<<5];//无滚动数组的数组大小
    LL dp[2][1<<5];//滚动数组的数组大小
    

    因为(线性DP)以及(区间DP)都可以用这个来优化内存。
    比如说 刚才 那一段(DP)的过程 可以转化为 ↓

        for(register int i=1;i<=T;i++){
            for(register int j=0;j<=T and j<=w;j++){
                if(!j) dp[i&1][j]=dp[(i-1)&1][j];
                else dp[i&1][j]=max(dp[(i-1)&1][j],dp[(i-1)&1][j-1]);
                if(a[i]==j%2+1) dp[i&1][j]++;
            }
        }
    

    然后也求出来了。
    不过求最大值需要注意一点。

    不是dp[T][0~w]!!!!(敲黑板)

        LL ans=-0x7f;
        T&=1;
        for(register int i=0;i<=w;i++) ans=max(ans,dp[T][i]);
        cout << ans << endl;
    

    下面是滚动数组优化的代码。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    inline LL In() { LL res(0),f(1); register char c ;
        while(isspace(c=getchar())) ; c == '-'? f = -1 , c = getchar() : f = 1 ;
        while(res = (res << 1) + (res << 3) + (c & 15) , isdigit(c=getchar())) ; return res * f ;
    }
    int T;
    int w;
    int a[1<<10];
    LL dp[2][1<<5];
    signed main () {
        T=In(); w=In();
        for(register int i=1;i<=T;i++) a[i]=In();
        for(register int i=1;i<=T;i++){
            for(register int j=0;j<=T and j<=w;j++){
                if(!j) dp[i&1][j]=dp[(i-1)&1][j];
                else dp[i&1][j]=max(dp[(i-1)&1][j],dp[(i-1)&1][j-1]);
                if(a[i]==j%2+1) dp[i&1][j]++;
            }
        }
        LL ans=-0x7f;
        T&=1;
        for(register int i=0;i<=w;i++) ans=max(ans,dp[T][i]);
        cout << ans << endl;
        return 0;
    }
    
    

    据说有大佬(O(T+W))
    还有大佬(O(玄学))
    (0ms) 这才叫玄学。

    不存在十全十美的文章 如同不存在彻头彻尾的绝望
  • 相关阅读:
    第二章 搭建Android开发环境
    彻底修改 Windows 系统用户名
    第一章 Android系统移植与驱动开发概述
    返回一个整数数组中最大子数组的和。
    返回一个二维整数数组中最大子数组的和。
    返回一个整数数组中最大的子数组的和。
    四则运算
    关于南方Cass的使用感受
    数据结构-王道2017-第3章 栈和队列-栈和队列的应用
    数据结构-王道2017-第3章 栈和队列-队列
  • 原文地址:https://www.cnblogs.com/qf-breeze/p/10574196.html
Copyright © 2020-2023  润新知