【搜索】DLX解靶形数独

题目链接:

     https://vijos.org/p/1755

朴素算法:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int sc[10][10]={{0,0,0,0,0,0,0,0,0,0},
                {0,6,6,6,6,6,6,6,6,6},
                {0,6,7,7,7,7,7,7,7,6},
                {0,6,7,8,8,8,8,8,7,6},
                {0,6,7,8,9,9,9,8,7,6},
				{0,6,7,8,9,10,9,8,7,6},
				{0,6,7,8,9,9,9,8,7,6},
				{0,6,7,8,8,8,8,8,7,6},
				{0,6,7,7,7,7,7,7,7,6},
                {0,6,6,6,6,6,6,6,6,6}};
int po[10][10]={{0,0,0,0,0,0,0,0,0,0},
                {0,1,1,1,2,2,2,3,3,3},
                {0,1,1,1,2,2,2,3,3,3},
                {0,1,1,1,2,2,2,3,3,3},
                {0,4,4,4,5,5,5,6,6,6},
				{0,4,4,4,5,5,5,6,6,6},
				{0,4,4,4,5,5,5,6,6,6},
				{0,7,7,7,8,8,8,9,9,9},
				{0,7,7,7,8,8,8,9,9,9},
                {0,7,7,7,8,8,8,9,9,9}};              
int q,ms,t,s;
int a[10][10];bool h[10][10],z[10][10],b[10][10];
int ds[83][2];
void dfs(int d,int sco)
{
	int i,j,Ii;
	//if (t==10000000) return;t++;
	if (d==0) 
	{ 
		if (sco>ms) ms=sco; 
		return;
	}
    i=ds[d][0],j=ds[d][1];
	for (Ii=1;Ii<=9;Ii++)
	if (h[i][Ii]&&z[j][Ii]&&b[po[i][j]][Ii])
	{
		h[i][Ii]=false;z[j][Ii]=false;b[po[i][j]][Ii]=false;
		dfs(d-1,sco+sc[i][j]*Ii);
		h[i][Ii]=true;z[j][Ii]=true;b[po[i][j]][Ii]=true;
	}
}
int main()
{
	q=0;ms=0;t=0;s=0;
	memset(h,1,sizeof(h));
	memset(z,1,sizeof(z));
	memset(b,1,sizeof(b));
	for (int i=1;i<=9;i++)
	for (int j=1;j<=9;j++)
	  { int x;
		scanf("%d",&x);
		a[i][j]=x;
		if (x!=0)
		 {
				h[i][x]=false;
				z[j][x]=false;
				b[po[i][j]][x]=false;
				s+=x*sc[i][j];	
		 }	else 
		{
			q++;
			ds[q][0]=i;
			ds[q][1]=j;
		}		
	  }
	dfs(q,s);
	if (ms==0)printf("-1\n");
	else printf("%d",ms);	
    return 0;	
}



DLX:

#include<iostream>
#include<cstdio>
#define INT_MAX 10000000
#define MR 9*9*9
#define MC 9*9*4
using namespace std;
int Left[MC+MR*9];				//左边的元素
int Right[MC+MR*9];				//右边的元素
int Up[MC+MR*9];				//下边的元素
int Down[MC+MR*9];				//上边的元素
int S[MC];						//列标元素 
int hang[MC+MR*9];				//所在行 
int lie[MC+MR*9];				//所在列 
int head[10][10][10];			
int count;
int map[10][10];
int result[10][10];
int best=-1,SCORE=0;
int score[10][10]=
{
		 {0,0,0,0,0,0,0,0,0,0},
		 {0,6,6,6,6,6,6,6,6,6},
		 {0,6,7,7,7,7,7,7,7,6},
		 {0,6,7,8,8,8,8,8,7,6},
		 {0,6,7,8,9,9,9,8,7,6},
		 {0,6,7,8,9,10,9,8,7,6},
		 {0,6,7,8,9,9,9,8,7,6},
		 {0,6,7,8,8,8,8,8,7,6},
		 {0,6,7,7,7,7,7,7,7,6},
		 {0,6,6,6,6,6,6,6,6,6}
};

void init();
int  getg(int i,int j);
void insert(int c,int count);					//插入节点 
void del(int c);								//暂时删除 
void res(int c);								//恢复节点 
void dfs(int k);								//开始深搜 


