题目来源:http://poj.org/problem?id=3074
★我是真滴菜,这题又搞了三天,最后又是一个小细节问题。但愿这次之后真正理解了舞蹈链
题意:
有一个9x9的矩阵,有些位置的数字已经确定了下来,但还有很多地方没确定。现在你要找到一个满足如下条件的填数字的方法,然后输入最终填完的矩阵
①:每个单元有且仅有一个数字
②:每一列包含了1-9的9个数字
③:每一行包含了1-9的9个数字
④:将矩阵划分为9块,每一块都包含了1-9的9个数字
思路:
这题可以用 舞蹈链求精确覆盖,由于模板题在HUSTOJ上但是不能提交了,我也没写过模板题,导致卡了三天,不过现在大概是清楚了
先把模型转化成舞蹈链
题目给了4个约束条件,我们可以顺藤摸瓜,将约束条件转化成列,根据题意很容易知道,必须 找到一系列的行使得 所有的列上都有且仅有一个1 那么那些行就是答案了
约束条件①:( x , y )单元被填过了 代表第p1列
int p1=(x-1)*9+y;
约束条件②:第x行有一个数字v 代表第p2列
int p2=(x-1)*9+v+81;
约束条件③:第y列有一个数字v 代表第p3列
int p3=(y-1)*9+v+81*2;
约束条件④:第tmp块有一个数字v 代表第p4列
int tmp=((x-1)/3)*3+(y-1)/3+1; int p4=(tmp-1)*9+v+81*3;
至于行,就是每一个单元放什么数字咯~
得出答案
我们dance之后会得到很多行,但是这些行并不能直接得到题目要求的答案
我们不妨保存一个get数组记录每一行代表的意义(坐标以及数字),然后就好解决了
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
using namespace std;
const int N=81*9*4+81*4+10;
const int M=81*4+10;
const int inf=0x7fffffff;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1);
typedef long long LL;
template<class T>
void read(T &x)
{
char c; x=1;
while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
T res=c-'0';
while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
x*=res;
}
struct DLX
{
int n,m,cnt;
int up[N],down[N],left[N],right[N],col[N];
int sz[M],head[M<<1],get[N][3],ans[100];
void init(int nn,int mm)
{
n=nn; m=mm;
for(int i=0;i<=m;i++){
up[i]=down[i]=i;
right[i]=i+1;
left[i]=i-1;
}
right[m]=0; left[0]=m;
cnt=m;
memset(head,-1,sizeof head);
memset(sz,0,sizeof sz);
}
void link(int r,int c,int x,int y,int v)
{
cnt++;
col[cnt]=c;
sz[c]++;
up[cnt]=up[c];
down[cnt]=c;
down[up[c]]=cnt;
up[c]=cnt;
get[cnt][0]=x; get[cnt][1]=y; get[cnt][2]=v;
if(head[r]==-1) head[r]=right[cnt]=left[cnt]=cnt;
else{
int tmp=head[r];
right[cnt]=tmp;
left[cnt]=left[tmp];
right[left[tmp]]=cnt;
left[tmp]=cnt;
}
}
void del(int c)
{
right[left[c]]=right[c];
left[right[c]]=left[c];
for(int i=down[c];i!=c;i=down[i]){
for(int j=right[i];j!=i;j=right[j]){
up[down[j]]=up[j];
down[up[j]]=down[j];
sz[col[j]]--;
}
}
}
void res(int c)
{
right[left[c]]=left[right[c]]=c;
for(int i=down[c];i!=c;i=down[i]){
for(int j=right[i];j!=i;j=right[j]){
up[down[j]]=down[up[j]]=j;
sz[col[j]]++;
}
}
}
bool dance(int dep)
{
// cout<<dep<<endl;
if(dep==81) return 1;
if(right[0]==0) return 0;
int now=right[0];
for(int i=now;i!=0;i=right[i]){
if(sz[i]<sz[now]) now=i;
}
del(now);
for(int i=down[now];i!=now;i=down[i]){
ans[dep]=i;
for(int j=right[i];j!=i;j=right[j]) del(col[j]);
if(dance(dep+1)) return 1;
for(int j=left[i];j!=i;j=left[j]) res(col[j]);
}
res(now);
return 0;
}
}dlx;
char s[100];
void hhh(int v,int x,int y,int &d)
{
d++;
int p1=(x-1)*9+y; //这个点放过没
int p2=(x-1)*9+v+81; //这个行放过几
int p3=(y-1)*9+v+81*2; //这个列放过几
int tmp=((x-1)/3)*3+(y-1)/3+1; //在第几块
int p4=(tmp-1)*9+v+81*3; //这一块放过几
// cout<<d<<' '<<p1<<' '<<p2<<' '<<p3<<' '<<p4<<endl;
dlx.link(d,p1,x,y,v);
dlx.link(d,p2,x,y,v);
dlx.link(d,p3,x,y,v);
dlx.link(d,p4,x,y,v);
}
int main()
{
while(~scanf("%s",s)){
if(s[0]=='e') break;
dlx.init(81*9,81*4);
int r=0;
for(int i=0;i<81;i++){
int x=i/9+1;
int y=i%9+1;
if(s[i]=='.'){
for(int i=1;i<=9;i++){
hhh(i,x,y,r);
}
}
else hhh(s[i]-'0',x,y,r);
}
dlx.dance(0);
int ans[10][10];
for(int i=0;i<81;i++){
int k=dlx.ans[i];
ans[dlx.get[k][0]][dlx.get[k][1]]=dlx.get[k][2];
}
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
printf("%d",ans[i][j]);
}
}
printf("\n");
}
}