【DP】Sgu250 Constructive Plan(修改版)

声明

此题解并不是Sgu250原题,输出与原题不同

原题面

Description

艺术节就要开始了,但是 Jolin是一个很臭屁的人,她要求把舞台设置成 C字形
的。艺术节的会场是一个 n*m的矩阵,有的地方安排了座位,被标记为 1;还有
些地方是空地,被标记为 0。只有在空地才能搭建舞台。
C字形的舞台是这样的舞台:
1.它由从上到下排开的矩形组成
2.三个矩形的左边界相同
3.第一个矩形的下边界和第二个矩形的上边界,第二个矩形的下边界和第三个
矩形的上边界相邻。
4.第二个矩形的右边界必须小于第一个和第二个矩形的右边界。
作为疯狂迷恋 Jolin的Fans,你希望让舞台尽量的大。

Input

输入数据第一行是两个数 n, m(1<=n,m<=200)。
接下来是一个 n*m的矩阵,是 0或者 1。意义如上所述

Output

输出舞台最大面积。

Sample Input

input 1
8 7
1 1 1 1 1 1 1
1 1 0 0 0 0 1
1 1 0 0 0 0 1
1 1 0 0 0 0 1
1 1 0 0 0 0 1
1 1 0 0 0 0 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1

input 2
8 8
1 1 1 1 1 1 1 1
1 0 0 0 0 1 0 1
1 1 1 1 0 0 0 1
1 0 0 0 0 0 0 1
1 1 0 0 0 0 0 1
1 0 1 0 1 0 0 1
1 0 0 0 0 0 1 1
1 1 1 1 1 1 1 1

Sample Output

output1
19
 
output 2
12

分析

题意比较清晰

首先,我们进行一波预处理

g[ i ][ k ]表示以第i行为上边界,以第k行为下边界的矩形向右延伸的最大宽度

n1[ i ][ k ]表示以第i行为上边界,下边界∈[ i, k ]的矩形中最大的矩形的下边界行数

n2[ i ][ k ]表示以第k行为下边界,上边界∈[ 1, i ]的矩形中最大的矩形的上边界行数

大致流程

枚举中间矩形的上边界i

    枚举下边界k,那么中间矩形的宽度就是g[ i ][ k ]

        分别二分答案向上和向下的最大矩形的宽度,若宽度小于中间矩形宽度则进入下一重循环

    更新答案

        若中间矩形的宽度等于上面或者下面的矩形宽度

            若中间矩形宽度<=1或者当前情况下的三个矩形面积之和只比ans大1,进入下一重循环

            (因为我们要把中间矩形的宽度减一)

        否则直接更新最大值

结束

代码

#include<bits/stdc++.h>
using namespace std;
int n, m;
int p[210][210], g[210][210];
int n1[210][210], n2[210][210];
int ans;
 
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 << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
    return x * f;
}
 
int main() {
    n = read(), m = read();
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            p[i][j] = read();
        }
    }
    for(int j = m; j >= 1; j--) {
        for(int i = 1; i <= n; i++) {
            int k = i;
            while (k <= n && !p[k][j]) g[i][k++]++;
            while (k <= n) g[i][k++] = 0;
        }
        for(int i = 1; i <= n; i++) {
            n1[i][i] = i;
            for(int k = i + 1; k <= n; k++) {
                if(g[i][n1[i][k - 1]] * (n1[i][k - 1] - i + 1) < g[i][k] * (k - i + 1)) n1[i][k] = k;
                else n1[i][k] = n1[i][k-1];
            }
        }
        for(int k = n; k >= 1; k--) {
            n2[k][k] = k;
            for(int i = k - 1; i >= 1; i--) {
                if(g[n2[i + 1][k]][k] * (k - n2[i + 1][k] + 1) < g[i][k] * (k - i + 1)) n2[i][k] = i;
                else n2[i][k] = n2[i + 1][k];
            }
        }
        for(int i = 1; i <= n; i++) if(g[i][i]) {
            for(int k = i + 2; k <= n && g[k][k]; k++) {
                int a, b, c, all, low, high, mid, U, D;
                b = g[i + 1][k - 1];
                if(b <= 0) break;
                b = min(b, min(g[i][i], g[k][k]));
                low = 1;
                high = i;
                while(low <= high) {
                    mid = (low + high) >> 1;
                    if(g[n2[mid][i]][i] >= b) high = mid - 1, a = mid;
                    else low = mid + 1;
                }
                if(g[n2[a][i]][i] < b) continue;
                low = k;
                high = n;
                while(low <= high) {
                    mid = (low + high) >> 1;
                    if(g[k][n1[k][mid]] >= b) low = mid + 1, c = mid;
                    else high = mid - 1;
                }
                if(g[k][n1[k][c]] < b) continue;
                U = n2[a][i], D = n1[k][c];
                a = g[U][i], c = g[k][D];
                all = a * (i - U + 1) + b * (k - i - 1) + c * (D - k + 1);
                if(b == a || b == c) {
                    if(b <= 1 || --all <= ans) continue;
                    ans = all;
                } else if(all > ans) ans = all;
            }
        }
    }
    printf("%d", ans);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值