"Concurso de algoritmos · 300 preguntas rápidas" Una pregunta por día: "Búsqueda del tesoro"

" 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.


" 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】

样例13 1 1
2
3
1 2
2 3 1
1 1

样例23 1 1
2
3
1 2
1 1
2 1 2

【Muestra de salida】

样例12

样例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)

Supongo que te gusta

Origin blog.csdn.net/weixin_43914593/article/details/132391048
Recomendado
Clasificación