zoukankan      html  css  js  c++  java
  • P1784 数独 题解

    CSDN同步

    原题链接

    前置知识:

    浅谈 ( ext{Dancing Links X}) 算法

    注:这次的前置知识如果你不会又不看,代码和思路肯定都看不懂的。

    简要题意:

    填满一个未完成的数独。

    首先数独的规则是:

    1. 每行所填数不得重复,为 (1) ~ (9) 之间。

    2. 每列所填数不得重复,为 (1) ~ (9) 之间。

    3. 每宫所填数不得重复,为 (1) ~ (9) 之间。

    首先这是一个数学游戏,但是如果你想在 (1s) 之内 ( ext{AK}) 吊打全场,那么就要学习如何解决数独问题。

    下面我们来说说,怎么把它扯到 精确覆盖 上面呢。

    决策应该是形如 ((i,j,x)) 的三元组,表示 (a_{i,j} = x).

    (i) 行只能用一个 (x),需要 (9 imes 9 = 81) 列。(每个格子都要开一列,对应 (1) ~ (81) 列)

    (j) 列只能用一个 (x) ,再开 (9 imes 9 = 81) 列。(每个格子都要开一列,对应 (82) ~ (162) 列)

    ((i,j)) 所在的宫只能用一个 (x),再开 (9 imes 9 = 81) 列。(每个格子都要开一列,对应 (162) ~ (243) 列)

    ((i,j)) 只能填一个数(千万不要忽略这个!),再开 (9 imes 9 = 81) 列。(每个格子都要开一列,对应 (244) ~ (324) 列)

    那么多少行呢?显然,(9^3 = 729) 行,因为每个行、列、宫都要决策一次。

    所以我们就将数独问题转化成一个 (729 imes 324) 的矩阵上有 (729 imes 4 = 3216)(1)精确覆盖问题

    然后直接套板子,主要建图即可。

    时间复杂度:(O(wys)).(很优但难以说明,严格来说是 (O(c^n)) ,其中 (n leq 3216)(c) 为极其接近 (1) 的常数)

    实际得分:(100pts).

    通过时间:(11ms).(看到了吧,精确覆盖比搜索剪枝快十几倍)

    // 板子部分不再解释
    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=1e5+1; 
    #define FOR(i,A,x) for(int i=A[x];i!=x;i=A[i])
    
    inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
    
    int n,m,id,fir[N],siz[N];
    int L[N],R[N],U[N],D[N];
    int col[N],row[N],ans;
    int stk[N];
    
    inline void remove(int x) {
    //	printf("remove : %d
    ",x);
    	L[R[x]]=L[x]; R[L[x]]=R[x];
    	FOR(i,D,x) FOR(j,R,i) U[D[j]]=U[j],D[U[j]]=D[j],--siz[col[j]];
    }
    
    inline void recover(int x) {
    //	printf("recover : %d
    ",x);
    	FOR(i,U,x) FOR(j,L,i) U[D[j]]=D[U[j]]=j,++siz[col[j]];
    	L[R[x]]=R[L[x]]=x;
    }
    
    inline void build(int x,int y) {
    //	printf("build : %d %d
    ",x,y);
    	n=x,m=y; for(int i=0;i<=y;i++)
    		L[i]=i-1,R[i]=i+1,U[i]=D[i]=i;
    	L[0]=y,R[y]=0,id=y;
    	memset(fir,0,sizeof(fir));
    	memset(siz,0,sizeof(siz));	
    }
    
    inline void insert(int x,int y) {
    //	printf("insert : %d %d
    ",x,y);
    	col[++id]=y,row[id]=x,++siz[y];
    //	U[id]=D[id]=y,U[D[y]]=id,D[y]=id;
    	D[id]=D[y],U[D[y]]=id,U[id]=y,D[y]=id;
    	if(!fir[x]) fir[x]=L[id]=R[id]=id;
    	else R[id]=R[fir[x]],L[R[fir[x]]]=id,L[id]=fir[x],R[fir[x]]=id;
    }
    
    int a[1001][1001];
    
    inline bool dance(int dep) {
    //	printf("dance : %d
    ",dep);
    	if(!R[0]) {
    		for(int i=1;i<dep;i++) {
    			int x=(stk[i]-1)/9/9+1,y=(stk[i]-1)/9%9+1,z=(stk[i]-1)%9+1;
    			a[x][y]=z; //算出行 , 列 , 宫 , 存储答案
    		} return 1;
    	}
    	int wz=R[0]; FOR(i,R,0) if(siz[i]<siz[wz]) wz=i;
    	remove(wz); FOR(i,D,wz) {
    		stk[dep]=row[i]; FOR(j,R,i) remove(col[j]);
    		if(dance(dep+1)) return 1;
    		FOR(j,L,i) recover(col[j]);
    	} recover(wz); return 0;
    }
    
    int main(){
    	build(729,324);
    	for(int i=1;i<=9;i++) for(int j=1;j<=9;j++) {
    		a[i][j]=read();
    		for(int k=1;k<=9;k++) {
    			if(a[i][j]-k && a[i][j]) continue;
    			int t=((i-1)*9+(j-1))*9+k;
    			insert(t,(i-1)*9+j);
    			insert(t,81+(i-1)*9+k);
    			insert(t,243+(j-1)*9+k);
    			insert(t,162+((i-1)/3*3+(j-1)/3)*9+k); //建图
    		}
    	} dance(1);
    	for(int i=1;i<=9;i++) {
    		for(int j=1;j<=9;j++) printf("%d ",a[i][j]);
    		puts("");
    	} 
    	return 0;
    }
    
    
  • 相关阅读:
    两种选择排序法
    三种方法求组合偶数字
    sizeof和mallo
    多态的概念与接口重用
    Delphi Exception的理解
    给老婆留着
    Delphi 一个简单的DELPHI自定义事件的例子
    Delphi 纯Pascal编写的程序,没有通过VCL
    Delphi 继承类的构造函数和析构函数的构建顺序
    Delphi 对象间数据的复制
  • 原文地址:https://www.cnblogs.com/bifanwen/p/12666653.html
Copyright © 2011-2022 走看看