• luogu P2858 [USACO06FEB]Treats for the Cows G/S


    题目链接

    题目概括:

      给定一个序列v,每次可以从左端点处或右端点处取走一个数v[i],第a次取数可获得的价值为v[i]*a,求把这个序列取完可获得的最大价值
    

    分析

      要想获得最大价值,肯定要让大的数字后取。
    

    做法

    1.贪心(27分)

      用双指针枚举首和尾,看哪个小就先取哪个。
    
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int N = 2010;
    int hh, tt, a, v[N], n, ans;
    
    int main() {
        scanf("%d", &n);
        hh = 1;
        for(tt = 1; tt <= n ; tt ++) 
            scanf("%d", &v[tt]);
        tt = n;
        // cout << endl;
        while(hh <= tt) {
            if(v[hh] < v[tt]) {
                ans += v[hh] * ++a;
                hh ++;
                // cout << ans << endl;
            }
            else{
                ans += v[tt] * ++a;
                tt --;
                // cout << ans << endl;
            }
        }
        cout << ans << endl;
        return 0;
    }
    

    2.正解:区间DP

    建议先看注释,注释很清楚。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int N = 2010;
    int n, v[N], f[N][N];
    
    int main() {
        cin >> n;
        for(int i = 1; i <= n ; i ++)
            scanf("%d", &v[i]);
        for(int i = 1; i <= n ; i ++)
            f[i][i] = v[i] * n;
        for(int len = 2; len <= n ; len ++) {
            for(int i = 1; i <= n ; i ++) {
                int j = i + len - 1;
                f[i][j] = max(f[i+1][j]+v[i]*(n - len + 1), f[i][j-1]+v[j]*(n - len + 1));
            }
        }
        /*
        f[i][j]表示区间[i, j]全都取完,可获得的最大价值。
        f[i][j]可以由两个方向转移过来
            一个是1.f[i+1][j]
            另一个2.f[i][j-1]
            在如上两种取法可获得的最大价值中取max,再加上由1或2转移到f[i][j]可获得的价值,就是f[i][j]
    
        边界:当区间长度(j - i + 1) 为 1 时也就是i == j 时,可获得的价值是v[i] * n
    
        值得一提:
            由1或2转移到f[i][j]时,可获得的价值是v[i]*a或v[j]*a,但是这里的a用什么来表示呢?
            当转移到的区间长度(len)为n时,a = 1;
            当转移到的区间长度(len)为n-1时,a = 2;
                    ...         ...
            当转移到的区间长度(len)为2时,a = n - 1;
            当转移到的区间长度(len)为1时,a = n
            这时候我们可以发现:
                a + len 始终是等于n + 1的
                ∵ a + len == n + 1
                ∴ a == n - len + 1
        代码实现:
            根据区间dp的普遍写法,先初始化边界(i:1 to n, f[i][i] = v[i] * n)
            然后枚举区间长度len
                    枚举左端点i
                        算出右端点j = i + len - 1
                        并转移f[i][j] = max(f[i+1][j]+v[i]*(n - len +1), f[i][j-1]+v[j]*(n-len+1))
            最后的答案就是f[1][n](从第1个数到第n个数全部取完,可获得的最大价值)
        */
        cout << f[1][n] << endl;
        return 0;
    }
    

    OI生涯中第一道区间DP

    2020.11.4
    写于初中OI退役前第3天

  • 相关阅读:
    【Azure】Azure 命令行接口 (CLI)
    【Git】敏感信息保护
    【LeetCode】31.下一个排列
    【LeetCode】26.删除排序数组中的重复项
    【LeetCode】16. 最接近的三数之和
    【LeetCode】15.三数之和
    工作中常用的 JVM 配置参数有哪些?
    Java面试突击100题
    Java奇淫异术
    Linux 大文件按大小切割,合并还原
  • 原文地址:https://www.cnblogs.com/sjzez-llb/p/13925176.html
Copyright © 2020-2023  润新知