"Concurso de algoritmos · 300 preguntas rápidas" Una pregunta por día: "Sudoku binario"

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


" Sudoku binario ", enlace: http://oj.ecustacm.cn/problem.php?id=1872

Descripción de la pregunta

[Descripción del problema] El granjero John juega un interesante juego de Sudoku con sus vacas.
Al igual que el Sudoku tradicional, este juego también consta de un cuadrado de 9x9, que se divide en nueve cuadrados pequeños de 3x3.
Pero la diferencia es que en este juego sólo se utilizan los números binarios 0 y 1 para llenar estos cuadrados.
El objetivo del juego es cambiar la menor cantidad de números posible para que cada fila, columna y cuadrado de 3x3 contenga un número par de unos.
Por ejemplo, la siguiente es una solución legal:
000 000 000
001 000 100
001 000 100

000 110 000
000 110
000 000 000 000

000 000 000
000 000 000
000 000 000 Para
una situación inicial determinada, debes ayudar a estas vacas a calcular el número mínimo de modificaciones.
[Formato de entrada] Introduzca 9 líneas, cada línea tiene 9 caracteres.
Cada carácter es 0 o 1.
[Formato de salida] Genera un número entero que representa la respuesta
[Ejemplo de entrada]

000000000
001000100
000000000
000110000
000111000
000000000
000000000
000000000
000000000

【Muestra de salida】

3

