sgu250:Constructive Plan(单调性乱搞)

题目大意:

给出一个n?m的01矩阵,0表示不能放,1表示能放,在其中放入三个矩形,要求满足如下条件:

1.每个矩形面积大于0。

2.这些矩形必须是一个联通块,矩形之间不能重叠。

3.矩形的左边界在同一条线上。

4.中间矩形的横向长度小于两边矩形的横向长度。

求出最大的三个矩形的总面积,无解输出?1。

分析:

我是个蒟蒻,TAT,想了一个晚上+中午+n个课间才想出来怎么做…

首先我们枚举左边界j,O(n)。

然后我们枚举中间矩形的上下边界p,q,O(n2)。

现在已经O(n3)了,然而解法也是O(n3)的(有没有感觉很神奇...)。

首先我们得有一些必要的预处理:rmi,j表示点(i,j)向右能到达的最远距离,minrj,p,q表示第j列以p→q行为左边界的矩形向右能到达的最远距离。

设上矩形的上边界为x,下矩形的下边界为y。

思考一下发现我们要考虑两种情况,一个是中间矩形的minrj,p,q≥minrj,x,p?1且minrj,p,q≥minrj,q+1,y,另一个是<。

先考虑第二种情况,这种情况下我们不需要缩减中间矩形的横向长度。(另一种情况会被上下矩形压制得横向长度减小)

我们先确定中间矩形的上边界p,随着下边界q的下移,minrj,p,q会呈非递增增长,下面矩形的minrj,q+1,y会呈非递减增长,这时我们下面矩形的下边界y的范围可以下移,此时我们要求的就是这个范围内的下面矩形的最大面积值,然而这个是可以预处理的,就是预处理maxdj,q+1,y表示以第j列为左边界且以q+1为上边界,下边界在[q+1,y]范围内的矩形面积最大值,则maxdj,q+1,y=max(maxdj,q+1,y,(y?q)minrj,q+1,y?1),这个预处理是O(n3)的,不虚啊,同样我们可以预处理出类似的上面矩形对应的maxuj,q+1,y,这样我们就可以在O(1)的时间内算出面积最大值,这样我们就解决了这个问题了。

回过头来考虑第一种情况,因为我们被迫缩减中间矩形的横向长度,因此在确定x,y,p,q后,把中间矩形的宽度变小(即把p++或q??后),总面积会变大,这个可以自己想一下(很显然的...),由此我们很显然可以把中间矩形的宽度设为1时,肯定是面积最大的(此时的时间要从枚举第j列算起)。

我们枚举中间矩形的那一行p,再枚举这个矩形缩减后的长度为t,预处理上面矩形的长度大于t的最大矩形面积maxu2j,p?1,t,下面矩形的长度大于t的最大矩形面积maxd2j,p+1,t,这个预处理也是O(n3)的,不虚啊。

总之就差不多了,头一次写这么多,也不知道讲没讲清…能不能看懂看造化了…

AC code:

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

const int MAXN = 189;

int n, m;
int g[MAXN][MAXN];
int rm[MAXN][MAXN];
int minr[MAXN][MAXN];

struct data
{
    int c, p1, p2, p3, p4, l1, l2, l3, s;
    data(int c=0, int p1=0, int p2=0, int p3=0, int p4=0, int l1=0, int l2=0, int l3=0, int s=0):c(c),p1(p1),p2(p2),p3(p3),p4(p4),l1(l1),l2(l2),l3(l3),s(s){}
}ans;

struct data2
{
    int p, l, s;
    data2(int p=0, int l=0, int s=0):p(p),l(l),s(s){}
    void init() {p = l = s = 0;}
};

data2 maxu[MAXN][MAXN];
data2 maxd[MAXN][MAXN];
data2 maxu2[MAXN][MAXN];
data2 maxd2[MAXN][MAXN];

void work_rm()
{
    for(int j = m; j >= 1; --j)
        for(int i = 1; i <= n; ++i)
            if(!g[i][j])
                rm[i][j] = rm[i][j+1]+1;
}

void work_minr(int j)
{
    for(int i = 1; i <= n; ++i)
    {
        minr[i][i] = rm[i][j];
        for(int k = i+1; k <= n; ++k)
            minr[i][k] = min(minr[i][k-1], rm[k][j]);
    }
}

