HNU2019 Summer Training 3 E. Blurred Pictures

在这里插入图片描述

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;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值