Problem Description
Damon loves to take photos of the places he visits during his travels, to put them into frames. All of his photos are in a square format of N x N pixels. He brought back beautiful pictures of the many monuments in Paris, such as the Eiffel Tower or the Louvre, but unfortunately, when he got back home, he realized that all his pictures were blurred on the edges. Looking closely, Damon realizes that he can easily distinguish the blurred pixels from the “good” (i.e., non-blurred) ones and that, luckily, all the non-blurred pixels are connected in such a way that any horizontal or vertical line drawn between two non-blurred pixels goes only through non-blurred pixels. In order to get the best from his failed pictures, he decides to cut out the biggest possible picture without any blurred pixel from each of his photos. And since his frames are all squares, for aesthetic reasons, the cut-out pictures have to be squares too. Damon does not want his pictures to be tilted so he wants the sides of the cut-outs to be parallel to the sides of the original picture.
Important Note
• In the input picture, each row and each column has at least one non-blurred pixel.
• In any two consecutive lines, there are at least two non-blurred pixels in the same column.
The input comprises several lines, each consisting of integers separated with single spaces:
• The first line contains the length N, in pixels, of the input photo;
• Each of the next N lines contains two integers ai and bi, the indices of the first (a_i) and the lasst (b_i) non-blurred pixel on the i-th line.
Limits
0<N <=100000
0<=ai<=bi<N
Brief Description
给定一幅N*N的画,每一行从第ai个像素到第bi个像素是非模糊点
要求在这副画中找到的最大的正的正方形,并且正方形中的像素都是非模糊点。
Solution
注意到 "luckily, all the non-blurred pixels are connected in such a way that any horizontal or vertical line drawn between two non-blurred pixels goes only through non-blurred pixels. "
因此我们可以很快地找到对于给定的高和位置,最宽能够找到多宽的矩形。
例如右下图,对于给定绿色的高
只需要查找绿色区间中,
最右的左端点和最左的右端点
就可以找到最大的矩形
此时是1*6的矩形
由于题目要求都是正方形,因此可以通过二分列举高,然后用刚刚说的方法找到最大的边长
区间最值可用RMQ求
Query:
hasValidSquare:
AC Code:
/*
* Copyright (c) 2019 Ng Kimbing, HNU, All rights reserved. May not be used, modified, or copied without permission.
* @Author: Ng Kimbing, HNU.
* @LastModified:2019-07-27 T 22:39:47.939 +08:00
*/
package ACMProblems.Summer2019.HNUSummer;
import static ACMProblems.ACMIO.*;
public class BlurredPic {
//Code for IO has been omitted
private static final int maxN = 100005;
private static int[] a = new int[maxN];
private static int[] b = new int[maxN];
private static int[][] dpA = new int[maxN][30];
private static int[][] dpB = new int[maxN][30];
private static int[] mm = new int[maxN];
private static int n;
/**
* 初始化dp数组
* dpA维护每行的左边界最大值
* dpB维护每行的右边界最小值
*/
private static void buildDp() {
mm[0] = -1;
for (int i = 1; i <= n; i++) {
mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
dpA[i][0] = a[i];
dpB[i][0] = b[i];
}
//dp[i][j] denotes the maximum/minimum of 2^j consecutive numbers starting from the ith bit
for (int j = 1; j <= mm[n]; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
dpA[i][j] = Math.max(dpA[i][j - 1], dpA[i + (1 << (j - 1))][j - 1]);
dpB[i][j] = Math.min(dpB[i][j - 1], dpB[i + (1 << (j - 1))][j - 1]);
}
}
}
private static int rangeMinQuery(int left, int right) {
int k = mm[right - left + 1];
return Math.min(dpB[left][k], dpB[right - (1 << k) + 1][k]);
}
private static int rangeMaxQuery(int left, int right) {
int k = mm[right - left + 1];
return Math.max(dpA[left][k], dpA[right - (1 << k) + 1][k]);
}
private static boolean hasValidSquare(int height) {
int lMax, rMin;
for (int i = height; i <= n; ++i) {
//区间内最右起点
lMax = rangeMaxQuery(i - height + 1, i);
//区间内最左终点
rMin = rangeMinQuery(i - height + 1, i);
int maxWidth = rMin - lMax + 1;
if (maxWidth >= height)
return true;
}
return false;
}
public static void main(String[] args) throws Exception {
n = nextInt();
for (int i = 1; i <= n; ++i) {
a[i] = nextInt() + 1;
b[i] = nextInt() + 1;
}
buildDp();
int l = 0, r = n + 1;
while (r > l) {
int mid = (l + r + 1) >> 1;
if (hasValidSquare(mid))
l = mid;
else r = mid - 1;
}
System.out.println(r);
}
}
复杂度 O(nlogn)
7/28/2019
今天发现个O(n)的做法 厉害厉害:
O(n)解法:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 50;
int n;
int L[maxn], R[maxn];
bool check(int i, int k){
if(R[i] - L[i] + 1 < k) return false;;
if(i + k - 1 > n) return false;
int d = i + k - 1;
int l = max(L[i], L[d]);
if(R[i] - l + 1 >= k && R[d] - l + 1 >= k) return true;
else return false;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d%d", &L[i], &R[i]);
int ans = 1;
for(int i = 1; i <= n; ++i) while(check(i, ans+1)) ans = ans+1;
cout<<ans<<endl;
}