void work_maxu()
{
    for(int i = 1; i <= n; ++i)
    {
        maxu[i][i] = data2(i, minr[i][i], minr[i][i]);
        for(int k = i-1; k >= 1; --k)
            if(maxu[i][k+1].s > (i-k+1)*minr[k][i])
                maxu[i][k] = maxu[i][k+1];
            else maxu[i][k] = data2(k, minr[k][i], (i-k+1)*minr[k][i]);
    }
}

void work_maxd()
{
    for(int i = n; i >= 1; --i)
    {
        maxd[i][i] = data2(i, minr[i][i], minr[i][i]);
        for(int k = i+1; k <= n; ++k)
            if(maxd[i][k-1].s > (k-i+1)*minr[i][k])
                maxd[i][k] = maxd[i][k-1];
            else maxd[i][k] = data2(k, minr[i][k], (k-i+1)*minr[i][k]);
    }
}

void work_maxu2()
{
    for(int i = 1; i <= n; ++i)
    {
        int u = i;
        for(int j = m; j >= 1; --j)
        {
            maxu2[i][j].init();
            u = min(i, u);
            while(u && minr[u][i] > j) u--;
            u++;if(u > i) continue;
            maxu2[i][j] = maxu[i][u];
        }
    }
}

void work_maxd2()
{
    for(int i = n; i >= 1; --i)
    {
        int d = i;
        for(int j = m; j >= 1; --j)
        {
            maxd2[i][j].init();
            d = max(i, d);
            while(d <= n && minr[i][d] > j) d++;
            d--;if(d < i) continue;
            maxd2[i][j] = maxd[i][d];
        }
    }
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    #endif

    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
            scanf("%d", &g[i][j]);
    work_rm();
    for(int j = 1; j <= m; ++j)
    {
        work_minr(j), work_maxu(), work_maxd();
        for(int p = 2; p <= n-1; ++p)
        {
            int u = p-1, d = p+1;
            for(int q = p; q <= n-1 && minr[p][q]; ++q)
            {
                u = min(p-1, u), d = max(q+1, d);
                while(u && minr[u][p-1] > minr[p][q]) u--;u++;if(u >= p) continue;
                while(d <= n && minr[q+1][d] > minr[p][q]) d++;d--;if(d <= q) continue;
                if(ans.s < (q-p+1)*minr[p][q]+maxu[p-1][u].s+maxd[q+1][d].s)
                    ans = data(j, maxu[p-1][u].p, p, q+1, maxd[q+1][d].p, maxu[p-1][u].l, minr[p][q], maxd[q+1][d].l, (q-p+1)*minr[p][q]+maxu[p-1][u].s+maxd[q+1][d].s);
            }
        }
        work_maxu2(), work_maxd2();
        for(int p = 2; p <= n-1; ++p)
            if(minr[p][p] > 1)
                for(int q = 1; q < minr[p][p]; ++q)
                    if(maxu2[p-1][q].s && maxd2[p+1][q].s && ans.s < q+maxu2[p-1][q].s+maxd2[p+1][q].s)
                        ans = data(j, maxu2[p-1][q].p, p, p+1, maxd2[p+1][q].p, maxu2[p-1][q].l, q, maxd2[p+1][q].l, q+maxu2[p-1][q].s+maxd2[p+1][q].s);
    }
    for(int i = ans.p1; i < ans.p2; ++i)
        for(int j = 1; j <= ans.l1; ++j)
            g[i][ans.c+j-1] = 8;
    for(int i = ans.p2; i < ans.p3; ++i)
        for(int j = 1; j <= ans.l2; ++j)
            g[i][ans.c+j-1] = 8;
    for(int i = ans.p3; i <= ans.p4; ++i)
        for(int j = 1; j <= ans.l3; ++j)
            g[i][ans.c+j-1] = 8;
    if(ans.s)
    {
        printf("%d\n", ans.s);
        for(int i = 1; i <= n; ++i)
        {
            for(int j = 1; j <= m; ++j)
            {
                printf("%d", g[i][j]);
                if(j < m) printf(" ");
            }
            puts("");
        }
    }
    else puts("-1");

    #ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    #endif
    return 0;
}
时间: 2024-10-14 09:07:08

sgu250:Constructive Plan(单调性乱搞)的相关文章

sgu-250 Constructive Plan