respuesta

   Pregunta resumida: dada una matriz de 9 × 9 01, pregunte cuántos números se pueden modificar al menos para que el número de unos en cada fila, cada columna y cada cuadrícula de nueve cuadrados sea un número par. En el ejemplo, un método de modificación es cambiar los dos 1 de arriba y el 1 de la esquina inferior derecha a 0, un total de tres cambios.
   ¿Cómo conseguir el número mínimo de modificaciones? Pruebe primero una búsqueda de fuerza bruta, utilice la codificación DFS y pruebe todas las modificaciones posibles. Compare el número de modificaciones de diferentes métodos y la respuesta será el número mínimo de modificaciones. ¿Cuántas modificaciones posibles hay? Hay 9×9=81 cuadrículas, y cada cuadrícula se puede modificar de dos maneras (a 0 o 1), un total de 2 81 2^{81}281 formas de modificar. 2 81 2^{81}281 es obviamente demasiado grande, pero después de agregar poda, se puede optimizar mucho.
   Hay otro tipo de ley violenta. Hay 81 cuadrículas en total, el número mínimo de modificaciones es 1 y el número máximo es 81. Juzgue el número de modificaciones una por una, de pequeña a grande. Primero intente cambiar solo una cuadrícula, un total de 81 métodos de modificación, para verificar si cada método puede lograr el objetivo; si no, intente cambiar 2 cuadrículas nuevamente, un total de 81 × 80 métodos de modificación; y así sucesivamente. Los lectores pueden demostrar que la cantidad de cálculo de este método de fuerza bruta es similar a la del método de fuerza bruta anterior. Puede utilizar el método de dicotomía para optimizar, pero solo optimiza 81 a log81, y la cantidad de otros cálculos sigue siendo enorme.
   Esta pregunta no se trata de cuántos métodos de modificación existen, sino del número mínimo de modificaciones. Considere utilizar DP para resolver este problema de optimización.
   El proceso de modificación se simula a continuación. Suponga que las coordenadas de la esquina superior izquierda son (0,0) y las coordenadas de la esquina inferior derecha son (8,8). Modifique cada cuadrícula en orden de izquierda a derecha y de arriba a abajo. Comenzando desde la esquina superior izquierda (0,0), primero cambie la línea 0, luego la línea 1, hasta la línea 8.
   La pregunta requiere que cada fila, cada columna y cada cuadrado de 3 × 3 contenga un número par de 1. Diseñe el DP de acuerdo con estos tres requisitos. Los pasos de DP son:
   (1) En la línea 0, use DP para registrar el número mínimo de modificaciones de la cuadrícula de 9 columnas en la línea 0. Debe verificar si hay un número par de unos en la línea 0.
   (2) En la fila 1, utilice DP para registrar el número mínimo de modificaciones de la cuadrícula de 9 columnas en las filas 0 a 1. Debe verificar si hay un número par de unos en la fila 1.
   (3) En la línea 2, utilice registros DP para registrar el número mínimo de modificaciones de la cuadrícula de 9 columnas en las líneas 0 a 2. Es necesario verificar si hay un número par de unos en la línea 2 y verificar si hay números pares en las tres cuadrículas de nueve cuadrados de 3×3. 1.
   Espere hasta la línea 8.
   Cada cuadrícula pequeña contiene 0 o 1, por lo que es fácil pensar en usar el estado para comprimir DP. El significado de definir el estado dp[][][][][], dp[r][c][máscara][sub][fila] es: (1) La posición de llegada actual (r, c), que es decir, la
   fila r, columna c, el rango de r y c en el código es 0 ~ 8.
   (2) La máscara representa el número de veces que aparece el número 1 en cada columna. Supongamos que actualmente estamos en la fila r y cuente si el número de unos en cada columna de las filas 0 a r es un número par. Para simplificar el uso de la compresión de estado, la máscara es un número binario de 9 bits, cada bit representa el número de veces que aparece el número 1 en cada columna, los números impares son 1 y los números pares son 0. Por ejemplo, 000000001 significa que la última columna (columna 8) tiene un número impar de unos y las otras columnas tienen un número par de unos.
   (3) sub representa el número de veces que aparece el número 1 en cada tres columnas. También usando compresión de estado, sub es un número binario de 3 bits. Los 3 bits representan respectivamente el número de veces que aparece el número 1 en las columnas 0, 2, 3, 5 y en las columnas 6 a 8. El número impar de veces es 1 , y el número par es 0.
   (4) la fila representa el número de apariciones del número 1 en la fila actual, el número impar de veces es 1 y el número par de veces es 0.
   DP utiliza la programación dfs() para procesar las pequeñas cuadrículas en la posición (r, c) una por una comenzando desde la fila 0 y la columna 0 hasta la última fila 8 y la columna 8. Hay dos formas de cambiar cada cuadrícula, cambiándola a 1 o 0.
   (1) Cambiar a 1, número de modificaciones ans:
      ans = !a[r][c] + dfs(r, c+1, mask ^ (1<<c), sub^(1<<(c/3 ) ), !row);
   !a[r][c]: Si el original a[r][c] = 0, ahora se cambia a a[r][c] = 1, el número de modificaciones ans+ 1; si el original a[r][c] = 1, todavía hay a[r][c] = 1 y el número de modificaciones permanece sin cambios. En ambos casos, el número de modificaciones adicionales es !a[r][c].
   c+1: continúa con dfs y ve una columna a la derecha.
   máscara ^ (1<<c): hay un 1 adicional en la columna c. Actualice el número de unos en la columna c al nuevo valor par o impar.
   sub^(1<<(c/3)): actualiza la paridad de cada tres columnas. Por ejemplo, cuando c = 2, c/3 = 0, lo que significa que c está en las primeras tres columnas, actualiza el número de 1 en las tres primeras columnas.
   !fila: actualiza la paridad del número de 1 en la fila actual. Dado que hay un 1 más en esta fila, la nueva fila es opuesta a la original.
   (2) Cambie a 0, el número de modificaciones ans:
      ans = min(ans, a[r][c] + dfs(r, c + 1, máscara, sub, fila));
   a[r][c] : Si originalmente a[r][c] = 1, pero ahora se cambia a a[r][c] = 0, entonces el número de modificaciones es +1; si originalmente a[r][c] = 0, el número de modificaciones se mantiene sin cambios. En ambos casos, el número de modificaciones adicionales es a[r][c].
   c+1: continúa dfs y va una columna a la derecha;
   máscara, sub, fila: debido a que la cuadrícula (r, c) se vuelve 0 y no aumenta en 1, todos permanecen sin cambios.
   Consulte el código para otros procesamientos.
[Puntos clave] Compresión de estado DP.

código C ++

