"Algorithm Competition·Quick 300 Questions" One question per day: "Binary Sudoku"

" Algorithm Competition: 300 Quick Questions " will be published in 2024 and is an auxiliary exercise book for "Algorithm Competition" .
All questions are placed in the self-built OJ New Online Judge .
Codes are given in three languages: C/C++, Java, and Python. The topics are mainly mid- to low-level topics and are suitable for entry-level and advanced students.


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

Question description

[Problem description] Farmer John plays an interesting Sudoku game with his cows.
Like traditional Sudoku, this game also consists of a 9x9 square, which is divided into nine 3x3 small squares.
But the difference is that in this game, only binary numbers 0 and 1 are used to fill these squares.
The object of the game is to change as few numbers as possible so that every row, column, and 3x3 square contains an even number of ones.
For example, the following is a legal solution:
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 000For
a given initial situation, you need to help these cows calculate the minimum number of modifications.
[Input format] Enter 9 lines, each line has 9 characters.
Each character is 0 or 1.
[Output format] Output an integer representing the answer
[Input example]

000000000
001000100
000000000
000110000
000111000
000000000
000000000
000000000
000000000

【Output sample】

3

answer

   Summary question: Given a 9×9 01 matrix, ask how many numbers can be modified at least so that the number of 1's in each row, each column and each nine-square grid is an even number. In the example, one modification method is to change the two 1's above and the 1 in the lower right corner to 0, a total of three changes.
   How to get the minimum number of modifications? Try a brute force search first, use DFS encoding, and try all possible modifications. Compare the number of modifications of different methods, and the minimum number of modifications is the answer. How many possible modifications are there? There are 9×9=81 grids, and each grid can be modified in two ways (to 0 or 1), a total of 2 81 2^{81}281 ways to modify. 2 81 2^{81}281 is obviously too big, but after adding pruning, it can be optimized a lot.
   There is another kind of violent law. There are 81 grids in total, the minimum number of modifications is 1, and the maximum number is 81. Judge the number of modifications one by one from small to large. First try changing only one grid, a total of 81 modification methods, to verify whether each method can achieve the goal; if not, try changing 2 grids again, a total of 81×80 modification methods; and so on. Readers can prove that the calculation amount of this brute force method is similar to that of the brute force method above. You can use the dichotomy method to optimize, but it only optimizes 81 to log81, and the amount of other calculations is still huge.
   This question is not about how many modification methods there are, but about the minimum number of modifications. Consider using DP to solve this optimality problem.
   The modification process is simulated below. Assume that the coordinates of the upper left corner are (0,0) and the coordinates of the lower right corner are (8,8). Modify each grid in order from left to right and top to bottom. Starting from the upper left corner (0,0), first change line 0, then line 1, until line 8.
   The question requires that each row, each column, and each 3×3 square contain an even number of 1s. Design the DP according to these three requirements. The steps of DP are:
   (1) In line 0, use DP to record the minimum number of modifications of the 9-column grid in line 0. You need to verify whether there is an even number of 1s in line 0.
   (2) In row 1, use DP to record the minimum number of modifications of the 9-column grid in rows 0 to 1. You need to verify whether there is an even number of 1s in row 1.
   (3) In line 2, use DP records to record the minimum number of modifications of the 9-column grid in lines 0 to 2. It is necessary to verify whether there is an even number of 1s in line 2, and to verify whether there are even numbers in the three 3×3 nine-square grids. 1.
   Wait until line 8.
   Each small grid contains 0 or 1, which is easy to think of using state to compress DP. The meaning of defining the status dp[][][][][], dp[r][c][mask][sub][row] is: (1) The current arrival position (r, c), that is, the rth
   row , Column c, the range of r and c in the code is 0~8.
   (2) Mask represents the number of times the number 1 appears in each column. Assume that we are currently in row r, and count whether the number of 1s in each column of rows 0 to r is an even number. In order to simplify the use of state compression, mask is a 9-bit binary number, each bit represents the number of times the number 1 in each column appears, odd numbers are 1, and even numbers are 0. For example, 000000001 means that the last column (column 8) has an odd number of 1s, and the other columns have an even number of 1s.
   (3) sub represents the number of times the number 1 appears in every three columns. Also using state compression, sub is a 3-bit binary number. The 3 bits respectively represent the number of times the number 1 appears in columns 0, 2 , 3, 5, and columns 6 to 8. The odd number of times is 1, and the even number is 0.
   (4) row represents the number of occurrences of the number 1 in the current row, the odd number of times is 1, and the even number of times is 0.
   DP uses dfs() programming to process the small grids at the (r, c) position one by one starting from row 0 and column 0, until the final row 8 and column 8. There are two ways to change each grid, changing it to 1 or 0.
   (1) Change to 1, number of modifications ans:
      ans = !a[r][c] + dfs(r, c+1, mask ^ (1<<c), sub^(1<<(c/3) ), !row);
   !a[r][c]: If the original a[r][c] = 0, now it is changed to a[r][c] = 1, the number of modifications ans+1; if the original a[ r][c] = 1, there is still a[r][c] = 1, and the number of modifications remains unchanged. In both cases, the number of additional modifications is !a[r][c].
   c+1: Continue with dfs and go one column to the right.
   mask ^ (1<<c): There is an extra 1 in column c. Update the number of 1s in column c to the new odd or even value.
   sub^(1<<(c/3)): Update the parity of every three columns. For example, when c = 2, c/3 = 0, which means that c is in the first three columns, update the number of 1 in the first three columns.
   !row: Update the parity of the number of 1's in the current row. Since there is one more 1 in this row, the new row is the opposite of the original one.
   (2) Change to 0, the number of modifications ans:
      ans = min(ans, a[r][c] + dfs(r, c + 1, mask, sub, row));
   a[r][c]: If Originally a[r][c] = 1, but now it is changed to a[r][c] = 0, then the number of modifications ans+1; if originally a[r][c] = 0, the number of modifications remains unchanged. In both cases, the number of additional modifications is a[r][c].
   c+1: Continue dfs and go one column to the right;
   mask, sub, row: Because the grid (r, c) becomes 0 and does not increase by 1, they all remain unchanged.
   See the code for other processing.
[Key points] State compression DP.

C++ code

#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;
}

Java code

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));
    }
}

Python code

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

Guess you like

Origin blog.csdn.net/weixin_43914593/article/details/132795227