题目大意: 给你一个N?M的{0,1}矩阵,然后只有为0的地方可以可用,然后要你找出一个最大的C,输出所占大小,并且在原图中把所占的地方改成8,然后输出新的图. PS:C的定义就是从上到下三个大小相邻且不为0的矩形,左边界需要对齐,并且上面和下面的矩形的右边界都要大于中间的矩形的右边界. 解题思路: 这道题我只想出了O(N3logN)的做法,虽然可以过,但是我写到一半写错了,然后偶然间看到了翱犇的代码,数了一下发现只有3个循环,"卧槽,N3,我于是赶快去膜拜了一下". O(N3logN

[ACM] poj 2823 Sliding Window(单调队列)

Sliding Window Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 36212   Accepted: 10723 Case Time Limit: 5000MS Description An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left

POJ2082---Terrible Sets(单调栈)

Description Let N be the set of all natural numbers {0 , 1 , 2 , - }, and R be the set of all real numbers. wi, hi for i = 1 - n are some elements in N, and w0 = 0. Define set B = {< x, y > | x, y ∈ R and there exists an index i > 0 such that 0 &

qscoj#19D(单调队列)

题目链接:http://qscoj.cn/problem/130/ 题意:中文题诶- 思路:直接用单调栈搞一下就好了 代码: 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int MAXN=1e6+10; 5 const int inf=1e9; 6 int sum[MAXN], q[MAXN]; 7 8 int main(void){ 9 int n, k; 10 while(scanf("%d%d"

BZOJ_1096_[ZJOI2007]_仓库建设_(斜率优化动态规划+单调队列+特殊的前缀和技巧)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1096 有\(n\)个工厂,给出第\(i\)个工厂的到1号工厂的距离\(x[i]\),货物数量\(p[i]\),建设仓库所需花费\(c[i]\). 现在要把所有货物都装入仓库,第\(i\)号工厂的货物可以选择在\(i\)建仓库并存入,或者移动到\(k\)号仓库\((i<k<=n)\).移动的花费为数量与距离的乘积. 分析 我们来想一想dp方程. 用\(dp[i]\)表示前\(i\)个工厂,且

Codeforces 873F Forbidden Indices 字符串 SAM/(SA+单调栈)

原文链接https://www.cnblogs.com/zhouzhendong/p/9256033.html 题目传送门 - CF873F 题意 给定长度为 $n$ 的字符串 $s$,以及给定这个字符串每一个位置是否 "禁止结尾" 的信息. 一个字符串 $a$ 的价值为 $|a|\times f(a)$ . 其中 $f(a)$为 $a$ 在 $s$ 中的匹配次数(如果匹配的结尾为禁止结尾点,那么不算匹配成功) 问在所有的字符串 $a$ 中,$\max(|a|\times f(a)$

noi.ac #289. 电梯(单调队列)

题意 题目链接 Sol 傻叉的我以为给出的\(t\)是单调递增的,然后\(100\rightarrow0\) 首先可以按\(t\)排序,那么转移方程为 \(f[i] = min_{j=0}^{i-1}(max(t[i], f[j]) + 2 * max_{k=j+1}^i x[k])\) 不难发现,若\(i < j\)且\(x[i] < x[j]\),那么从\(i\)转移过来一定是不优的,一定是从\(i\)之前的某个位置转移过来.(f单增) 然后直接单调队列搞一搞就行了, #include&l

Gym - 101234J Zero Game (单调队列优化dp)

题意:有一个长度为n的01序列,你可以移动k次,每次将一个数移到任意一个位置,求经过操作后区间连续最大的连续0的个数. “移动”操作看似情况很复杂,不好讨论,但其实无非就两种情况: 一.移动的是1:显然最优的策略是将1移动到最边上(相当于“移走”),目的是将两段连续的0合并. 二.移动的是0:最优策略是将小堆中的0移动到大堆里,目的是增加大堆中0的个数. 这样一来,情况就简单多了,问题转化成了求“将一段连续区间中的0合并,然后剩下的操作次数用于把其他地方的0引进来”的最优解,即求$min(max

一些字符串有关的题目

模板可以在上一篇文章中找到. 因为最近都没有做codeforces,所以这篇文章的主要题目来源就是codeforces啦~ 需要这类题目可以在codeforces上找到hashing.string suffix structures之类的标签. 这些题目都是随便点的,所以有些题目和字符串并没有太大的关系 CF653F Paper Task(非常规比赛) 给一个长度为n的由左右括号做成的字符串,求它子串中不同括号序列的个数. (注意不是求是合法括号序列的子串数量,而是不同括号序列个数) 1<=n<