zoukankan      html  css  js  c++  java
  • [USACO19DEC]Greedy Pie Eaters P(区间dp)

    题目链接(luogu)

    根据题目条件和数据范围不难看出这是一道区间dp。

    定义子状态$dp[i][j]$代表吃区间$[i,j]$内的pie可以得到的最大价值。

    如果不增加新的奶牛可以得到转移方程:$dp[i][j]=max(dp[i][j], dp[i][k]+dp[k+1][j])$。

    如果要新增一个奶牛,则必定有一个k是要被吃的,可以得到转移方程:$dp[i][j]=max(dp[i][j], dp[i][k-1]+dp[k+1][j]+f[k][i][j])$。

    其中$f[k][i][j]$代表满足$i le l le k le r le j$的奶牛的最大价值。

    Q:为什么要求$l,r$在$i,j$内呢?

    A:因为dp的无后效性,所选的牛不能影响后面,可以理解为目前的定义域只有$[i,j]$。

    Q:怎么求出$f$呢?

    A:同样考虑区间dp,我们发现对于每个k的f是不相干的,可以分开求。

    具体来讲,$f[k][i - 1][j] = max(f[k][i - 1][j], f[k][i][j])$,$f[k][i][j + 1] = max(f[k][i][j + 1], f[k][i][j])$。

    有初值:$f[k][l][r]=w[i]$,不用取max因为每个牛的$l,r$一定不同(题目要求)。

    Code:

    for (int k = 1; k <= n; k++) {
        for (int i = k; i >= 1; i--) {
            for (int j = k; j <= n; j++) {
                if (i != 1)
                    f[k][i - 1][j] = max(f[k][i - 1][j], f[k][i][j]);
                if (j != n)
                    f[k][i][j + 1] = max(f[k][i][j + 1], f[k][i][j]);
            }
        }
    }

    之后的转移也就非常简单了,初值$dp[i][i]=f[i][i][i]$。

    Code(完整版):

    #include <bits/stdc++.h>
    using namespace std;
    int n, m;
    int w[10000], l[10000], r[10000];
    int f[301][301][301];
    int dp[310][310];
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &w[i], &l[i], &r[i]);
            for (int j = l[i]; j <= r[i]; j++) {
                f[j][l[i]][r[i]] = w[i];
            }
        }
        for (int k = 1; k <= n; k++) {
            for (int i = k; i >= 1; i--) {
                for (int j = k; j <= n; j++) {
                    if (i != 1)
                        f[k][i - 1][j] = max(f[k][i - 1][j], f[k][i][j]);
                    if (j != n)
                        f[k][i][j + 1] = max(f[k][i][j + 1], f[k][i][j]);
                }
            }
        }
        for (int i = 1; i <= n; i++) dp[i][i] = f[i][i][i];
        for (int len = 2; len <= n; len++) {
            for (int i = 1; i + len - 1 <= n; i++) {
                int j = len + i - 1;
                for (int k = i; k < j; k++) dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j]);
                for (int k = i; k <= j; k++) {
                    int val = 0;
                    if (k != i) val += dp[i][k - 1];
                    if (k != j) val += dp[k + 1][j];
                    val += f[k][i][j];
                    dp[i][j] = max(dp[i][j], val);
                }
            }
        }
        printf("%d", dp[1][n]);
        return 0;
    }
  • 相关阅读:
    PAT甲级——A1006 Sign In and Sign Out
    PAT甲级——A1005 Spell It Right
    左神算法书籍《程序员代码面试指南》——1_05用一个栈实现另一个栈的排序
    左神算法书籍《程序员代码面试指南》——1_03如何使用递归函数和栈操作逆序一个栈
    PAT甲级——A1004 Counting Leaves
    PAT甲级——A1003Emergency
    Dijkstra算法
    PAT甲级——A1002 A+B for Polynomials
    PAT甲级——A1001A+BFormat
    Oil Deposits UVA
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/13039941.html
Copyright © 2011-2022 走看看