dlx 精确覆盖

dlx的合集比赛点击这里  hust

poj 3074 标准数独模版

#include <cstdio>
#include <cstring>
/**
在计算科学理论中,这一类问题的解答被称之为NPC问题中的Hitting Set Problem,中文名应该叫做碰集问题。
该类问题可以通过转换成为精确覆盖问题,其中以行表示概然,以列表示常规约束。
在数独问题中,行所表示的概然状态很明显为(r,c,k)即行,列,放置的数字。而列所表示的约束大致整理了下,
分做四种,即r行中放置数k可行性,c列放置数k可行性,放置于(r,c)格子可行性以及块b(即所属区域)放置数k的可行性。
因此行总共有N*N*N=9*9*9=729个,列总共有9*9*4=324个,问题转化为在729*324的矩阵中取若干行,使每个列只有一个'1',
此时对应一个数独的解,而(r,c)格的约束保证了我们最后解的行数一定<=N*N。至此,模型转化完成。
**/
const int maxn=100000+123;
const int maxc=1000+5;

int S[maxc], L[maxn], R[maxn], D[maxn], U[maxn];
int H[maxc], ok[maxc], sz, C[maxn], mark[maxn];
///H是横向的表头, 不知道必不必要
void Link(int row, int col){
    S[col]++; C[sz]=col;///C域指向列头
    U[sz]=U[col]; D[U[col]]=sz;
    D[sz]=col; U[col]=sz;
    
    if(H[row]==-1)H[row]=L[sz]=R[sz]=sz;
    else{
        L[sz]=L[H[row]]; R[L[H[row]]]=sz;
        R[sz]=H[row]; L[H[row]]=sz;
    }
    mark[sz]=row;/// 标记每个点是哪一行(题目要求输出解属于哪一行)
    sz++;
}

void remove(int col)
{
    L[R[col]]=L[col];
    R[L[col]]=R[col]; /// 在列对象链表中删除col
    for (int i=D[col]; i!=col; i=D[i]){/// 删除col列中有1元素的行
        for (int j=R[i]; j!=i; j=R[j]){///删除每行的1元素,并修改所在列的S域
            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]){
            U[D[j]]=j; D[U[j]]=j;
            S[C[j]]++;
        }///恢复删除的元素,恢复S域
    }///恢复删除的行
    L[R[col]]=col;
    R[L[col]]=col;
}

bool Dance(int k){
    if(R[0]==0){
        char ans[100];
        for (int i=0; i<k; ++i){
        	ans[(ok[i]-1)/9]=(ok[i]-1)%9+1+'0';
        }
        ans[k]=0;
        puts(ans);
        return true;
    }
    int c=R[0];
    for(int i=R[0]; i; i=R[i])
        if(S[i]<S[c])c=i;
    remove(c);
    for(int i=D[c]; i!=c; i=D[i]){
        ok[k]=mark[i];
        for (int j=R[i]; j!=i; j=R[j])
            remove(C[j]);
        if(Dance(k+1))return true;
        for (int j=L[i]; j!=i; j=L[j])
            resume(C[j]);
    }
    resume(c);
    return false;
}

void initL(int x){
    for (int i=0; i<=x; ++i){
        S[i]=0;
        D[i]=U[i]=i;
        L[i+1]=i; R[i]=i+1;
    }///对列表头初始化
    R[x]=0;
    sz=x+1;///真正的元素从m+1开始
    memset (H, -1, sizeof(H));
    ///mark每个位置的名字
}
const int m=9*9*4;
const int n=9*9*9;
int main()
{/// 1~81:row col  82~162:row k 163~243:col k 244~324:block k
    char str[100];
    while(~scanf("%s", str)){
    	if(str[0]=='e')break;
        initL(m);
        for (int i=0; i<9; ++i){
        	for (int j=0; j<9; ++j){
        		int pos=i*9+j;
        		if(str[pos]=='.'){
        			for (int k=1; k<=9; ++k){
        				int row=pos*9+k;
        				Link(row, pos+1);
        				int col=81+i*9+k;
        				Link(row, col);
        				col=81*2+j*9+k;
        				Link(row, col);
        				col=81*3+((i/3)*3+j/3)*9+k;
        				Link(row, col);
        			}
        		}
        		else {
        			int k=str[pos]-'0';
					int row=pos*9+k;
					Link(row, pos+1);
					int col=81+i*9+k;
					Link(row, col);
					col=81*2+j*9+k;
					Link(row, col);
					col=81*3+((i/3)*3+j/3)*9+k;
					Link(row, col);
        		}
        	}
        }
        Dance(0);
    }
    return 0;
}


poj 3076 16*16数独 代码不贴了




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值