int main()
{
        init();
        for (int i=0;i<=81*4;i++)				//初始化 
        {
	            S[i]=0;
	            Left[i]=i-1; Right[i]=i+1;
	            Up[i]=Down[i]=i;
	            lie[i]=0;
        }
        Left[0]=81*4; Right[81*4]=0;
        count=81*4;
        for (int i=1;i<=9;i++)
            for (int j=1;j<=9;j++)
            {
                if (map[i][j])					//处理已占用的格子 
                {
	                    int k=map[i][j];
	                    for (int u=1;u<=4;u++)
	                    {
	                        Left[count+u]=count+u-1;
	                        Right[count+u]=count+u+1;
	                        hang[count+u]=100*i+10*j+k;
	                    }
	                    Left[count+1]=count+4; 
						Right[count+4]=count+1;
	                    head[i][j][k]=count+1;
	                    //cout<<head[i][j][k]<<endl;
	                    insert((i-1)*9+j,count+1);						
	                    insert(81+(i-1)*9+k,count+2);
	                    insert(81*2+(j-1)*9+k,count+3);
	                    insert(81*3+(getg(i,j)-1)*9+k,count+4);
	                    count+=4;
	                   // cout<<count<<endl;
                }
                else							//处理未占用的格子 
                    for (int k=1;k<=9;k++) 
                    {
                        for (int u=1;u<=4;u++)
                        {
	                            Left[count+u]=count+u-1;
	                            Right[count+u]=count+u+1;
	                            hang[count+u]=100*i+10*j+k;
                        }
                        Left[count+1]=count+4; 
						Right[count+4]=count+1;
                        head[i][j][k]=count+1;
                        //cout<<head[i][j][k]<<endl;
                        insert((i-1)*9+j,count+1);
                        insert(81+(i-1)*9+k,count+2);
                        insert(81*2+(j-1)*9+k,count+3);
                        insert(81*3+(getg(i,j)-1)*9+k,count+4);
                        count+=4;
                        //cout<<count<<endl;
                    }
            }
        int k=0;
        for (int i=1;i<=9;i++)				//先加上已有的格子的得分 
            for (int j=1;j<=9;j++)
                if (map[i][j])
                {
                    k++;
                    SCORE+=(map[i][j])*score[i][j];
                    del(lie[head[i][j][map[i][j]]]);
                    for (int u=Right[head[i][j][map[i][j]]];u!=head[i][j][map[i][j]];u=Right[u])
                        del(lie[u]);
                }
        dfs(k+1);
        printf("\n%d\n",best);
        for (int i=1;i<10;i++)
        {
        	for (int j=1;j<10;j++) cout<<result[i][j]<<" ";
    		cout<<endl;
		}
    return 0;
}
void init()
{
    for (int i=1;i<=9;i++) 
		for (int j=1;j<=9;j++)
            scanf("%d",&map[i][j]);			//输入元素
}
int getg(int i,int j)
{
	    i--; 
		j--;
	    return (i/3)*3+(j/3+1);				//获得宫数 
}
void insert(int c,int count)				//插入节点 
{
	    Up[Down[c]]=count;					 
	    Down[count]=Down[c];
	    Up[count]=c;
	    Down[c]=count;
	    S[c]++;
	    lie[count]=c;
}
void del(int c)								//暂时删除 
{
	    Left[Right[c]]=Left[c];				//删左右 
	    Right[Left[c]]=Right[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];
		            S[lie[j]]--;
	        }
}
void res(int c)											//恢复节点 
{
	    for (int i=Up[c];i!=c;i=Up[i])					//逐行恢复 
	        for (int j=Left[i];j!=i;j=Left[j])
	        {
		            Up[Down[j]]=Down[Up[j]]=j;
		            S[lie[j]]++;
	        }
	    Left[Right[c]]=Right[Left[c]]=c;				//左右接起来 
}
void dfs(int k)											//开始深搜 
{
	    if (k>81)
	    {
	    		cout<<endl;
	    		for (int i=1;i<10;i++)
	    		{
	    			for (int j=1;j<10;j++)	cout<<map[i][j]<<" ";
	    			cout<<endl;
		    	}
				if (SCORE>best) 
				{
					best=SCORE;
					for (int i=1;i<10;i++)
	    			for (int j=1;j<10;j++)
	    				result[i][j]=map[i][j];
		    	}
				return;
	    }
	    int c,minnum=INT_MAX;
	    for (int i=Right[0];i!=0;i=Right[i])			//从个数最小的开始(启发式思想) 
	    {
		        if (!S[i]) return;
		        if (S[i]<minnum)
		        {
		            minnum=S[i];
		            c=i;
		        }
	    }
	    del(c);											//删除列。 
	    for (int i=Down[c];i!=c;i=Down[i])				//枚举每一个 
	    {
		        int tmp=hang[i];
		        SCORE=SCORE+(tmp%10)*(score[tmp/100][(tmp/10)%10]);
		        map[tmp/100][(tmp/10)%10]=tmp%10;
		        for (int j=Right[i];j!=i;j=Right[j])
		            del(lie[j]);
		        dfs(k+1);//下一层 
		        SCORE=SCORE-(tmp%10)*(score[tmp/100][(tmp/10)%10]);//回溯 
		        map[tmp/100][(tmp/10)%10]=0;
		        for (int j=Left[i];j!=i;j=Left[j])
		            res(lie[j]);
	    }
	    res(c);
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值