首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

BZOJ 1084 [SCOI2005] 最大子矩陣:動態規劃與前綴和優化

     編輯:關於C++

問題描述

給定一個 n × m 的矩陣(1 ≤ n ≤ 100, 1 ≤ m ≤ 2, 1 ≤ k ≤ 10),需選出 k 個不重疊的子矩陣,使其分值總和最大。子矩陣不可相互重疊,元素分值絕對值不超過 32767。

輸入輸出格式

  • 輸入:第一行 n, m, k,接著 n 行矩陣元素分值。
  • 輸出:k 個子矩陣分值之和的最大值。

範例

Input:
3 2 2
1 -3
2 3
-2 3

Output:
9

解題思路:動態規劃 + 二維前綴和

本題屬於經典區間 DP,利用二維前綴和快速計算任意子矩陣的分值和,再以 DP 狀態 f[i][j] 或 g[i][j][l] 表示前 i 行、選 j 個子矩陣的最大值。由於 m ≤ 2,可分情況討論優化。

狀態定義

  • m = 1:退化為一維區間選取,f[i][j] 表示前 i 行選 j 段的最大值。
  • m = 2:需考慮兩欄的組合,g[i][j][l] 表示前 i 行第一欄、前 j 行第二欄選 l 段的最大值。

轉移方程

利用前綴和 s[i][col] 快速得到區間和,枚舉轉移點 h 更新 DP 值:

f[i][j] = max(f[i-1][j], f[h][j-1] + s[i][1] - s[h][1])  (0 ≤ h < i)
g[i][j][l] = max(
    g[i-1][j][l], g[i][j-1][l],
    g[h][j][l-1] + s[i][1] - s[h][1],
    g[i][h][l-1] + s[j][2] - s[h][2],
    (i==j) ? g[h][h][l-1] + s[i][1]-s[h][1] + s[j][2]-s[h][2] : -inf
)

完整程式碼 (C++)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
const int maxn = 105;
int n, m, k;
int f[maxn][15], g[maxn][maxn][15], s[maxn][3];

inline int read() {
    int x=0, f=1; char ch=getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0' && ch<='9') { x=x*10+ch-'0'; ch=getchar(); }
    return x*f;
}

int main() {
    n=read(); m=read(); k=read();
    memset(s, 0, sizeof(s));
    F(i,1,n) F(j,1,m) s[i][j] = s[i-1][j] + read();

    if (m == 1) {
        memset(f, 0, sizeof(f));
        F(i,1,n) F(j,1,k) {
            f[i][j] = f[i-1][j];
            F(h,0,i-1) f[i][j] = max(f[i][j], f[h][j-1] + s[i][1] - s[h][1]);
        }
        printf("%d\n", f[n][k]);
    } else {
        memset(g, 0, sizeof(g));
        F(i,1,n) F(j,1,n) F(l,1,k) {
            g[i][j][l] = max(g[i-1][j][l], g[i][j-1][l]);
            F(h,0,i-1) g[i][j][l] = max(g[i][j][l], g[h][j][l-1] + s[i][1] - s[h][1]);
            F(h,0,j-1) g[i][j][l] = max(g[i][j][l], g[i][h][l-1] + s[j][2] - s[h][2]);
            if (i == j) F(h,0,i-1) g[i][j][l] = max(g[i][j][l], g[h][h][l-1] + s[i][1]-s[h][1] + s[j][2]-s[h][2]);
        }
        printf("%d\n", g[n][n][k]);
    }
    return 0;
}

複雜度分析

  • 時間複雜度:O(n³·k),在 n≤100, k≤10 範圍內可通過。
  • 空間複雜度:O(n²·k)。

關鍵點總結

  1. 二維前綴和實現 O(1) 區間求和。
  2. 針對 m=1/2 分類討論,降低狀態維度。
  3. DP 轉移時枚舉分割點,確保子矩陣不重疊。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved