" Competencia de algoritmos: 300 preguntas rápidas " se publicará en 2024 y es un libro de ejercicios auxiliar para la "Competencia de algoritmos" .
Todas las preguntas se colocan en el nuevo juez en línea del DO de creación propia .
Los códigos se proporcionan en tres lenguajes: C/C++, Java y Python. Los temas son principalmente temas de nivel medio a bajo y son adecuados para estudiantes principiantes y avanzados.
Directorio de artículos
" Intervalo mínimo ", enlace: http://oj.ecustacm.cn/problem.php?id=1869
Descripción de la pregunta
[Descripción del problema] Dada la posición P[i] y la categoría T[i] de N vacas. El intervalo [L,R] satisface todas las categorías de ganado. Encuentre la longitud mínima del intervalo [L,R].
[Formato de entrada] La primera línea es un número entero positivo N, 1≤N≤50000. Las siguientes N líneas, cada línea contiene dos números enteros positivos P [i] y T [i], no más de 10 ^ 9.
[Formato de salida] Genera la longitud mínima.
【Muestra de entrada】
6
25 7
26 1
15 1
22 3
20 1
30 1
【Muestra de salida】
4
respuesta
Esta es una pregunta típica de búsqueda de reglas: utilice el puntero de velocidad para escanear el intervalo y calcular la longitud mínima del intervalo que cumpla con los requisitos. Los pasos son:
(1) Ordenar por la posición del ganado.
(2) Utilice la ventana [L, R] para recorrer todos los intervalos y encontrar la longitud mínima del intervalo que cumpla con los requisitos. L es el puntero lento, R es el puntero rápido y el valor inicial apunta a la primera vaca. Primero, deje que R avance hasta que el intervalo [L, R] contenga todas las categorías (el número total es type_all). Este es un intervalo que cumple con los requisitos. Luego deje que la siguiente L avance hasta que la categoría no sea suficiente para type_all. Esto significa que el intervalo [L, R] no cumple con los requisitos. A continuación, deje que R avance hasta que se cumplan los requisitos nuevamente. Cuando R alcanza la posición de la última vaca y L alcanza la última posición que satisface los requisitos, se han atravesado todos los intervalos que satisfacen los requisitos. Compare las longitudes de todos los intervalos que cumplen los requisitos y encuentre la longitud mínima.
La cantidad de cálculo es igual al método de clasificación más regla. La clasificación es O(nlogn); el método de la regla es O(n) porque tanto L como R solo pasan una vez. La complejidad total es O (nlogn).
[Puntos clave] Cómo utilizar la regla y el puntero de velocidad.
código C ++
Utilice el mapa para contar el número de categorías en la ventana [L, R].
#include<bits/stdc++.h>
using namespace std;
int main(){
int n; cin >> n;
vector<pair<int,int>>a(n, pair<int,int>(0, 0));
set<int>Type; //用set统计有多少类别
for(int i = 0; i < n; i++){
cin >> a[i].first >> a[i].second;
Type.insert(a[i].second); //Type.size()是类别总数
}
sort(a.begin(), a.end()); //按first从小到大排序
int L = 0, R = 0, ans = 1e9;
int type_all = Type.size(), type_now = 0; //type_all是类别总数,type_now是[L,R]内的类别数量
unordered_map<int,int> Cnt; //Cnt统计当前窗口内出现的类别各有多少个
while(L < n){
while(R < n && type_now != type_all) //快指针R一直走,直到窗口包含所有类别
if(Cnt[a[R++].second]++ == 0) //统计R位置的类别出现次数,等于0表示没有出现过
type_now++; //窗口内包含的类别数量加1
if(type_now == type_all) //满足条件
ans = min(ans, a[R-1].first - a[L].first); //计算区间长度,找最小的
if(--Cnt[a[L++].second] == 0) //去掉慢指针L位置的类别,然后L往前走一步
type_now--; //如果这个类别的数量减到0,那么type_now减一
}
cout<<ans<<endl;
return 0;
}
código java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
List<Pair<Integer, Integer>> a = new ArrayList<>();
Set<Integer> Type = new HashSet<>(); //用set统计有多少类别
for (int i = 0; i < n; i++) {
int first = input.nextInt();
int second = input.nextInt();
a.add(new Pair<>(first, second));
Type.add(second); //Type.size()是类别总数
}
Collections.sort(a, (x, y) -> {
//按first从小到大排序
int cmp = x.getFirst().compareTo(y.getFirst());
if (cmp != 0) return cmp;
else return x.getSecond().compareTo(y.getSecond());
});
int L = 0, R = 0, ans = Integer.MAX_VALUE;
int type_all = Type.size(), type_now = 0; //type_all是类别总数,type_now是[L,R]内的类别数量
Map<Integer, Integer> Cnt = new HashMap<>(); //Cnt统计当前窗口内出现的类别各有多少个
while (L < n) {
while (R < n && type_now != type_all) {
//快指针R一直走,直到窗口包含所有类别
if (Cnt.getOrDefault(a.get(R).getSecond(), 0) == 0) //统计R位置的类别出现次数,等于0表示没有出现过
type_now++; //窗口内包含的类别数量加1
Cnt.put(a.get(R).getSecond(), Cnt.getOrDefault(a.get(R).getSecond(), 0) + 1);
R++;
}
if (type_now == type_all) //满足要求
ans = Math.min(ans, a.get(R - 1).getFirst() - a.get(L).getFirst()); //计算区间长度,找最小的
Cnt.put(a.get(L).getSecond(), Cnt.getOrDefault(a.get(L).getSecond(), 0) - 1); //去掉慢指针L位置的类别
if (Cnt.getOrDefault(a.get(L).getSecond(), 0) == 0)
type_now--; //如果这个类别的数量减到0,那么type_now减一
L++; //然后L往前走一步
}
System.out.println(ans);
}
}
class Pair<A, B> {
A first;
B second;
Pair(A first, B second) {
this.first = first;
this.second = second;
}
public A getFirst(){
return this.first; }
public B getSecond(){
return this.second;}
}
código pitón
from collections import defaultdict
def solve(n, a):
type = set() # 用set统计有多少类别
for i in range(n): type.add(a[i][1]) # type.size()是类别总数
a.sort() # 按位置a[i][0]从小到大排序
L, R = 0, 0
ans = float("inf")
type_all, type_now = len(type), 0 # type_all是类别总数,type_now是[L,R]内的类别数量
cnt = defaultdict(int) # cnt统计当前窗口内出现的类别各有多少个
while L < n:
while R < n and type_now != type_all: # 快指针R一直走,直到窗口包含所有类别
if cnt[a[R][1]] == 0: # 统计R位置的类别出现次数,等于0表示没有出现过
type_now += 1 # 窗口内包含的类别数量加1
cnt[a[R][1]] += 1
R += 1
if type_now == type_all: # 满足条件
ans = min(ans, a[R-1][0] - a[L][0]) # 计算区间长度,找最小的
cnt[a[L][1]] -= 1
if cnt[a[L][1]] == 0: # 去掉慢指针L位置的类别,然后往前走一步
type_now -= 1 # 如果这个类别的数量减到0,那么type_now减一
L += 1
return ans
if __name__ == "__main__":
n = int(input())
a = []
for i in range(n): a.append(tuple(map(int, input().split())))
ans = solve(n, a)
print(ans)