Loading

CF2021D 题解 | dp

传送门

题意

给你一个 \(n\)\(m\) 列的矩阵,要求对于每行选出一个区间 \([l_i, r_i]\),得到 \(\sum _ {j = {l_i}} ^ {r_i} a_{i, j}\) 的权值。

区间之间有限制:

\[\exists j, j \in [l_{i - 1}, r_{i - 1}], j \in [l_i, r_i] \]

\[\exists j, j \not \in [l_{i - 1}, r_{i - 1}], j \in [l_i, r_i] \]

求最大权值。

思路

显然这是一个 dp 题。

考虑朴素的状态设计 \(f_{i, l, r}\) 表示计算了前 \(i\) 行,第 \(i\) 行选区间 \([l, r]\) 的最大权值。
显然状态数 \(O(nm^2)\) 直接倒闭。

我们试图转换题目限制,可以发现,相当于是限制 \(l_{i - 1}, r_{i - 1}\) 至少有一个在区间 \((l_i, r_i)\) 中。

那也就是说,我们没有必要记录左右端点,于是得到状态优化:
\(f_{i, j, 0/1}\) 表示计算前 \(i\) 行,第 \(i\) 行选择的左(\(0\))/ 右(\(1\))端点是 \(j\),此时的最大权值。

可以启发想到这一点的例子:

\(i - 1\) 行的左右端点分别是 \(l', r'\),第 \(i\) 行左右端点是 \(l, r\)

