EOJ 1065. 糖果盒(悬线法)

一个被分为 n*m 个格子的糖果盒,第 i 行第 j 列位置的格子里面有 a [ i ][ j ] 颗糖。本来 tenshi 打算送这盒糖果给某 PPMM 的,但是就在要送出糖果盒的前一天晚上,一只极其可恶的老鼠夜袭糖果盒,有部分格子被洗劫并且穿了洞。tenshi 必须尽快从这个糖果盒里面切割出一个矩形糖果盒,新的糖果盒不能有洞,并且 tenshi 希望保留在新糖果盒内的糖的总数尽量多。

请帮 tenshi 设计一个程序 计算一下新糖果盒最多能够保留多少糖果。

输入格式
从标准输入读入数据。第一行有两个整数 n、m。第 i + 1 行的第 j 个数表示 a [ i ][ j ],如果这个数为 0 ,则表示这个位置的格子被洗劫过。其中:

1 ≤ n,m ≤ 1000

0 ≤ a [ i ][ j ]≤ 255

输出格式
输出最大糖果数到标准输出。

样例
input
3 4
1 2 3 4
5 0 6 3
10 3 4 0
output
17

思路:
https://www.cnblogs.com/lxyyyy/p/11376224.html#_label3_0_4_2
这篇论文里的题

使用悬线法,枚举极大子矩阵然后求这个子矩阵的和,复杂度是n^2的
不过你也可以把坏点设成-INF,然后dp求最大子矩阵和;但这个算法貌似只能n^3。

#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;
const int maxn = 1005;

int a[maxn][maxn],dp[maxn][maxn];
int L[maxn][maxn],R[maxn][maxn],Up[maxn][maxn];

int main() {
    int n,m;scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++) {
        for(int j = 1;j <= m;j++) {
            scanf("%d",&a[i][j]);
            dp[i][j] = a[i][j] + dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1];
        }
    }

    int ans = 0;
    for(int i = 1;i <= n;i++) {
        int l = 0,r = m + 1;
        for(int j = 1;j <= m;j++) {
            if(a[i][j] == 0) {
                l = j;
            } else {
                L[i][j] = i == 1 ? l + 1 : max(l + 1,L[i - 1][j]);
                Up[i][j] = i == 1 ? 1 : Up[i - 1][j] + 1;
            }
        }

        for(int j = m;j >= 1;j--) {
            if(a[i][j] == 0) {
                r = j;
                R[i][j] = m + 1;
            } else {
                R[i][j] = i == 1 ? r - 1 : min(r - 1,R[i - 1][j]);
                int x = i,y = R[i][j];
                int x1 = i - Up[i][j],y1 = L[i][j] - 1;
                ans = max(ans,dp[x][y] - dp[x][y1] - dp[x1][y] + dp[x1][y1]);

            }
        }
    }

    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(#,优化dp)