Búsqueda a mitad de camino (encuentro en el medio) + enumeración violenta: la alquimia de PIPI

Búsqueda a mitad de camino (encuentro en el medio) + enumeración violenta: la alquimia de PIPI

pregunta:

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Ideas:

  Primero, necesitamos transformar el problema. De acuerdo con el significado de la pregunta, podemos alquimia con éxito varios materiales, siempre que la suma de los caracteres de estas cadenas de materiales sea un número par, y las cadenas de materiales estén todas compuestas de letras minúsculas, y las letras minúsculas az pueden corresponder a los números 0-25. Por lo tanto, podemos considerar las siguientes operaciones: registramos si el número de ocurrencias de caracteres en cada cadena es par o impar, y lo guardamos en un alfabeto de matriz bidimensional, indicando que el número de ocurrencias de la letra j de la i -ésima cadena es par alphabet[i][j]=0, alphabet[i][j]=1lo que indica la i-ésima cadena tiene un número impar de ocurrencias de la letra j. Entonces podemos convertir la información de si el número de ocurrencias de las 26 letras de cada cadena material es par o impar en un número binario de 26 bits.Si un cierto bit es 1, significa que el número de ocurrencias de la letra correspondiente en esta posición es impar y viceversa es un número par. Luego, n cadenas se convierten en n números, por lo que el problema es: para n números, puede elegir cualquier número (pero el número debe ser mayor que 0) de ellos, XOR en todos, y el otro Si el valor es 0, es un esquema legal (porque si el número de ocurrencias de letras en la cadena combinada es par, el número de ocurrencias de la letra en la cadena original combinada es impar o par, lo que significa que solo corresponde a la situación de 1 1 y 0 0 en los dígitos binarios correspondientes, y el mismo XOR es 0, que corresponde a un número par de ocurrencias de letras después de la combinación, y luego si el resultado de XOR es un valor de 0, entonces todos los bits binarios son 0, correspondientes a un número par de ocurrencias de todos los caracteres, que es la condición en la pregunta).
  Después de la conversión, es hora de contar el número de soluciones. Cualquier cadena se puede combinar en la pregunta. Para cada cadena, obviamente, hay dos opciones: elegir o no, por lo que es fácil pensar en el método de enumeración violenta O(2^n), de 000...001 a 111... 111, y luego XOR la ​​cadena seleccionada correspondiente a través de la operación de bits y el cambio. Sin embargo, el tamaño de n en esta pregunta es 35, y la complejidad del tiempo es demasiado para pasar. En este momento, necesitamos usar el método de media búsqueda para optimizar la complejidad del tiempo.
  La búsqueda intermedia también se denomina método de encuentro intermedio. En pocas palabras, es procesar la primera mitad, almacenar el resultado, procesar la segunda mitad y luego hacer coincidir el resultado almacenado en la primera mitad. Es decir, al enumerar la segunda mitad, se utilizan los resultados generados por la primera mitad para evitar el doble cálculo.
  Para esta pregunta, es enumerar la mitad primero, enumerando desde 1 (correspondiente a 000...001) hasta2 ^ (n / 2 + 1) - 1(correspondiente a 000...111...111), si el resultado XOR es 0, entonces el resultado ans++ significa que se ha encontrado una solución, sin importar cuál sea el resultado XOR, se almacenará la cantidad de resultados XOR calculados en una tabla hash. Luego enumeramos la otra mitad, desde 2 ^ (n / 2 + 1)la enumeración (correspondiente a 000...100...000) hasta todos los 1 en la segunda mitad (es decir, correspondiente a 111...111000...000), tenga en cuenta que cuando enumerar la segunda mitad, el tamaño del paso es, es decir, enumeramos 2 ^ (n/2 + 1)La segunda mitad significa enumerar las cadenas en la segunda mitad. En este momento, no consideramos las cadenas en la primera mitad, es decir, el binario los bits en la primera mitad de la enumeración son todos 0. Si el resultado XOR es 0, el resultado ans++ significa que se ha encontrado una solución. No importa cuál sea el resultado XOR, se consultará la tabla hash. Si el valor XOR calculado actualmente se guarda en la tabla hash, significa que en la primera mitad de la enumeración Si es exactamente igual a la distribución de paridad de caracteres actual, entonces necesitamos usar ans para agregar el número de esquemas almacenados en la tabla hash, porque los esquemas guardados previamente se pueden combinar con el esquema actual para cumplir con los requisitos del tema. (Porque si la distribución par-impar de caracteres en dos cadenas combinadas es exactamente la misma, entonces se combinan nuevamente y todos los caracteres deben aparecer un número par de veces).
  De esta forma, a través de la búsqueda media, se optimiza la complejidad temporal de O(2 ^ n) a O(2 ^ (n/2)).

código:

import java.util.*;

public class Main {
    
    
    static int[][] alphabet = new int[37][27];
    static long[] hash = new long[37];
    static long[] twoMul = new long[37];
    static HashMap<Long, Long> num = new HashMap<>();
    public static void main(String[] args) {
    
    
        int n, i, j, count;
        long loop, temp, k, ans = 0, hVal;
        twoMul[0] = 1;
        for (i = 1; i < 37; i++) {
    
    
            twoMul[i] = twoMul[i - 1] * 2;
        }
        StringBuilder stringBuilder = new StringBuilder();
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        for (i = 0; i < n; i++) {
    
    
            stringBuilder.delete(0, stringBuilder.length());
            stringBuilder.append(scanner.next());
            for (j = 0; j < stringBuilder.length(); j++) {
    
    
                if (alphabet[i][stringBuilder.charAt(j)-'a'] == 1) {
    
    
                    alphabet[i][stringBuilder.charAt(j)-'a'] = 0;
                } else {
    
    
                    alphabet[i][stringBuilder.charAt(j)-'a'] = 1;
                }
            }
        }
        for (i = 0; i < n; i++) {
    
    
            for (j = 0; j < 26; j++) {
    
    
                hash[i] += alphabet[i][j] * twoMul[j];
            }
        }
        for (loop = 1; loop <= twoMul[n / 2 + 1] - 1; loop++) {
    
    
            temp = loop;
            count = 0;
            k = 0;
            while (temp != 0) {
    
    
                if ((temp & 1) != 0) {
    
    
                    k = hash[count] ^ k;
                }
                temp >>= 1;
                count++;
            }
            if (k == 0 && count != 0) {
    
    
                ans++;
            }
            if (num.get(k) != null) {
    
    
                hVal = num.get(k);
                num.put(k, hVal + 1);
            } else {
    
    
                num.put(k, 1L);
            }
        }
        for (; loop <= twoMul[n] - 1; loop += twoMul[n / 2 + 1]) {
    
    
            temp = loop;
            count = n / 2 + 1;
            k = 0;
            while (temp >= twoMul[n / 2 + 1]) {
    
    
                if ((temp & twoMul[n / 2 + 1]) != 0) {
    
    
                    k = hash[count] ^ k;
                }
                temp >>= 1;
                count++;
            }
            if (k == 0 && count != n / 2 + 1) {
    
    
                ans++;
            }
            if (num.get(k) != null) {
    
    
                hVal = num.get(k);
                ans += hVal;
            }
        }
        System.out.println(ans);
    }


}

Supongo que te gusta

Origin blog.csdn.net/qq_44709990/article/details/123456434
Recomendado
Clasificación