考虑情况 \(l \lt l'\),则 \(r\) 的限制只有 \(r \ge l'\),显然没有必要记录 \(r'\),可以启发优化状态。

接下来就是分类讨论:

250910_1

case 1: \(l \lt l'\)

此时要求满足 \(r \ge l'\)

\[f_{i,l,0} = \max _ {l' = l + 1} ^ m \left( f_{i - 1, l', 0} + calc1(l, l') \right) \]

其中 \(calc1(l, r)\) 表示所有区间左端点 \(=l\),右端点 \(\ge r\) 的区间中区间和最大值。

\[\begin{align*} calc1(l,r) = \max _ {j = r} ^ m \left( s_j - s_{l - 1} \right) \\ = \max _ {j = r} ^ m \left( s_j \right) - s_{l - 1} \end{align*} \]

此时,我们可以倒序枚举 \(l\),维护 \(s\) 后缀 \(\max\)(记为 \(mxs\))、转移式中与 \(l'\) 有关的项的后缀 \(\max\) (记为 \(mx\))即可做到每次 \(O(1)\) 更新答案。

具体地,流程如下:

  • 倒序枚举 \(l\)

    • \(l + 1\) 计算入 \(s\) 的后缀 \(\max\)。(因为限制是 \(l' \gt l\),那么算入 \(mxs\) 的也需要满足 \(j \gt l\),就在枚举到 \(l\) 时加入 \(l + 1\)

    • \(f_{i - 1, l + 1, 0} + mxs\) 更新 \(mx\)。(\(l + 1\) 与上一步同理,实际上是把和 \(l'\) 有关的部分一起算)

    • \(mx - s_{l - 1}\) 更新 \(f_{i, l, 0}\)\(- s_{l - 1}\)\(mx\) 一起组成整个转移式)

case 2: \(l = l'\)

此时 \(l'\) 并不在 \((l,r)\) 中,于是不能从 \(f_{i - 1, l', 0}\) 转移过来,所以从 \(f_{i - 1, r', 1}\) 转移。

\[\begin{align*} f_{i,l,0}=\max_{r' = l} ^ m \left( f_{i-1,r',1} + calc1(l,r'+1)\right) \end{align*} \]

case 3 & 4

同理 case 1 & 2。

\(calc2(l,r)\) 表示右端点是 \(r\),左端点 \(\leq l\) 的区间的最大利润。

\[\begin{align*} calc2(l,r)=\max_{j=1}^l \left( s_r - s_{j - 1}\right) \\ =s_r - \min_{j=1}^l \left( s_{j-1}\right) \end{align*} \]

case 3:

\[f_{i,r,1}=\max_{r'=1}^{r-1}\left(f_{i-1,r',1}+calc2(r',r)\right) \]

case 4:

\[f_{i,r,1}=\max_{l'=2}^r\left(f_{i-1,l',0}+calc2(l'-1,r)\right) \]

代码

link

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int ui;
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define per(i, l, r) for(int i = l; i >= r; i--)
#define NESTED_MACRO_STR(x) #x
#define MACRO_STR(x) NESTED_MACRO_STR(x) 
inline ll read(){
	ll res = 0, flg = 1;
	char c = getchar();
    for(; c > '9' || c < '0'; c = getchar()) if(c == '-') flg = -flg;
    for(; c >= '0' && c <= '9'; c = getchar()) res = res * 10 + c - '0';
	return res * flg;
}
template <typename T> inline void write(T x, char c = '\n'){
    if(x < 0) putchar('-'), x = -x;
    static int sta[35]; int top = 0;
    do { sta[top++] = x % 10, x /= 10; } while (x);
    while (top) putchar(sta[--top] + 48);
    putchar(c);
}
void __deb__(const char *s) { cerr << '\n'; }
template <typename T, typename... Ts>
void __deb__(const char *s, T v, Ts... vs){
    const char *c = strchr(s, ',');
    string name(s, c ? c - s : strlen(s));
    if(std::is_floating_point<T>::value) {
        cerr << name << " : " << fixed << setprecision(10) << v;
    } else {
        cerr << name << " : " << v;
    }
    if(c) { cerr << " | ";  __deb__(c + 1, vs...); }
    else cerr << '\n';
}
#define deb(...) __deb__(#__VA_ARGS__, __VA_ARGS__)
template <typename T> void chkmx(T &a, T b) { a = (a > b) ? a : b; }
template <typename T> void chkmn(T &a, T b) { a = (a < b) ? a : b; }
template <typename T> T calc_mod(T x, T mod){ while(x < 0) {x += mod;} x %= mod; return x; }
template <typename T> void mod_plus(T &x, T y, T mod) { x = calc_mod(calc_mod(x, mod) + calc_mod(y, mod), mod); }
template <typename T> void mod_sub(T &x, T y, T mod) { x = calc_mod(calc_mod(x, mod) - calc_mod(y, mod), mod); }
// #define FILE_NAME (type your freopen file name here)
const ll inf = 1e18;
void solve_test_case(){
    int n = read(), m = read();
    vector<vector<ll>> a(n + 5, vector<ll> (m + 5));
    vector<vector<ll>> f[2];
    f[0] = f[1] = vector<vector<ll>> (n + 5, vector<ll> (m + 5, -inf));
    vector<ll> s(m + 5, 0);

    rep(i, 1, n){
        rep(j, 1, m){
            a[i][j] = read();
        }
    }

    rep(j, 1, m){
        s[j] = s[j - 1] + a[1][j];
    }
    ll mxs = -inf, mns = inf;
    per(l, m, 1){
        chkmx(mxs, s[l]);
        f[0][1][l] = mxs - s[l - 1];
    }
    rep(r, 1, m){
        chkmn(mns, s[r - 1]);
        f[1][1][r] = s[r] - mns;
    }
    rep(i, 2, n){
        vector<ll> s(m + 5, 0);
        rep(j, 1, m){
            s[j] = s[j - 1] + a[i][j];
        }

        ll mx, mxs, mns;

        mx = mxs = -inf, mns = inf;
        per(l, m, 1){
            if(l + 1 <= m) chkmx(mxs, s[l + 1]);
            chkmx(mx, f[0][i - 1][l + 1] + mxs);
            chkmx(f[0][i][l], mx - s[l - 1]);
        }

        mx = mxs = -inf, mns = inf;
        per(l, m, 1){
            if(l + 1 <= m) chkmx(mxs, s[l + 1]);
            chkmx(mx, f[1][i - 1][l] + mxs);
            chkmx(f[0][i][l], mx - s[l - 1]);
        }

        mx = mxs = -inf, mns = inf;
        rep(r, 1, m){
            if(r - 2 >= 0) chkmn(mns, s[r - 2]);
            chkmx(mx, f[1][i - 1][r - 1] - mns);
            chkmx(f[1][i][r], mx + s[r]);
        }

        mx = mxs = -inf, mns = inf;
        rep(r, 1, m){
            if(r - 2 >= 0) chkmn(mns, s[r - 2]);
            chkmx(mx, f[0][i - 1][r] - mns);
            chkmx(f[1][i][r], mx + s[r]);
        }
    }

    ll ans = -inf;
    rep(i, 1, m){
        chkmx(ans, max(f[0][n][i], f[1][n][i]));
    }
    write(ans);
}
signed main(){
    #ifdef FILE_NAME
        freopen((string(MACRO_STR(FILE_NAME)) + ".in").c_str(), "r", stdin);
        freopen((string(MACRO_STR(FILE_NAME)) + ".out").c_str(), "w", stdout);
    #endif
    int Test_case_num = read();
    while(Test_case_num--) solve_test_case();
    return 0;
}
posted @ 2025-09-10 23:40  lajishift  阅读(10)  评论(0)    收藏  举报