[二维DP] 洛谷P1736 创意吃鱼法(预处理)

题目

LP1736

思路

本题作为一道提高+/省选-的题,基本思路是自己想出来的,拿了90,然后有一个小bug一直没发现,拿来标程对比,改了这个bug然后AC,还是很开心的。


首先,借鉴最大正方形的经验,这种题应该下手画图,不出意外,通过画图找到了状态转移的规律。
首先我们将d(i,j)定义为,(i,j)为一个对角线的右下角或左下角时,最长的对角线代表正方形内无其它1的对角线长度。
这里我们先拿右下角分析。
这里写图片描述
我们定义lf[i][j],元素(i,j)的左边第一个1的横坐标,uf[i][j],元素(i,j)上边第一个1的纵坐标。由图可知,设对角线左上角坐标为(a,b),d[i][j] = d[i-1][j-1]+1当且仅当uf[i][j] < a && lf[i][j] < b。而此处对角线左上角的坐标,可以根据d[i-1][j-1]的值来确定。这就明确了状态转移的条件。
拿左下角分析时,有两种方法,要么直接将图像左右反转,然后再用一遍之前的代码,要么再根据uf和lf制定另一个状态转移条件判断。第一种是偷懒的办法。。我之前试了。。洛谷最后一个点会TLE,第二种用uf和lf套来套去,在此处就不说了。


确定对角线另一端的坐标。
可以根据tl=d[i-1][j-1]或d[i-1][j+1],而l [1,tl],将l从大到小遍历,每次的另一端坐标就是d[i-l][j-l]或d[i-l][j+l]。
注意此处不能直接用tl而不用l遍历小于等于tl的每一个值,本题只有一个数据点能反映这个事实,这也是我得90的bug,考虑如下情况:
0 0 0 0 0 0 0
1 1 0 0 1 0 0
0 0 1 0 0 0 0
0 0 0 1 0 0 0
1 0 0 0 1 0 0
0 1 0 0 0 1 0
1 0 0 0 0 0 1
显然在考虑加粗体1时,如果直接用tl=4,直接就否定了这个点作为右小角端点的权利。而l=3时,这个点可以,从而在整个图形中得到了长度为5的合法对角线。

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;

const int maxn = 2500 + 10;
int n, m, G[maxn][maxn], d[maxn][maxn], lf[maxn][maxn], uf[maxn][maxn];

int main() {
    scanf("%d%d", &n, &m);
    _rep(i, 1, n) _rep(j, 1, m) scanf("%d", &G[i][j]);

    // 打表lf和uf
    int last;
    _rep(i, 1, n) {
        last = 0;
        _rep(j, 1, m)
            if (G[i][j] == 1) {
                lf[i][j] = last;
                last = j;
            }
            else {
                lf[i][j] = last;
            }
    }

    _rep(j, 1, m) {
        last = 0;
        _rep(i, 1, n)
            if (G[i][j] == 1) {
                uf[i][j] = last;
                last = i;
            }
            else {
                uf[i][j] = last;
            }
    }

    int ans = 0;
    // (i,j)作为一个对角线的右下角时
    _rep(i,1,n)
        _rep(j, 1, m)
            if (G[i][j] == 1) {
                d[i][j] = 1;
                int tl = d[i - 1][j - 1];
                for (int l = tl; l >= 1; l--) {
                    if (uf[i][j] < i - l && lf[i][j] < j - l) {
                        d[i][j] = l + 1;
                        break;
                    }
                }
                ans = max(ans, d[i][j]);
            }

    //(i,j)作为一个对角线的左下角时
    _rep(i, 1, n)
        for(int j = m; j>=1; j--)
            if (G[i][j] == 1) {
                d[i][j] = 1;
                int tl = d[i - 1][j + 1];
                for (int l = tl; l >= 1; l--) {
                    if (uf[i][j + l] == i - l && lf[i][j + l] == j && uf[i][j] < i - l && lf[i - l][j + l] < j) {
                        d[i][j] = l + 1;
                        break;
                    }
                }
                ans = max(ans, d[i][j]);
            }


    printf("%d\n", ans);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/icecab/article/details/81011092