2021.11.2-测试T1数独
Posted gyc#66ccff
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021.11.2-测试T1数独相关的知识,希望对你有一定的参考价值。
数 独
【问题描述】
给定一个9*9矩阵,对其进行几种操作,分别是插入,删除,合并,查询,输出
主要学到了一些特别的操作。
(1)备份( 本蒟蒻第一次了解到)
(2)对与数据的一些特别的改动
(3)进行标记
这道题目,直接模拟就可以了,但要注意细节以及一些很妙的操作:
首先是字符组的存入:注意将其他的字符进行排除:
1 for(int i=1;i<=19;i++){
2 cin>>s;
3 if(i&1)continue;//奇数
4 for(int j=1;j<=9;j++)
5 c[0][i/2][j]=s[j*2-1];
6 }
然后就是分类讨论:
char c[105][10][10];//前面用于备份每一次操作后的数独(第一次看到这种操作,太强了)
cin>>T; for(int i=1;i<=T;i++){ cin>>s; if(s[0]==\'I\')x=in,y=in,k=in,insert(i,x,y,k);//in:快读{i记录每一次操作} else if(s[0]==\'D\')x=in,y=in,del(i,x,y); else if(s[0]==\'Q\')x=in,y=in,query(i,x,y); else if(s[0]==\'M\')x=in,y=in,merge(i,x,y); else print(i); }
1:插入:
1 void insert(int n,int x,int y,int k){
2 for(int i=1;i<=9;i++)
3 for(int j=1;j<=9;j++)
4 c[n][i][j]=c[n-1][i][j];//备份
5 if(c[n][x][y]!=\'0\'){
6 cout<<"Error!"<<endl;
7 return;
8 }
9 for(int i=1;i<=9;i++)
10 if(c[n][x][i]==k+\'0\'){
11 cout<<"Error:row!"<<endl;
12 return;//冲突1
13 }
14 for(int i=1;i<=9;i++)
15 if(c[n][i][y]==k+\'0\'){
16 cout<<"Error:column!"<<endl;
17 return;//冲突2
18 }
19 int gg=(x-1)/3*3+(y-1)/3+1;
20 for(int i=0;i<=2;i++)
21 for(int j=0;j<=2;j++)
22 if(c[n][i+xs[gg]][j+ys[gg]]==k+\'0\'){
23 cout<<"Error:square!"<<endl;
24 return;//冲突3
25 }
26 cout<<"OK!"<<endl;
27 c[n][x][y]=k+\'0\';
28 }
2:删除:
1 void del(int n,int x,int y){
2 for(int i=1;i<=9;i++)
3 for(int j=1;j<=9;j++)
4 c[n][i][j]=c[n-1][i][j];//注意备份
5 if(c[n][x][y]==\'0\')
6 cout<<"Error!"<<endl;
7 else{
8 cout<<"OK!"<<endl;
9 c[n][x][y]=\'0\';
10 }
11 }
3:查询:
1 int can[10],ans;
2 void query(int n,int x,int y){
3 for(int i=1;i<=9;i++)
4 for(int j=1;j<=9;j++)
5 c[n][i][j]=c[n-1][i][j];
6 if(c[n][x][y]!=\'0\'){cout<<"Error!"<<endl;}
7 memset(can,0,sizeof(can));ans=0;
8 int gg=(x-1)/3*3+(y-1)/3+1;
9 for(int i=1;i<=9;i++)
10 can[c[n][x][i]-\'0\']=1;
11 for(int i=1;i<=9;i++)
12 can[c[n][i][y]-\'0\']=1;
13 for(int i=0;i<=2;i++)
14 for(int j=0;j<=2;j++)
15 can[c[n][i+xs[gg]][j+ys[gg]]-\'0\']=1;//记录不满足的数(因为冲突)
16 for(int i=1;i<=9;i++)
17 if(!can[i]) ans++;
18 cout<<ans<<endl;
19 for(int i=1;i<=9;i++)
20 if(!can[i]) cout<<i<<endl;
21 }
4:合并:
1 int ansx,ansy;
2 void merge(int n,int x,int y){
3 for(int i=1;i<=9;i++)
4 for(int j=1;j<=9;j++){
5 int flag=0;
6 if(c[x][i][j]!=\'0\'){
7 flag=1;
8 for(int k=1;k<=9;k++)
9 if(c[n][i][k]==c[x][i][j]){
10 flag=0;break;
11 }
12 for(int k=1;k<=9;k++)
13 if(c[n][k][j]==c[x][i][j]){
14 flag=0;break;
15 }
16 int wh=(i-1)/3*3+(j-1)/3+1;
17 for(int l=0;l<=2;l++)
18 for(int r=0;r<=2;r++)
19 if(c[n][l+xs[wh]][r+ys[wh]]==c[x][i][j]){
20 flag=0;break;
21 }
22 }
23 if(flag){
24 c[n][i][j]=c[x][i][j];
25 ansx++;
26 continue;
27 }
28 if(c[y][i][j]!=\'0\'){
29 flag=1;
30 for(int k=1;k<=9;k++)
31 if(c[n][i][k]==c[y][i][j]){
32 flag=0;break;
33 }
34 for(int k=1;k<=9;k++)
35 if(c[n][k][j]==c[y][i][j]){
36 flag=0;break;
37 }
38 int wh=(i-1)/3*3+(j-1)/3+1;
39 for(int l=0;l<=2;l++)
40 for(int r=0;r<=2;r++)
41 if(c[n][l+xs[wh]][r+ys[wh]]==c[y][i][j]){
42 flag=0;break;
43 }
44 }
45 if(flag){
46 c[n][i][j]=c[y][i][j];
47 ansy++;
48 continue;
49 }
50 c[n][i][j]=\'0\';
51 }
52 cout<<ansx<<" "<<ansy<<endl;
53 ansx=0,ansy=0;//注意多次操作要恢复
54 }
总代码:
1 #include<bits/stdc++.h>
2 using namespace std;
3 #define int long long
4 #define in read()
5 inline int read(){
6 int p=0,f=1;
7 char c=getchar();
8 while(!isdigit(c)){
9 if(c==\'-\')f=-1;
10 c=getchar();
11 }
12 while(isdigit(c)){
13 p=p*10+c-\'0\';
14 c=getchar();
15 }
16 return p*f;
17 }
18 char c[105][10][10];
19 string s;
20 int xs[10]={0,1,1,1,4,4,4,7,7,7};
21 int ys[10]={0,1,4,7,1,4,7,1,4,7};
22 void insert(int n,int x,int y,int k){
23 for(int i=1;i<=9;i++)
24 for(int j=1;j<=9;j++)
25 c[n][i][j]=c[n-1][i][j];
26 if(c[n][x][y]!=\'0\'){
27 cout<<"Error!"<<endl;
28 return;
29 }
30 for(int i=1;i<=9;i++)
31 if(c[n][x][i]==k+\'0\'){
32 cout<<"Error:row!"<<endl;
33 return;
34 }
35 for(int i=1;i<=9;i++)
36 if(c[n][i][y]==k+\'0\'){
37 cout<<"Error:column!"<<endl;
38 return;
39 }
40 int gg=(x-1)/3*3+(y-1)/3+1;
41 for(int i=0;i<=2;i++)
42 for(int j=0;j<=2;j++)
43 if(c[n][i+xs[gg]][j+ys[gg]]==k+\'0\'){
44 cout<<"Error:square!"<<endl;
45 return;
46 }
47 cout<<"OK!"<<endl;
48 c[n][x][y]=k+\'0\';
49 }
50 void del(int n,int x,int y){
51 for(int i=1;i<=9;i++)
52 for(int j=1;j<=9;j++)
53 c[n][i][j]=c[n-1][i][j];
54 if(c[n][x][y]==\'0\')cout<<"Error!"<<endl;
55 else{cout<<"OK!"<<endl;c[n][x][y]=\'0\';}
56 }
57 int can[10],ans;
58 void query(int n,int x,int y){
59 for(int i=1;i<=9;i++)
60 for(int j=1;j<=9;j++)
61 c[n][i][j]=c[n-1][i][j];
62 if(c[n][x][y]!=\'0\'){cout<<"Error!"<<endl;}
63 memset(can,0,sizeof(can));ans=0;
64 int gg=(x-1)/3*3+(y-1)/3+1;
65 for(int i=1;i<=9;i++)
66 can[c[n][x][i]-\'0\']=1;
67 for(int i=1;i<=9;i++)
68 can[c[n][i][y]-\'0\']=1;
69 for(int i=0;i<=2;i++)
70 for(int j=0;j<=2;j++)
71 can[c[n][i+xs[gg]][j+ys[gg]]-\'0\']=1;
72 for(int i=1;i<=9;i++)
73 if(!can[i]) ans++;
74 cout<<ans<<endl;
75 for(int i=1;i<=9;i++)
76 if(!can[i]) cout<<i<<endl;
77 }
78 int ansx,ansy;
79 void merge(int n,int x,int y){
80 for(int i=1;i<=9;i++)
81 for(int j=1;j<=9;j++){
82 int flag=0;
83 if(c[x][i][j]!=\'0\'){
84 flag=1;
85 for(int k=1;k<=9;k++)
86 if(c[n][i][k]==c[x][i][j]){
87 flag=0;break;
88 }
89 for(int k=1;k<=9;k++)
90 if(c[n][k][j]==c[x][i][j]){
91 flag=0;break;
92 }
93 int wh=(i-1)/3*3+(j-1)/3+1;
94 for(int l=0;l<=2;l++)
95 for(int r=0;r<=2;r++)
96 if(c[n][l+xs[wh]][r+ys[wh]]==c[x][i][j]){
97 flag=0;break;
98 }
99 }
100 if(flag){
101 c[n][i][j]=c[x][i][j];
102 ansx++;
103 continue;
104 }
105 if(c[y][i][j]!=\'0\'){
106 flag=1;
107 for(int k=1;k<=9;k++)
108 if(c[n][i][k]==c[y][i][j]){
109 flag=0;break;
110 }
111 for(int k=1;k<=9;k++)
112 if(c[n][k][j]==c[y][i][j]){
113 flag=0;break;
114 }
115 int wh=(i-1)/3*3+(j-1)/3+1;
116 for(int l=0;l<=2;l++)
117 for(int r=0;r<=2;r++)
118 if(c[n][l+xs[wh]][r+ys[wh]]==c[y][i][j]){
119 flag=0;break;
120 }
121 }
122 if(flag){
123 c[n][i][j]=c[y][i][j];
124 ansy++;
125 continue;
126 }
127 c[n][i][j]=\'0\';
128 }
129 cout<<ansx<<" "<<ansy<<\'\\n\';
130 ansx=0,ansy=0;
131 }
132 void print(int n){
133 for(int i=1;i<=9;i++)
134 for(int j=1;j<=9;j++)
135 c[n][i][j]=c[n-1][i][j];
136 for(int i=1;i<=9;i++){
137 cout<<"+-+-+-+-+-+-+-+-+-+"<<endl;
138 for(int j=1;j<=9;j++)
139 cout<<"|"<<c[n][i][j];
140 cout<<"|"<<endl;
141 }
142 cout<<"+-+-+-+-+-+-+-+-+-+\\n";
143 }
144 int T,x,y,k;
145 signed main(){
146 for(int i=1;i<=19;i++){
147 cin>>s;
148 if(i&1)continue;//奇数
149 for(int j=1;j<=9;j++)
150 c[0][i/2][j]=s[j*2-1];
151 }
152 T=in;
153 for(int i=1;i<=T;i++){
154 cin>>s;
155 if(s[0]==\'I\')x=in,y=in,k=in,insert(i,x,y,k);
156 else if(s[0]==\'D\')x=in,y=in,del(i,x,y);
157 else if(s[0]==\'Q\')x=in,y=in,query(i,x,y);
158 else if(s[0]==\'M\')x=in,y=in,merge(i,x,y);
159 else print(i);
160 }
161 return 0;
162 }
数独回溯算法
【中文标题】数独回溯算法【英文标题】:Sudoku backtracking algorithm 【发布时间】:2011-10-08 09:30:41 【问题描述】:首先,我要声明这是一项大学作业,所以我不是要求有人为我编写代码,我只需要指出正确的方向。 :)
好的,所以我需要编写一个算法来解决任意大小的任何(可解决的)数独板。我编写了一个递归函数,可以快速解决任何 9x9 板(约 1 毫秒),但是当我做较大的板(16x16)时,它很难解决。我已经进行了 20 分钟的测试,它可以t似乎解决它。它可以解决简单的 16x16 难题,甚至可以解决空白的 16x16 板,所以我认为问题不是尺寸问题。我认为问题更可能是算法问题。
反正这是我程序的基本逻辑..
我有一个 3D 向量来存储我的每个正方形的可能值 当一个值放在一个正方形中时,它会从它所在的周围正方形、行和列的可能值中删除那么我的求解函数基本上是:
bool solve()
if (there are no unfilled squares)
return true
if (the board is unsolvable - there are empty squares that have no possible values)
return false
while (there are empty squares)
int squaresFilled = fillSquaresWithOnlyOneChoice(); //this method updates the possible results vector whenever it fills a square
if (squaresFilled == 0)
break;
//exhausted all of the 'easy' squares (squares with only one possible choice), need to make a guess
while (there are empty squares that have choices left)
find the square with the least number of choices
if (the square with the least number of choices has 0 choices)
return false; //not solvable.
remove that choice from the 3D vector (vector that has the choices for each square)
make a copy of the board and the 3D choices vector
fill the square with the choice
if (solve())
return true; //we're done
restore the board and choices vector
//the guess didn't work so keep looping and make a new guess with the restored board and choices -- the choice we just made has been removed though so it won't get made again.
return false; //can't go any further
这有什么低效的吗?有什么办法可以让它更好地工作吗?我猜一个 16x16 的板需要这么长时间是因为它的决策树对于一个没有太多填充的板来说太大了。不过这很奇怪,因为 9x9 板会很快解决。
任何想法或建议都会非常棒。如果有任何我遗漏的信息也请告诉我!
【问题讨论】:
我建议你研究dancing links算法,它是解决这类问题的一种有趣的方法。 (不过与您的方法完全不同。) 你的算法是指数级的,所以增加一个额外的难度让它运行很长时间也就不足为奇了。碰巧(即运行时间/问题的难度+ cpu / ram速度+程序设计)你就在袖口。这意味着,您的计算机可以处理 e^f(81) 但不能处理 e^f(256)。不足为奇 - 如果第一个是 1ms,这意味着您的计算机处理大约 e^f(81) op/ms,这意味着第二个将需要 e^f(256) / e^f(81) ms。这个数字可能在 e^170 毫秒左右,比宇宙存在的时间还要长。 f 只是一个粗略的估计函数,估计应该被认为是低估了,因为我没有考虑基数变化 - 你的程序是指数的,具有变体基数和指数(~9 ^81 理论选择 vs. ~16^256),并且因为你得到的越大,你会失去速度,例如缓存。 @Mat 我想在完全更改之前尝试修复我的原始算法,但感谢链接,如果我绝望了,我可能会试一试! :) @davin.. 嗯,那确实有道理。我真的认为我很好地实现了算法,但我想我可能需要完全重新考虑它? 【参考方案1】:解决数独的快速算法是 Donald Knuth 的算法 X。您将解决数独问题表示为exact cover 问题,然后使用Algorithm X 解决EC 问题。然后使用DLX作为算法X的高效实现。
***上有很好的解释如何应用精确覆盖来解决数独问题。
我可以告诉你,DLX 解决数独的速度非常快,通常用于最快的算法。
http://www.setbb.com/phpbb/index.php?mforum=sudoku 是一个很棒的论坛,可能是最好的数独程序员。
【讨论】:
【参考方案2】:在仅使用一个选项填充方格和在棋盘上完全递归之间,您可以执行更多高级操作。假设“区域”是一行、一列或一个正方形区域(3x3 或 4x4)。
战术 1
如果一个区域中有 K 个方格只能取相同的 K 个数字(例如,两个方格只能取 2 和 5,或者三个方格只能取 1、7 和 8),那么该区域中的所有其他方格地区不能接受这些具体数字。您需要迭代每个区域以清除“已取”数字,因此您可以找到一个只有一个逻辑选择的正方形(例如,逻辑上具有 2、4 和 5 的第三个正方形只能取 4,或带有 1、3 的第四个正方形, 7 和 8 在逻辑上只能取 3)。
如果您考虑以下示例,则必须通过迭代解决此问题。一个区域有这个可能的数字的正方形:
答:1 2 3 乙:2 3 C: 2 3 4 5 D:4 5 E: 4 5
算法应该检测方块 D 和 E 包含数字 4 和 5,因此 4 和 5 被排除在该区域中的其他方块之外。然后,该算法检测方块 B 和 C 包含数字 2 和 3,因此将它们排除在其他方块之外。这使得正方形 A 只有数字 1。
战术 2
如果一个数字只出现在该区域的一个方格中,那么逻辑上该方格包含该数字。
战术 3
策略 1 和 2 只是策略 3 的特殊情况,其中有 K 个正方形,只有 K 个相同的数字。你可以有 K 个正方形和一组 K 个数字,而这些 K 个正方形可以包含这些 K 个数字的任何子集。考虑以下区域示例:
答:1 2 乙:2 3 C: 1 3 D:1 2 3 4
方格 A、B 和 C 只能容纳数字 1、2 和 3。这是 K 对 K。这意味着任何其他方格在逻辑上都无法容纳这些数字,因此方格 D 只能容纳数字 4。
当 K = N - 1 时,战术 2 是战术 3 的特例。
战术 4
利用区域重叠。假设某个数字只能存在于该区域的某些方格中。如果所有这些方块都属于另一个重叠区域,则该数字应从该其他区域中的所有其他方块中排除。
战术 5
缓存结果。所有区域都应该有一个“脏”标志,表示该区域中的某些内容自上次处理该区域以来发生了变化。您不必处理未设置此标志的区域。
人类使用所有这些策略,并且非常讨厌猜测数字,因为回溯是一种真正的痛苦。实际上,棋盘的难度是用一个人必须做出的最小猜测数来衡量的。对于大多数“极端”电路板来说,一个好的猜测就足够了。
【讨论】:
但这些技术是类人技术。尽可能使用这些方法是否更有效? 我为 OP 的问题量身定制了答案。对于“哪种数独算法最快”的问题,有 ralu 的答案。【参考方案3】:我不知道你以前是否看过这个链接。 此外,它在效率上可能无法与跳舞链接算法相媲美 因为它使用简单的回溯形式。无论如何它有效。 看看对你有没有用!
您也可以添加几行来解决“简单”的正方形。 http://edwinchan.wordpress.com/2006/01/08/sudoku-solver-in-c-using-backtracking/
【讨论】:
【参考方案4】:你的算法看起来不错。问题是您使用的是蛮力方法,因此您的运行时间是(字符数)^(板的大小) - 所以对于 9x9 有 9^9=387420489 而对于 16x16,运行时间是 16^ 16=18446744073709551616。你可以看到区别。
尝试查找Dynamic programing approach
如果您是一年级/二年级的学生,请保持现有的状态(并检查正确性)。您的老师不会期待更多。【讨论】:
真的吗?我的算法是蛮力?我觉得稍微好一点,不就是回溯算法吗?我想我需要做一些谷歌搜索。不幸的是,我是一名二年级学生,预计能够在 30 秒内解决 16x16 的难题。 :(【参考方案5】:没有必要将只有一个可能数字的单元格视为特殊的。您已经先访问了可能性最少的单元格。
另外:当您“从 3D 向量中删除该选项”时,您还可以将其从同一 row,columns,box 上的其他单元格中删除。位掩码可能会很好地适应。 (但回溯会变得有点困难)
【讨论】:
【参考方案6】:正如@ralu 提到的,解决数独的最快算法是使用 DLX。下面是我过去写的一个程序。它可以在1秒内解决4*4的数独。
假设输入是这样的:
--A----C-----O-I
-J--A-B-P-CGF-H-
--D--F-I-E----P-
-G-EL-H----M-J--
----E----C--G---
-I--K-GA-B---E-J
D-GP--J-F----A--
-E---C-B--DP--O-
E--F-M--D--L-K-A
-C--------O-I-L-
H-P-C--F-A--B---
---G-OD---J----H
K---J----H-A-P-L
--B--P--E--K--A-
-H--B--K--FI-C--
--F---C--D--H-N-
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int INF=1<<30;
const int N=4;
const int MAXR=N*N*N*N*N*N+10;
const int MAXC=N*N*N*N*4+10;
const int SIZE=MAXR*MAXC/N/N;
int n,m;
int mat[MAXR][MAXC],r,c,ans;
int L[SIZE],R[SIZE],U[SIZE],D[SIZE],S[MAXC],C[SIZE],RH[SIZE],O[MAXC];
int a[MAXR];
char b[MAXR];
char s[N*N+10][N*N+10];
int head,cnt;
int node(int up,int down,int left,int right)
U[cnt]=up;D[cnt]=down;L[cnt]=left;R[cnt]=right;
D[up]=U[down]=L[right]=R[left]=cnt;
return cnt++;
void init()
cnt=0;
head=node(0,0,0,0);
for (int i=1;i<=c;i++)
C[i]=node(cnt,cnt,L[head],head);
S[i]=0;
for (int i=1;i<=r;i++)
int rowh=-1;
for (int j=1;j<=c;j++) if (mat[i][j])
if (rowh==-1)
rowh=node(U[C[j]],C[j],cnt,cnt);
RH[rowh]=i;
C[rowh]=C[j];
S[j]++;
else
int k=node(U[C[j]],C[j],L[rowh],rowh);
RH[k]=i;
C[k]=C[j];
S[j]++;
void remove(int col)
L[R[col]]=L[col];
R[L[col]]=R[col];
for (int i=D[col];i!=col;i=D[i])
for (int j=R[i];j!=i;j=R[j])
U[D[j]]=U[j];
D[U[j]]=D[j];
S[C[j]]--;
void resume(int col)
for (int i=U[col];i!=col;i=U[i])
for (int j=L[i];j!=i;j=L[j])
S[C[j]]++;
U[D[j]]=j;
D[U[j]]=j;
L[R[col]]=col;
R[L[col]]=col;
bool dfs(int k)
if (R[head]==head)
ans=k;
return true;
int mins=INF,cur=0;
for (int i=R[head];i!=head;i=R[i])
if (S[i]<mins)
mins=S[i];
cur=i;
remove(cur);
for (int i=D[cur];i!=cur;i=D[i])
O[k]=RH[i];
for (int j=R[i];j!=i;j=R[j])
remove(C[j]);
if (dfs(k+1)) return true;
for (int j=L[i];j!=i;j=L[j])
resume(C[j]);
resume(cur);
return false;
void makegraph()
r=0;
for (int i=0;i<N*N;i++)
for (int j=0;j<N*N;j++)
int t=(i/N)*N+(j/N);
int p=i*N*N+j;
if (s[i][j]=='-')
for (int k=1;k<=N*N;k++)
r++;
mat[r][i*N*N+k]=1;
mat[r][N*N*N*N+j*N*N+k]=1;
mat[r][2*N*N*N*N+t*N*N+k]=1;
mat[r][3*N*N*N*N+p+1]=1;
a[r]=p;
b[r]='A'+k-1;
else
int k=s[i][j]-'A'+1;
r++;
mat[r][i*N*N+k]=1;
mat[r][N*N*N*N+j*N*N+k]=1;
mat[r][2*N*N*N*N+t*N*N+k]=1;
mat[r][3*N*N*N*N+p+1]=1;
a[r]=p;
b[r]=s[i][j];
int main()
freopen("sudoku.txt","r",stdin);
freopen("sudoku_sol.txt","w",stdout);
for (int i=0;i<N*N;i++)
scanf("%s",s[i]);
memset(mat,0,sizeof(mat));
makegraph();
c=N*N*N*N*4;
init();
ans=INF;
dfs(0);
for (int i=0;i<ans;i++)
for (int j=i+1;j<ans;j++)
if (a[O[i]]>a[O[j]]) swap(O[i],O[j]);
for (int i=0;i<ans;i++)
printf("%c",b[O[i]]);
if ((i+1)%(N*N)==0) printf("\n");
fclose(stdin);
fclose(stdout);
return 0;
你可以试试:)
【讨论】:
R、S、b、k、a、p...使用一些真实的变量名!以上是关于2021.11.2-测试T1数独的主要内容,如果未能解决你的问题,请参考以下文章