#include<bits/stdc++.h>
using namespace std;
const int INF = 999;
bool a[9][9];         //存方格矩阵,行标0~8,列标0~8
int dp[9][9][1<<9][1<<3][2];  //dp[r][c][mask][sub][row]
int dfs(int r, int c, int mask, int sub, bool row){
    
    
    if(r == 9)        //0-8行已经填满,必须保证mask=0,sub=0,row=0
        return (!mask && !sub && !row) ? 0 : INF;
    if(c == 9){
    
           //0-8列已经填完
        if(row)  return INF;                //1、保证本行偶数个
        if(r%3 == 2 && sub)  return INF;    //2、保证每三行统计一下每三列数字1出现次数为偶数个
        return dfs(r + 1, 0, mask, sub, 0); //3、下一行
    }
    int& ans = dp[r][c][mask][sub][row];    //ans是dp的别名,把下面的ans改成dp,结果一样
    if(ans != -1) return ans;  //记忆化
    ans = !a[r][c] + dfs(r, c+1, mask ^ (1<<c), sub^(1<<(c/3)), !row);
        //a[r][c]设置为1。  若原来a[r][c]=0,ans+1
    ans = min(ans, a[r][c] + dfs(r, c + 1, mask, sub, row));
        //a[r][c]设置为0。  若原来a[r][c]=1,ans+1
    return ans;
}
int main(){
    
    
    for(int i = 0; i < 9; i++){
    
    
        string s;  cin >> s;
        for(int j = 0; j < 9; j++)
            a[i][j] = (s[j] == '1'); //存到 a[0][0]~a[8][8]
    }
    memset(dp, -1, sizeof(dp));
    cout<<dfs(0, 0, 0, 0, 0)<<endl;
}

código java

import java.util.*;
import java.io.*;
public class Main {
    
    
    private static final int INF = 999;
    private static boolean[][] a; // 存方格矩阵,行标0~8,列标0~8
    private static int[][][][][] dp; // dp[r][c][mask][sub][row]
    private static int dfs(int r, int c, int mask, int sub, int row) {
    
    
        if (r == 9)  // 0-8行已经填满,必须保证mask=0,sub=0,row=0
            return (mask == 0 && sub == 0 && row == 0) ? 0 : INF;        
        if (c == 9) {
    
     // 0-8列已经填完
            if (row==1)  return INF;   // 1、保证本行偶数个
            if (r % 3 == 2 && sub != 0)    return INF; 
// 2、保证每三行统计一下每三列数字1出现次数为偶数个
            return dfs(r + 1, 0, mask, sub, 0); // 3、下一行
        }
        if (dp[r][c][mask][sub][row] != -1)    // 记忆化
            return dp[r][c][mask][sub][row];
        int ans;
        ans = (!a[r][c]?1:0) + dfs(r, c+1, mask ^ (1<<c), sub^(1<<(c/3)), 1 - row);
        ans = Math.min(ans, (a[r][c]?1:0) + dfs(r, c + 1, mask, sub, row));
        dp[r][c][mask][sub][row] = ans;
        return ans;
    }
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        a = new boolean[9][9];
        for (int i = 0; i < 9; i++) {
    
    
            String s = scanner.next();
            for (int j = 0; j < 9; j++) 
                a[i][j] = (s.charAt(j) == '1'); // 存到 a[0][0]~a[8][8]            
        }
        dp = new int[9][9][1 << 9][1 << 3][2];
        for (int[][][][] rows : dp) 
            for (int[][][] row : rows) 
                for (int[][] sub : row) 
                    for (int[] arr : sub) 
                        Arrays.fill(arr, -1);
        System.out.println(dfs(0, 0, 0, 0, 0));
    }
}

código pitón

INF = 999
a = [[False for j in range(9)] for i in range(9)] # 存方格矩阵,行标0~8,列标0~8
dp = [[[[[-1 for k in range(2)] for j in range(1 << 3)] for i in range(1 << 9)] for c in range(9)] for r in range(9)]
def dfs(r, c, mask, sub, row):
    if r == 9: # 0-8行已经填满,必须保证mask=0,sub=0,row=0
        return 0 if not mask and not sub and not row else INF
    if c == 9: # 0-8列已经填完
        if row:   return INF       # 1、保证本行偶数个
        if r % 3 == 2 and sub: 
            return INF             # 2、保证每三行统计一下每三列数字1出现次数为偶数个
        return dfs(r + 1, 0, mask, sub, False)     # 3、下一行
    if dp[r][c][mask][sub][row] != -1:
        return dp[r][c][mask][sub][row] # 记忆化
ans = dfs(r, c+1, mask ^ (1 << c), sub ^ (1 << (c // 3)), not row) + (not a[r][c]) 
# a[r][c]设置为1。若原来a[r][c]=0,ans+1
ans = min(ans, dfs(r, c+1, mask, sub, row) + a[r][c])
  # a[r][c]设置为0。若原来a[r][c]=1,ans+1
    dp[r][c][mask][sub][row] = ans        # 存储结果
    return ans
for i in range(9):
    s = input().strip()
    for j in range(9):   a[i][j] = s[j] == '1' # 存到 a[0][0]~a[8][8]
print(dfs(0, 0, 0, 0, False))

Supongo que te gusta

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