数独 (dfs+最適化+ビット演算)

リンク: https://www.acwing.com/problem/content/168/
出典: 「アルゴリズム コンペティションの上級ガイド」

数独是一种传统益智游戏,你需要把一个 9×9 的数独补充完整,使得图中每行、每列、每个 3×3 的九宫格内数字 
1∼9 均恰好出现一次。

请编写一个程序填写数独。

输入格式
输入包含多组测试用例。

每个测试用例占一行,包含 81 个字符,代表数独的 81 个格内数据(顺序总体由上到下,同行由左到右)。

每个字符都是一个数字(1−9)或一个 .(表示尚未填充)。

您可以假设输入中的每个谜题都只有一个解决方案。

文件结尾处为包含单词 end 的单行,表示输入结束。

输出格式
每个测试用例,输出一行数据,代表填充完全后的数独。

输入样例:
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end
输出样例:
417369825632158947958724316825437169791586432346912758289643571573291684164875293
416837529982465371735129468571298643293746185864351297647913852359682714128574936

アイデア:
空白の各点で、行、列、正方形を移動し、入力できる数字を選択し、数字を 1 つずつ埋めて、次の点を入力してから元に戻します。
これではタイムアウトになるのは間違いありません。
最適化:
9 つの数値のうちどの数値が使用されていないかをバイナリで表します。ones と map の 2 つの配列を開きます。ones には残りのオプションの数値の数が記録され、map には埋められる値が記録されます。使用時に何度も通過するのを待つ必要はありません。さらに、可能性が最も低い空のスポットを埋めると、全体的な再帰の規模がはるかに小さくなり、時間を大幅に節約できます。
コード:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 9;
char str[100];
int col[N],row[N],cell[3][3];//存放不同行,列,区块可以填的数的情况。
int ones[1<<N],map[1<<N];
inline int lowbit(int x)//方便对2进制进行操作
{
    
    
    return x&-x;
}
void init()//初始化函数
{
    
    
    for(int i=0;i<N;i++) col[i]=row[i]=(1<<N)-1;
    for(int i=0;i<3;i++)
    for(int j=0;j<3;j++)
    cell[i][j]=(1<<N)-1;
}
inline int get(int i,int j)//行列块中能使用的数的交集
{
    
    
    return col[i]&row[j]&cell[i/3][j/3];
}

bool dfs(int cnt)//cnt为空点数量
{
    
    
    if(!cnt) return true;//cnt=0时,退出递归
    int min=10,x,y;//寻找可以选的数最少的空
    for(int i=0;i<N;i++)
    for(int j=0;j<N;j++)
    {
    
    if(str[9*i+j]=='.')
        {
    
    int t=ones[get(i,j)];
            if(t<min)
        {
    
    
            x=i;
            y=j;
            min=t;
        }
        }
    }
    for(int k=get(x,y);k;k-=lowbit(k))
    {
    
    
        int t=map[lowbit(k)];
        col[x]-=1<<t;
        row[y]-=1<<t;
        cell[x/3][y/3]-=1<<t;
        str[x*9+y]='1'+t;
        if(dfs(cnt-1)) return true;
        col[x]+=1<<t;
        row[y]+=1<<t;
        cell[x/3][y/3]+=1<<t;
        str[x*9+y]='.';
    }
    return false;
    
}
int main()
{
    
    
   for(int i=0;i<N;i++) map[1<<i]=i;//初始化map和ones
   for(int i=0;i<1<<N;i++)
   {
    
    
       int s=0;
       for(int j=i;j;j-=lowbit(j)) s++;
       ones[i]=s;
   }
   while(cin>>str,str[0]!='e')
   {
    
    init();
       int cnt=0;
       for(int i=0,k=0;i<N;i++)
       for(int j=0;j<N;j++,k++)
       {
    
    
           if(str[k]!='.')//根据已填入的数,变更行列块的信息
           {
    
    
               int t=str[k]-'1';
               col[i]-=1<<t;
               row[j]-=1<<t;
               cell[i/3][j/3]-=1<<t;
           }
           else cnt++;
       }
       dfs(cnt);
       cout<<str<<endl;
   }
    
}

一般に、この質問には最適化が必要ですが、最適化方法は比較的複雑です。とても賢いですね、自分で考えるのは難しかったと思います。最適化のアイデアを理解するには、このコードを数回入力する必要があります。

Supongo que te gusta

Origin blog.csdn.net/qq_57109165/article/details/120685549
Recomendado
Clasificación