hihocoder1321 搜索五·数独
2018-01-25 20:33
260 查看
DLX
题目传送门DLX的一个经典应用。
首先我们需要把这个数独问题转化为一个精确覆盖问题。
根据数独的规则,我们有如下几个限制:
1.每个数字在每一行只能出现一次。限制编号1~81
2.每个数字在每一列只能出现一次。限制编号82~162
3.每个数字在每一个九宫格内只能出现一次。限制编号163~243
4.每一格只能填其中一个数字。限制编号244~364
这些限制就是列。
而数独的全部填法一共有9*81=729种。这些填法就是行。
原问题就转化为一个729*364大小的01矩阵的精确覆盖问题。
加一个优化:每次不选择h的右指针。增加一个数组cnt记录当前列的节点个数,每次选择cnt最小的那个。当删除或恢复时相应地更新cnt。
跑一边DLX就好了。
又臭又长的代码:
#include<cstdio> #include<cstring> #include<algorithm> #define N 81*9 #define M 81*4 using namespace std; struct nd{ nd *u,*d,*l,*r; int x,y; }c[M+5],a[N*M+5],*h=&c[0],s,*null=&s; int t,ti,n=N,m=M,cnt[M+5],mp[10][10],ans[10][10],id[N+5][M+5]; bool f[N+5][M+5]; inline void nsrt(int x,int y,int z){//在(x,y)填入的值为k时的限制 int i=(x-1)*9+y,j=(i-1)*9+z; f[j][(x-1)*9+z]=true;//行限制 f[j][81+(y-1)*9+z]=true;//列限制 int k=((x-1)/3*3+(y-1)/3)+1; f[j][162+(k-1)*9+z]=true;//九宫格限制 f[j][243+i]=true;//每一格的限制 } inline void build(){ memset(f,false,sizeof(f)); memset(cnt,0,sizeof(cnt)); for (int i=1;i<=9;i++) for (int j=1;j<=9;j++) if (!mp[i][j])//如果是0的话什么都能填 for (int k=1;k<=9;k++) nsrt(i,j,k); else nsrt(i,j,mp[i][j]);//否则只能填这个 h=&c[0],h->d=h->u=h->l=h->r=h,h->x=h->y=0,ti=0; nd *pre=h; for (int i=1;i<=m;i++){ nd *p=&c[i]; p->u=p,p->d=p,p->x=0,p->y=i; p->r=pre->r,p->l=pre,pre->r->l=p,pre->r=p,pre=p; } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (f[i][j]){ a[id[i][j]=++ti].x=i,a[ti].y=j; a[ti].l=a[ti].r=a[ti].d=a[ti].u=&a[ti]; } for (int j=1;j<=m;j++){ nd *pre=&c[j]; for (int i=1;i<=n;i++) if (f[i][j]){ cnt[j]++; nd *p=&a[id[i][j]]; p->d=pre->d,p->u=pre,pre->d->u=p,pre->d=p,pre=p; } } for (int i=1;i<=n;i++){ nd *pre=null; for (int j=1;j<=m;j++) if (f[i][j]) if (pre==null) pre=&a[id[i][j]]; else{ nd *p=&a[id[i][j]]; p->r=pre->r; p->l=pre,pre->r->l=p,pre->r=p,pre=p; } } } inline void rmv(int x){ nd *p=&c[x],*p1=p->d; p->r->l=p->l,p->l->r=p->r; while (p1!=p){ nd *p2=p1->r; while (p2!=p1) p2->d->u=p2->u,p2->u->d=p2->d,cnt[p2->y]--,p2=p2->r; p1=p1->d; } } inline void rsm(int x){ nd *p=&c[x],*p1=p->d; p->r->l=p->l->r=p; while (p1!=p){ nd *p2=p1->r; while (p2!=p1) p2->d->u=p2->u->d=p2,cnt[p2->y]++,p2=p2->r; p1=p1->d; } } inline nd *find(){//每次选择cnt最小的 nd *p=h->r,*ans=h->r; int mn=0x7fffffff; while (p!=h){ if (cnt[p->y]<mn) mn=cnt[p->y],ans=p; p=p->r; } return ans; } bool dfs(){ if (h->r==h) return true; nd *p=find(),*p1=p->d; if (p1==p) return false; rmv(p->y); while (p1!=p){ //答案数组 ans[(p1->x-1)/81+1][((p1->x-1)/9)%9+1]=(p1->x-1)%9+1; nd *p2=p1->r; while (p2!=p1) rmv(p2->y),p2=p2->r; if (dfs()) return true; p2=p1->l; while (p2!=p1) rsm(p2->y),p2=p2->l; p1=p1->d; } return rsm(p->y),false; } int main(){ scanf("%d",&t); while (t--){ for (int i=1;i<=9;i++) for (int j=1;j<=9;j++) scanf("%d",&mp[i][j]); build(),dfs(); for (int i=1;i<=9;i++){ for (int j=1;j<=8;j++) printf("%d ",ans[i][j]); printf("%d\n",ans[i][9]); } } return 0; }
相关文章推荐
- hihoCoder #1321 : 搜索五•数独 (Dancing Links ,精确覆盖)
- POJ 1321 基础搜索 DFS 二
- 图论, 1.各种方案的最短路径,最小生成树,拓扑排序, 2.隐式图的搜索,N-皇后问题,数独,马踏棋盘,中文划分,回文划分.
- [kuangbin带你飞]专题一 简单搜索 POJ - 1321
- poj-1321(简单搜索)
- 搜索POJ1321
- 靶形数独 2009年NOIP全国联赛提高组(搜索)
- 搜索系列——1321 棋盘问题
- noip2009 靶形数独 (搜索)
- hihoCoder1041—国庆出游(搜索好题)
- POJ-1321 棋盘问题 搜索
- hihocoder 微软编程之美2015 初赛 第一场 (树算法 + 暴力思想 + 搜索思想)
- poj 2676(dfs求解数独问题,对行列和格子分别加bool数组优化搜索)
- 【搜索】靶形数独
- 【hihocoder 1317】搜索四·跳舞链
- 搜索系列——1321 棋盘问题
- poj 1321 简单搜索
- poj2676--Sudoku(搜索练习5-数独游戏)
- hihocoder 1050 树中的最长路(动态规划,dfs搜索)
- 一中OJ #1194 数独 | 回溯 + 搜索 | 解题报告