"Algorithm Competition 300 Quick Questions" One question per day: "Water"

" Algorithm Competition: 300 Quick Questions " will be published in 2024 and is an auxiliary exercise book for "Algorithm Competition" .
All questions are placed in the self-built OJ New Online Judge .
Codes are given in three languages: C/C++, Java, and Python. The topics are mainly mid- to low-level topics and are suitable for entry-level and advanced students.


" Water ", link: http://oj.ecustacm.cn/problem.php?id=1902

Question description

[Problem description] The coordinates of N drops of water are given, y represents the height of the water drop, and x represents the position where it falls to the x-axis.
  Each drop of water falls at a rate of 1 unit of length per second.
  You need to place the flowerpot at a position on the x-axis such that the time difference between the first drop of water caught by the flowerpot and the end of the last drop of water caught by the flowerpot is at least D.
  We consider that as long as the water drop falls on the x-axis, is aligned with the edge of the flower pot, or is in the flower pot, it is considered to be caught.
  Given the coordinates of N dripping water and the size of D, please calculate the width W of the smallest flowerpot.
Insert image description here

[Input format] Line 1: two integers N and D, 1 <= N <= 100,000, 1 <= D <= 1,000,000.
  Next N lines: Each line contains two integers x and y, representing the coordinates of the raindrop, 0 <= x, y <= 1,000,000.
[Output format] Only one integer in one line, indicating the width of the smallest flower pot.
  If it is impossible to construct a flowerpot wide enough to catch the required water droplets in D units of time, then output −1.
【Input sample】

4 5
6 3
2 4
4 10
12 15

【Output sample】

2

answer

   The meaning of the question is a bit confusing, so I’ll explain it with an example. There are n = 4 water droplets. Place the flowerpot somewhere to catch the water droplets. From the first water droplet to the last water droplet, the total time for all the water droplets falling on the flowerpot is required to exceed d = 6 seconds. . Ask what the minimum width of a flower pot is. The answer is to choose the first water drop (6, 3) and the third water drop (4, 10). The time difference between them falling into the flower pot is 10 - 3 = 7. If it exceeds 6 seconds, the width of the flower pot is 6 - 4 =2.
   The following outlines the meaning of the question. Select an arbitrary interval (flower pot width) [L, R], and count the difference between the maximum value and the minimum value (the highest and lowest water droplet) in this interval. If ≥ d, it is called a legal interval, and the interval width is recorded. Traverse all such legal intervals and find the minimum width, which is the answer.
   For interval problems, you can use the ruler method and use the "sliding window" formed by the fast and slow pointers to traverse all intervals, and the calculation complexity is O ( n 2 ) O (n^2)O ( n2 ), timeout. This question requires at leastO (nlogn) O(nlogn)Algorithm of O ( n log n ) complexity .
   In addition to the ruler method, there are also monotonic queues for using sliding windows to traverse intervals. There is a similar example question "Luogu P1886"on page 10 of "Algorithm Competition"Given a fixed window width k, it is required to output the maximum and minimum values ​​in all intervals where the window width is equal to k. Solved with a monotonic queue, the complexity is onlyO ( n ) O(n)O ( n ) . Please read this section carefully to understand why the complexity is O ( n ) O(n)O ( n ) ._
   This question is the same as Luogu P1886 in finding the maximum and minimum values ​​within the window. The difference is that this question does not give a fixed window width. Then use the dichotomy method to guess the smallest k: guess a window width k each time, use the function check(k) to determine whether there is a legal interval when the window width is k, and the function check() uses a monotonic queue to solve the problem. The computational complexity of "bisection method + monotonic queue", binary method guessingO (logn) O(logn)O ( l o g n ) times, each time using check() to find the maximum and minimum values ​​of all intervals with width k isO ( n ) O(n)O ( n ) , total complexityO ( nlogn ) O(nlogn)O ( nlogn ) . _ _ _ _
   The monotonic queue in the code is handwritten (for handwritten queues, see "Algorithm Competition", Tsinghua University Press, by Luo Yongjun and Guo Weibin, page 7). h is the head of the queue, t is the tail of the queue; keeping h ≤ t, the queue length is equal to t - h + 1; h++ means popping (deleting) the head of the queue, and t– means popping (deleting) the tail of the queue.
   The function of function check(k) is to check whether there is a legal window with width k, and the difference between the maximum and minimum values ​​in this window is ≥ d. Use two monotonic queues to find the maximum and minimum values ​​in the window respectively. q1 is a monotonically increasing queue, and the head of the queue is the minimum value; q2 is a monotonically decreasing queue, and the head of the queue is the maximum value.
[Key point]Monotone queue.

C++ code

#include<bits/stdc++.h>
using namespace std;
const int N=100001;
int n,d;
int q1[N],q2[N];                  //q1队头是窗口内最小值,q2队头是窗口内最大值
struct node{
    
    int x,y;}a[N];
int cmp(node u,node v){
    
     return u.x<v.x;}
