" 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
" Búsqueda del tesoro ", enlace: http://oj.ecustacm.cn/problem.php?id=1707
Descripción de la pregunta
[Descripción del problema] Hay rubíes y zafiros en el juego de búsqueda del tesoro, y hay como máximo un tipo de gema en cada punto.
En un gráfico unidireccional con n puntos, cada vez que un jugador ocupa un punto, debe colocar una bandera en ese punto.
Al principio, el jugador ocupa el punto de inicio 1 y ha insertado una bandera en el punto de inicio 1. Después de eso, cada vez el jugador solo puede comenzar desde el punto que ha sido ocupado y luego ocupar los puntos adyacentes. Cuando el área ocupada contiene al menos 1 rubí y 1 zafiro, la victoria del juego.
Por favor, dígame cuántas banderas necesita al menos el jugador para ganar (excluyendo la bandera en el punto inicial 1).
[Formato de entrada] La primera línea de entrada contiene tres números enteros positivos n, m, k (2≤n≤10^5,1≤m,k<n), que representan respectivamente el número de puntos, el número total de rubíes, y el número total de zafiros.
La segunda línea contiene m números, que representan puntos que contienen rubíes.
La tercera fila contiene k números, que representan puntos que contienen zafiros.
Las siguientes n líneas, la i-ésima línea: el primer número k representa el grado de salida del punto i, y los números del segundo a k+1 representan los puntos que el punto i puede alcanzar.
[Formato de salida] Si la victoria es imposible, genere "imposible"; de lo contrario, genere un número para representar la respuesta.
【Muestra de entrada】
样例1:
3 1 1
2
3
1 2
2 3 1
1 1
样例2:
3 1 1
2
3
1 2
1 1
2 1 2
【Muestra de salida】
样例1:
2
样例2:
impossible
respuesta
Dado un gráfico dirigido, algunos vértices tienen rubíes, algunos vértices tienen zafiros o puede que no haya gemas. Pregunte cuántos pasos se necesitan para llegar al menos a un punto rojo y un punto azul.
Este es obviamente un problema de camino más corto, son al menos dos caminos más cortos: la primera vez al punto rojo (sin pasar el punto azul), la segunda vez al punto azul, o viceversa, primero el punto azul y luego el punto rojo.
Cada punto tiene tres posibilidades: rojo, azul e incoloro. Descomponer un camino que puede llevar al rojo y al azul en tres partes, que pasan por el punto i: ir del 1 al i (uno de rojo, azul o incoloro), i al azul (o rojo) y luego ir al rojo ( o azul). Recorre todo i, el camino más pequeño es la respuesta.
Los pasos del código son:
(1) Calcular la distancia más corta desde el punto inicial 1 hasta todos los demás puntos.
(2) Calcule la distancia más corta desde cualquier punto i al punto rojo. Como hay muchos puntos rojos, utilice esta técnica: agregue un nuevo punto 0 y sus vecinos serán todos puntos rojos. Entonces la distancia más corta desde el punto i a cualquier punto rojo es igual a la distancia más corta a 0 menos 1.
(3) Calcule la distancia más corta desde cualquier punto i al punto azul. De manera similar, se agrega un nuevo punto n+1. Sus vecinos son todos puntos azules. La distancia de i a cualquier punto azul es igual a la distancia más corta de i al punto n+1 menos 1.
Sumar las tres distancias anteriores al punto i es el camino más corto a través de i. Compare los caminos más cortos de todos los i y el valor mínimo es la respuesta.
La codificación utiliza BFS para encontrar el camino más corto. Debido a que las longitudes de los lados son todas 1, es mejor usar BFS para encontrar la ruta más corta, con una complejidad de O (n), no es necesario usar otros algoritmos de ruta más corta.
(1) Encuentre la ruta más corta desde el punto 1 a otros puntos y ejecute BFS una vez.
(2) Encontrar el camino más corto desde cualquier punto i hasta el punto 0 es equivalente a encontrar el camino más corto desde el punto inicial 0 hasta todos los demás puntos del gráfico inverso.
(3) Encontrar el camino más corto desde cualquier punto i hasta el punto n+1 es equivalente a encontrar el camino desde el punto inicial n+1 hasta todos los demás puntos del gráfico inverso.
Tenga en cuenta que las colas utilizadas por BFS deben tener una gran ponderación. Generalmente, int vis [n] se define durante la codificación para determinar si ha sido procesado por la cola. Si vis [i] = 1, significa que la cola ha procesado i y ya no ingresaré a la cola. El siguiente código no utiliza este método, pero utiliza un juicio implícito. Cuando d[x]=inf en la línea 19, significa que el punto x no ha sido procesado por la cola y debe colocarse en la cola; de lo contrario, se ha calculado la ruta más corta al punto x y no es necesario colocarlo en la cola.
Hay otro detalle en el código C++ a continuación. Inf se define como 0x3f3f3f. Solo necesita ser mayor que n=105, porque la ruta más larga no excederá n. No lo defina como un 0x3f3f3f3f más grande, porque los tres d [] en la línea 48 suman un máximo de 3 * inf, que excede int.
Para conocer la complejidad del código, realice BFS tres veces y finalmente encuentre el valor mínimo. La complejidad total es O (n).
[Punto clave] Gráfico inverso, camino más corto.
código C ++
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, inf = 0x3f3f3f; //注意不要写成0x3f3f3f3f,会导致3*inf超过int
vector<int> G[N], NG[N]; //原图,反图
int d0[N], d1[N], dn[N];
int n, m, k;
void add(int a, int b) {
G[a].push_back(b); //原图加边:a->b
NG[b].push_back(a); //反图加边:b->a
}
void bfs(int s, int *d, vector<int> *G) {
//求s到其他所有点的最短路,d[i]是s->i的最短路
for (int i=0; i<=n+1; i++) d[i] = inf;
queue<int> q;
q.push(s);
d[s] = 0;
while (q.size()) {
int t = q.front(); q.pop();
for (auto x : G[t]) {
//扩展t的邻居点
if(d[x] == inf) {
//有判重的作用。如果不等于inf,说明已经算过,不用进队列
d[x] = d[t] + 1;
q.push(x);
}
}
}
}
int main() {
scanf("%d%d%d",&n,&m,&k);
for (int i = 1; i <= m; i++) {
// m个红宝石
int x; scanf("%d",&x);
add(x, 0); //加边(x,0)。把所有红宝石和0点连接
}
for (int i = 1; i <= k; i++) {
//k个蓝宝石
int x; scanf("%d",&x);
add(x, n + 1); //加边(x,n+1)。把所有蓝宝石和n+1点连接
}
for (int i = 1; i <= n; i++) {
int j; scanf("%d",&j); //第i点的邻居点
while (j--) {
int x; scanf("%d",&x);
add(i, x); //加边:i-x
}
}
bfs(0, d0, NG); //在反图上计算所有点到0点(终点是红宝石)的最短路,d0[i]是i到0的最短路
bfs(1, d1, G); //在原图上计算1到所有点的最短路,d1[i]是1到i的最短路
bfs(n+1, dn, NG); //在反图上计算所有点到n+1(终点是蓝宝石)的最短路,dn[i]是i到+1的最短路
int ans = inf;
for (int i = 1; i <= n; i++)
ans = min(ans, d0[i] + d1[i] + dn[i]); //3个d相加,最大可能 = 3*inf,小心越界
if(ans == inf) printf("impossible\n");
else printf("%d\n",ans - 2);
return 0;
}
código java
import java.util.*;
public class Main {
static final int N = 100010, inf = 0x3f3f3f;
static List<Integer>[] G = new ArrayList[N]; //原图
static List<Integer>[] NG = new ArrayList[N]; //反图
static int[] d0 = new int[N], d1 = new int[N], dn = new int[N];
static int n, m, k;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
k = sc.nextInt();
for (int i = 0; i <= n + 1; i++) {
G[i] = new ArrayList<>();
NG[i] = new ArrayList<>();
}
for (int i = 1; i <= m; i++) {
// m个红宝石
int x = sc.nextInt();
add(x, 0);
}
for (int i = 1; i <= k; i++) {
//k个蓝宝石
int x = sc.nextInt();
add(x, n + 1);
}
for (int i = 1; i <= n; i++) {
int j = sc.nextInt();
while (j-- > 0) {
int x = sc.nextInt();
add(i, x);
}
}
bfs(0, d0, NG);
bfs(1, d1, G);
bfs(n + 1, dn, NG);
int ans = inf;
for (int i = 1; i <= n; i++)
ans = Math.min(ans, d0[i] + d1[i] + dn[i]);
if (ans == inf) System.out.println("impossible");
else System.out.println(ans - 2);
}
static void add(int a, int b) {
G[a].add(b); //原图加边:a->b
NG[b].add(a); //反图加边:b->a
}
static void bfs(int s, int[] d, List<Integer>[] G) {
//求s到其他所有点的最短路
Arrays.fill(d, inf);
Queue<Integer> q = new LinkedList<>();
q.offer(s);
d[s] = 0;
while (!q.isEmpty()) {
int t = q.poll();
for (int x : G[t]) {
//扩展t的邻居点
if (d[x] == inf) {
//有判重的作用。如果不等于inf,说明已经算过,不用进队列
d[x] = d[t] + 1;
q.offer(x);
}
}
}
}
}
código pitón
from collections import deque
N = 100010
inf = 0x3f3f3f
G = [[] for _ in range(N)] # 原图
NG = [[] for _ in range(N)] # 反图
d0 = [inf] * N
d1 = [inf] * N
dn = [inf] * N
n, m, k = 0, 0, 0
def add(a, b):
G[a].append(b) # 原图加边:a->b
NG[b].append(a) # 反图加边:b->a
def bfs(s, d, G): # 求s到其他所有点的最短路,d[i]是s->i的最短路
global inf
d[s] = 0
q = deque()
q.append(s)
while q:
t = q.popleft()
for x in G[t]: # 扩展t的邻居点
if d[x] == inf: # 有判重的作用。如果不等于inf,说明已经算过,不用进队列
d[x] = d[t] + 1
q.append(x)
if __name__ == '__main__':
n, m, k = map(int, input().split())
a = [0] + list(map(int, input().split()))
for i in range(1, m + 1): # m个红宝石
x = a[i]
add(x, 0)
b = [0] + list(map(int, input().split()))
for i in range(1, k + 1): # k个蓝宝石
x = b[i]
add(x, n + 1)
for i in range(1, n + 1):
nums = list(map(int, input().split()))
for j in range(1, len(nums)):
x = nums[j]
add(i, x)
bfs(0, d0, NG)
bfs(1, d1, G)
bfs(n + 1, dn, NG)
ans = inf
for i in range(1, n + 1): ans = min(ans, d0[i] + d1[i] + dn[i])
if ans == inf: print("impossible")
else: print(ans - 2)