codeforces 713D Animals and Puzzle

题意:

给出n * m 的0/1矩阵,然后q组询问,问询问的矩形中包含的全1正方形的最大边长。

 

题解:

首先考虑暴力算法,对于每个询问的矩形,在里面暴力找到全1正方形,现在的问题就是怎么找全1正方形,可以递推实现,dp[i][j]表示以(i, j)作为右下角的全一正方形最长边长。

dp[i][j] = min (dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1。

while (T--) {
        int x1, y1, x2, y2;
        scanf ("%d%d%d%d", &x1, &y1, &x2, &y2);
        int ans = 0;
        for (int i = x1; i <= x2; ++i) {
            for (int j = y1; j <= y2; ++j) {
                int len = dp[i][j];
                if (i - len + 1 < x1 && j - len + 1 < y1) ans = max (ans, min(i - x1 + 1, j - y1 + 1));
                else if (j - len + 1 < y1) ans = max (ans, j - y1 + 1);
                else if (i - len + 1 < x1) ans = max (ans, i - x1 + 1);
                else ans = max (ans, len);
            }
        }
        cout << ans << endl;
    }

  

现在考虑,对每个询问的两个for循环优化:

那么现在的问题就是维护一个矩形中最大的dp值是多少,二维线段树?不不不,TLE,复杂度(T * (logn)^3),显然可以用二维ST表

但是会发现有很多情况会越过边界从而不符合条件,可以二分出矩形的全1正方形的边长,根据定义那么问题就等价于在(x1 + mid - 1, y1 + mid -1)到 (x2, y2)中寻找最大的dp值,就直接拿最大值和二分值比较就好了,就可以不用考虑是否越界的问题了。O(∩_∩)O~

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

const int N = 1e3 + 100;
int n, m, dp[11][11][N][N], T, lg[N];

void BuildSt () {
	lg[1] = 0;
	for (int i = 2; i <= 1000; ++i) lg[i] = lg[i >> 1] + 1;
	for (int x = 1; x <= n; ++x) 
		for (int i = 1; i <= 10; ++i) 
			for (int y = 1; y <= m; ++y) 
				if (y + (1<<i-1) <= m) dp[0][i][x][y] = max (dp[0][i-1][x][y], dp[0][i-1][x][y+(1<<i-1)]);
	for (int i = 1; i <= 10; ++i) 
		for (int x = 1; x <= n; ++x) 
			for (int j = 0; j <= 10; ++j) 
				for (int y = 1; y <= m; ++y) 
					if (x + (1<<i-1) <= n) dp[i][j][x][y] = max (dp[i-1][j][x][y], dp[i-1][j][x+(1<<i-1)][y]);
}

int query (int x1, int y1, int x2, int y2) {
	int lx = lg[x2 - x1 + 1], ly = lg[y2 - y1 + 1];
	x2 = x2 - (1 << lx) + 1;
	y2 = y2 - (1 << ly) + 1;
	return max (max (dp[lx][ly][x1][y1], dp[lx][ly][x1][y2]), max (dp[lx][ly][x2][y1], dp[lx][ly][x2][y2]));
}

int main () {
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			int x;
			scanf ("%d", &x);
			if (x == 1)dp[0][0][i][j] = min (dp[0][0][i-1][j], min (dp[0][0][i][j-1], dp[0][0][i-1][j-1])) + 1;	
		}
	}
	BuildSt();
	scanf ("%d", &T);
	while (T--) {
		int x1, y1, x2, y2;
		scanf ("%d%d%d%d", &x1, &y1, &x2, &y2);
		int l = 0, r = min (x2 - x1 + 1, y2 - y1 + 1);
		while (l < r) {
			int mid = l + r + 1 >> 1;
			if (query(x1 + mid - 1, y1 + mid - 1, x2, y2) >= mid) l = mid;
			else r = mid - 1;
		}
		cout << l << endl;
	}
	return 0;
}

  

总结:

蒟蒻涨姿势了~学到了二维的ST,还有这道题的边界处理也非常巧妙,而且我不知道下次我能不能想到,总之先记住这个套路吧。。。

posted @ 2016-10-25 17:45  xgtao  阅读(221)  评论(0编辑  收藏  举报