bool check(int k){
    
    
    memset(q1,0,sizeof(q1));      //单调递增,队头最小
    memset(q2,0,sizeof(q2));      //单调递减,队头最大
    int h1=1,t1=0,h2=1,t2=0;      //h:队头,t队尾 ,注意保持 h<=t
    for(int i=1;i<=n;i++){
    
            //a[i]一个个地从队尾进入
        while(h1<=t1 && a[q1[h1]].x < a[i].x-k)  h1++;  //窗口宽度大于k了,弹走队头,减小到k
        while(h1<=t1 && a[i].y < a[q1[t1]].y)
            t1--;           //如果原队尾比a[i].y更大,删除队尾。保持队头a[q1[h1]].y最小
        q1[++t1]=i;         //现在队内都比a[i]小了,a[i]从队尾进队
        //前面几行求窗口内的最小值,下面几行求窗口内的最大值
        while(h2<=t2 && a[q2[h2]].x < a[i].x-k)  h2++;  //窗口宽度大于k了,弹走队头,减小到k
        while(h2<=t2 && a[i].y > a[q2[t2]].y)
            t2--;           //如果原队尾比a[i].y更小,删除队尾。保持队头a[q2[h2]].y最大
        q2[++t2]=i;         //现在队内都比a[i]大了,a[i]从队尾进队

        if(a[q2[h2]].y-a[q1[h1]].y >= d) return true; //窗口内最大值最小值之差大于等于d
    }
    return false;
}
int main(){
    
    
    cin>>n>>d;
    for(int i=1;i<=n;i++)  scanf("%d%d",&a[i].x,&a[i].y);
    sort(a+1,a+n+1,cmp);     // 根据x值升序
    int L=1,R=1e6,ans=-1;
    while(L<=R){
    
                 //二分求最小宽度
        int mid=(L+R)>>1;
        if(check(mid)) ans=mid,R=mid-1;
        else  L=mid+1;
    }
    cout<<ans<<endl;
    return 0;
}

Java code

import java.util.*;
import java.io.*;
class Main {
    
    
    static class Node {
    
    
        int x, y; 
        Node(int x, int y) {
    
    
            this.x = x;
            this.y = y;
        }
    } 
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int d = scanner.nextInt();
        Node[] a = new Node[n + 1];
        for (int i = 1; i <= n; i++) {
    
    
            int x = scanner.nextInt();
            int y = scanner.nextInt();
            a[i] = new Node(x, y);
        }
        Arrays.sort(a, 1, n + 1, (u, v) -> u.x - v.x);
        int L = 1, R = 1000000, ans = -1;
        while (L <= R) {
    
    
            int mid = (L + R) >> 1;
            if (check(a, n, d, mid)) {
    
    
                ans = mid;
                R = mid - 1;
            } else {
    
    
                L = mid + 1;
            }
        }
        System.out.println(ans);
    }
 
    static boolean check(Node[] a, int n, int d, int k) {
    
    
        int[] q1 = new int[n+10];
        int[] q2 = new int[n+10];
        int h1 = 1, t1 = 0, h2 = 1, t2 = 0;
        for (int i = 1; i <= n; i++) {
    
    
            while (h1 <= t1 && a[q1[h1]].x < a[i].x - k) 
                h1++;
            while (h1 <= t1 && a[i].y < a[q1[t1]].y) 
                t1--;
            q1[++t1] = i; 
            while (h2 <= t2 && a[q2[h2]].x < a[i].x - k) 
                h2++;
            while (h2 <= t2 && a[i].y > a[q2[t2]].y) 
                t2--;
            q2[++t2] = i; 
            if (a[q2[h2]].y - a[q1[h1]].y >= d) 
                return true;
        }
        return false;
    }
}

Python code

 #pypy
 import sys
input = sys.stdin.readline
def check(a, n, d, k):
    q1 = [0] * (n + 1)
    q2 = [0] * (n + 1)
    h1, t1, h2, t2 = 1, 0, 1, 0
    for i in range(1, n + 1):
        while h1 <= t1 and a[q1[h1]][0] < a[i][0] - k:    h1 += 1
        while h1 <= t1 and a[i][1] < a[q1[t1]][1]:        t1 -= 1
        t1 += 1
        q1[t1] = i
 
        while h2 <= t2 and a[q2[h2]][0] < a[i][0] - k:    h2 += 1
        while h2 <= t2 and a[i][1] > a[q2[t2]][1]:        t2 -= 1
        t2 += 1
        q2[t2] = i
 
        if a[q2[h2]][1] - a[q1[h1]][1] >= d:   return True
    return False
  
n, d = map(int, input().split())
a = [(0,0)]
for _ in range(n):
    x, y = map(int, input().split())
    a.append((x, y))
a.sort()
L, R, ans = 1, 1000000, -1
while L <= R:
    mid = (L + R) >> 1
    if check(a, n, d, mid):
        ans = mid
        R = mid - 1
    else:  L = mid + 1
print(ans)

Guess you like

Origin blog.csdn.net/weixin_43914593/article/details/132576183