转自:https://www.cnblogs.com/wust-ouyangli/p/5750581.html
题意:
给出一个9*9的矩阵,有一些格子已经填了数,有一些是.代表未填。求任意一组解使得每行包含1 ~ 9,每列包含1 ~ 9,每个小矩形(3 * 3)包含1 ~ 9。
解析:
精确覆盖DLX的经典题目,每一行代表要填数的情况,列共有81*4列,第一个81行代表第i行j列放了数,第二个81列代表第i行放的数k,第三个81列代表第j列放的数k,第四个81行代表第i个小矩形放的数k。对于字符为 . 的情况添加9行,对于字符为数字的情况添加一行。然后就是跑一遍DLX,保存一下答案。
code:
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int INF=1e9+7;
const int ms=81*10;
const int maxn=ms*4;
int ans[maxn];
struct DLX
{
int n,id;
int L[maxn],R[maxn],U[maxn],D[maxn];
int C[maxn],S[maxn],loc[maxn][3];
int H[ms];
void init(int nn=0)
{
n=nn;
for(int i=0;i<=n;i++) U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
L[0]=n; R[n]=0;
id=n;
memset(S,0,sizeof(S));
memset(H,-1,sizeof(H));
}
void Link(int x,int y,int px,int py,int k)
{
++id;
D[id]=y; U[id]=U[y];
D[U[y]]=id; U[y]=id;
loc[id][0]=px,loc[id][1]=py,loc[id][2]=k;
C[id]=y; S[y]++;
if(H[x]==-1) H[x]=L[id]=R[id]=id;
else
{
int a=H[x];
int b=R[a];
L[id]=a; R[a]=id;
R[id]=b; L[b]=id;
H[x]=id;
}
}
void Remove(int c)
{
L[R[c]]=L[c];
R[L[c]]=R[c];
for(int i=D[c];i!=c;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 c)
{
for(int i=U[c];i!=c;i=U[i])
for(int j=R[i];j!=i;j=R[j])
{
S[C[j]]++;
U[D[j]]=j;
D[U[j]]=j;
}
L[R[c]]=c;
R[L[c]]=c;
}
bool dfs(int step)
{
if(step==81) return true;
if(R[0]==0) return false;
int Min=INF,c=-1;
for(int i=R[0];i;i=R[i])
if(Min>S[i]){ Min=S[i]; c=i; }
Remove(c);
for(int i=D[c];i!=c;i=D[i])
{
ans[step]=i;
for(int j=R[i];j!=i;j=R[j]) Remove(C[j]);
if(dfs(step+1)) return true;
for(int j=L[i];j!=i;j=L[j]) Resume(C[j]);
}
Resume(c);
return false;
}
}dlx;
int main()
{
char S[90];
while(scanf("%s",S)!=EOF)
{
if(S[0]=='e') break;
dlx.init(81*4);
int k=0,r=0;
for(int x=0;x<9;x++)
for(int y=0;y<9;y++)
{
char ch=S[k++];
int a,b,c,d;
if(ch=='.')
{
for(int i=1;i<=9;i++)
{
a=x*9+y+1;
b=x*9+i+81;
c=y*9+i+81+81;
int s=(x/3)*3+y/3;
d=s*9+i+81+81+81;
++r;
dlx.Link(r,a,x,y,i);
dlx.Link(r,b,x,y,i);
dlx.Link(r,c,x,y,i);
dlx.Link(r,d,x,y,i);
}
}
else
{
int i=ch-'0';
a=x*9+y+1;
b=x*9+i+81;
c=y*9+i+81+81;
int s=(x/3)*3+y/3;
d=s*9+i+81+81+81;
++r;
dlx.Link(r,a,x,y,i);
dlx.Link(r,b,x,y,i);
dlx.Link(r,c,x,y,i);
dlx.Link(r,d,x,y,i);
}
}
dlx.dfs(0);
int res[10][10];
for(int i=0;i<81;i++)
{
int a=ans[i];
int x=dlx.loc[a][0],y=dlx.loc[a][1],k=dlx.loc[a][2];
res[x][y]=k;
}
for(int i=0;i<9;i++)
for(int j=0;j<9;j++) printf("%d",res[i][j]);
printf("\n");
}
return 0;
}
题意:
一个数独求解,与一般数独不同的是此数独用墙把81个数分成了9 个宫,每个宫都有1到9 。样例中每个数独的数最多是由四个墙和本身的数组成
对于输出,数独无解则输出无解,有多个解则输出多个解,右一个解则输出补全的数独。
思路:
与一般的数独解法大致相同,不同的是此题首先用搜索将数独中的数分好宫,然后是当有解时,保存答案。
code:
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <iostream>
#define mod 1000000007
#define eps 1e-6
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=10240;
struct DLX
{
int n,id;
int L[maxn],R[maxn],U[maxn],D[maxn];
int C[maxn],S[maxn],loc[maxn][3];//C代表列,S代表每列有的数字数量,loc代表这个数在数独中的位置和数值
int H[maxn],ans[maxn],ansed;//ansed表示解的个数,ans记录答案
int fans[maxn],fansed;//复制的ansed,ans数组
void init(int nn)
{
n=nn;
for(int i=0;i<=n;i++)
{
U[i]=D[i]=i;
L[i]=i-1;
R[i]=i+1;
}
L[0]=n; R[n]=0;
id=n;
ansed=0;
fansed=0;
memset(S,0,sizeof(S));
memset(H,-1,sizeof(H));
}
void Link(int x,int y,int px,int py,int k)
{
++id;
D[id]=y; U[id]=U[y];
D[U[y]]=id; U[y]=id;
loc[id][0]=px,loc[id][1]=py,loc[id][2]=k;//存放数的位置和数
C[id]=y;
S[y]++;//此列1的数量加一
if(H[x]==-1) H[x]=L[id]=R[id]=id;
else
{
int a=H[x];
int b=R[a];
L[id]=a; R[a]=id;
R[id]=b; L[b]=id;
H[x]=id;
}
}
void Remove(int c)
{
L[R[c]]=L[c];
R[L[c]]=R[c];
for(int i=D[c];i!=c;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 c)
{
for(int i=U[c];i!=c;i=U[i])
for(int j=R[i];j!=i;j=R[j])
{
S[C[j]]++;
U[D[j]]=j;
D[U[j]]=j;
}
L[R[c]]=c;
R[L[c]]=c;
}
void fuzhi()//复制已经得到的数独,
{
fansed=ansed;
for(int i=0;i<81;i++)
{
fans[i]=ans[i];
}
}
void dfs(int step)
{
if(R[0]==0&&step==81)
{
ansed++;
fuzhi();
return ;
}
int c=R[0];
for(int i=R[0];i;i=R[i])//优先循环1的数量少的一列
{
if(S[i]<S[c])
{
c=i;
}
}
Remove(c);
for(int i=D[c];i!=c;i=D[i])
{
ans[step]=i;
for(int j=R[i];j!=i;j=R[j]) Remove(C[j]);
dfs(step+1);
if(ansed>=2)//当解多于两个时已是多解,不需要再循环了
{
return ;
}
for(int j=L[i];j!=i;j=L[j]) Resume(C[j]);
}
Resume(c);
}
}dlx;
struct node
{
int fxi[4],gong; //fxi表示四个方向,gong表示所在的第几宫
int x,y,num; //x,y表示行列,num表示数
};
node sd[10][10];
int bj[10][10];
int fx[4]={0,1,0,-1},fy[4]={-1,0,1,0};
void bfs(int x,int y,int z) //搜索同一宫的位置
{
queue<node> qu;
node no=sd[x][y];
bj[x][y]=1;
qu.push(no);
while(!qu.empty())
{
no=qu.front();
qu.pop();
for(int k=0;k<4;k++)
{
int i=no.x+fx[k];
int j=no.y+fy[k];
if(!no.fxi[k]&&bj[i][j]==0) //没有墙并且没走过
{
sd[i][j].gong=z;
bj[i][j]=1;
node s=sd[i][j];
qu.push(s);
}
}
}
}
int main()
{
int t,ans=0; //ans表示第几个测试任务
scanf("%d",&t);
while(t--)
{
ans++;
memset(sd,0,sizeof(sd));
int js=0;
dlx.init(81*4);
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
scanf("%d",&sd[i][j].num);
sd[i][j].x=i;
sd[i][j].y=j;
if(sd[i][j].num>=128)//左
{
sd[i][j].num-=128;
sd[i][j].fxi[0]=1;
}
if(sd[i][j].num>=64)//下
{
sd[i][j].num-=64;
sd[i][j].fxi[1]=1;
}
if(sd[i][j].num>=32)//右
{
sd[i][j].num-=32;
sd[i][j].fxi[2]=1;
}
if(sd[i][j].num>=16)//上
{
sd[i][j].num-=16;
sd[i][j].fxi[3]=1;
}
}
}
js=0;
memset(bj,0,sizeof(bj));
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
if(bj[i][j]==0)//循环所有数独
{
bfs(i,j,js);
sd[i][j].gong=js;
js++;
}
}
}
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
int a,b,c,d;
if(sd[i][j].num==0)
{
for(int k=1;k<=9;k++)
{
a = i*9+j+1;
b = i*9+k+81;
c = j*9+k+81+81;
d = sd[i][j].gong*9+k+81+81+81;
js++;
dlx.Link(js,a,i,j,k);
dlx.Link(js,b,i,j,k);
dlx.Link(js,c,i,j,k);
dlx.Link(js,d,i,j,k);
}
}
else
{
int k=sd[i][j].num;
a=i*9+j+1;
b=i*9+k+81;
c=j*9+k+81+81;
d=sd[i][j].gong*9+k+81+81+81;
js++;
dlx.Link(js,a,i,j,k);
dlx.Link(js,b,i,j,k);
dlx.Link(js,c,i,j,k);
dlx.Link(js,d,i,j,k);
}
}
}
dlx.dfs(0);
int num=dlx.fansed;
if(num==0)//无解
{
printf("Case %d:\nNo solution\n",ans);
}
else if(num>1)//多解
{
printf("Case %d:\nMultiple Solutions\n",ans);
}
else//一个解
{
int no[10][10];
for(int i=0;i<81;i++)
{
int a=dlx.fans[i];
int x=dlx.loc[a][0];
int y=dlx.loc[a][1];
int k=dlx.loc[a][2];
no[x][y]=k;
}
printf("Case %d:\n",ans);
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
printf("%d",no[i][j]);
}
printf("\n");
}
}
}
}