【NOIP2009】靶形数独 DLX(Dancing Links)

9 篇文章 0 订阅
5 篇文章 0 订阅

 [NOIP2009]靶形数独 T4

Time Limit: 2 Sec   Memory Limit: 128 MB

Description

  小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
  靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有9 个3 格宽×3 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入1 到9 的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)

  上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红色区域)每个格子为9 分,再外面一圈(蓝色区域)每个格子为8 分,蓝色区域外面一圈(棕色区域)每个格子为7 分,最外面一圈(白色区域)每个格子为6 分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为2829。游戏规定,将以总分数的高低决出胜负。

  由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。

Input

  输入文件一共 9 行。每行9 个整数(每个数都在0—9 的范围内),表示一个尚未填满的数独方格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。

Output

  输出文件 共1 行。
  输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-1。

Sample Input

Input I7 0 0 9 0 0 0 0 11 0 0 0 0 5 9 0 00 0 0 2 0 0 0 8 00 0 5 0 2 0 0 0 30 0 0 0 0 0 6 4 84 1 3 0 0 0 0 0 00 0 7 0 0 2 0 9 02 0 1 0 6 0 8 0 40 8 0 5 0 4 0 1 2Input II0 0 0 7 0 2 4 5 39 0 0 0 0 8 0 0 07 4 0 0 0 5 0 1 01 9 5 0 8 0 0 0 00 7 0 0 0 0 0 2 50 3 0 5 7 9 1 0 80 0 0 6 0 1 0 0 00 6 0 9 0 0 0 0 10 0 0 0 0 0 0 0 6

Sample Output

Output I2829Output II2852

HINT

【数据范围】
  40%的数据,数独 中非0 数的个数不少于30。
  80%的数据,数独中非0 数的个数不少于26。
  100%的数据,数独中非0 数的个数不少于24。

Source



题意:求所有的数独填法中按本题要求 求得 的权值中的最大值。

题解:就是怒求所有的数独填法,然后取min就好了。不过正常的暴搜需要很强的剪枝,还是来写一发裸的DLX吧。

没有任何技巧,就是裸DLX。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 800
#define M 400
#define NN 10000
#define inf 0x3f3f3f3f

#define Li_Sdk 3
#define Gi_Sdk 9
#define Su_Sdk 81
using namespace std;
int TS[Gi_Sdk+1][Gi_Sdk+1];
const int score[Gi_Sdk+1][Gi_Sdk+1]=
{
	{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}
};
struct DLX
{
	int elist,eline;
	int id[Gi_Sdk+1][Gi_Sdk+1][Gi_Sdk+1];
	int eid[4][Gi_Sdk][Gi_Sdk];
	bool map[M][N];

	int U[NN],D[NN],L[NN],R[NN],C[NN],V[NN];
	int H[N],T[M],cnt,ans;

	inline void init()
	{
		int i,j,k,_i,_j;
		for(i=1;i<=Gi_Sdk;i++)
			for(j=1;j<=Gi_Sdk;j++)
				for(k=1;k<=Gi_Sdk;k++)
					id[i][j][k]=++eline;
		for(i=1;i<=Gi_Sdk;i++)/*行*/
		{
			for(j=1;j<=Gi_Sdk;j++)/*数*/
			{
				int A=eid[0][i][j]=++elist;
				for(k=1;k<=Gi_Sdk;k++)/*列*/
				{
					int B=id[i][k][j];
					map[A][B]=1;
				}
			}
		}
		for(i=1;i<=Gi_Sdk;i++)/*列*/
		{
			for(j=1;j<=Gi_Sdk;j++)/*数*/
			{
				int A=eid[1][i][j]=++elist;
				for(k=1;k<=Gi_Sdk;k++)/*行*/
				{
					int B=id[k][i][j];
					map[A][B]=1;
				}
			}
		}
		for(i=0;i<Li_Sdk;i++)for(j=0;j<Li_Sdk;j++)/*九宫格*/
		{
			for(k=1;k<=Gi_Sdk;k++)/*数*/
			{
				int A=eid[2][i*Li_Sdk+j+1][k]=++elist;
				for(_i=1;_i<=Li_Sdk;_i++)for(_j=1;_j<=Li_Sdk;_j++)/*格内点*/
				{
					int B=id[i*Li_Sdk+_i][j*Li_Sdk+_j][k];
					map[A][B]=1;
				}
			}
		}
		for(i=1;i<=Gi_Sdk;i++)for(j=1;j<=Gi_Sdk;j++)/*点的位置*/
		{
			int A=eid[3][i][j]=++elist;
			for(k=1;k<=Gi_Sdk;k++)/*点的9个数*/
			{
				int B=id[i][j][k];
				map[A][B]=1;
			}
		}
	}
	inline void clear()
	{
		ans=cnt=0;
		memset(U,0,sizeof(U));
		memset(D,0,sizeof(D));
		memset(L,0,sizeof(L));
		memset(R,0,sizeof(R));
		memset(C,0,sizeof(C));
		memset(H,0,sizeof(H));
		memset(T,0,sizeof(T));
	}
	inline void newnode(int x,int y)
	{
		C[++cnt]=y;V[cnt]=x;T[y]++;

		if(!H[x])H[x]=L[cnt]=R[cnt]=cnt;
		else L[cnt]=H[x],R[cnt]=R[H[x]];
		R[H[x]]=L[R[H[x]]]=cnt,H[x]=cnt;

		U[cnt]=U[y],D[cnt]=y;
		U[y]=D[U[y]]=cnt;
	}
	inline void remove(int x)
	{
		for(int i=D[x];i!=x;i=D[i])
		{
			for(int j=R[i];j!=i;j=R[j])
			{
				U[D[j]]=U[j];
				D[U[j]]=D[j];
				T[C[j]]--;
			}
		}
		L[R[x]]=L[x];
		R[L[x]]=R[x];
	}
	inline void resume(int x)
	{
		for(int i=U[x];i!=x;i=U[i])
		{
			for(int j=L[i];j!=i;j=L[j])
			{
				U[D[j]]=j;
				D[U[j]]=j;
				T[C[j]]++;
			}
		}
		L[R[x]]=x;
		R[L[x]]=x;
	}
	inline void build()
	{
	//	clear();
		int i,j,k;
		cnt=4*Su_Sdk;
		for(i=1;i<=cnt;i++)
		{
			U[i]=D[i]=i;
			L[i]=L[0],R[i]=0;
			L[0]=R[L[0]]=i;
		}
		for(i=1;i<=Gi_Sdk;i++)for(j=1;j<=Gi_Sdk;j++)
		{
			int get=(i-1)*Gi_Sdk+j-1;
			if(!TS[i][j])
			{
				for(k=get*Gi_Sdk+1;k<=get*Gi_Sdk+Gi_Sdk;k++)
					for(int temp=1;temp<=elist;temp++)
						if(map[temp][k])newnode(k,temp);
			}
			else
			{
				k=get*Gi_Sdk+TS[i][j];
				for(int temp=1;temp<=elist;temp++)
					if(map[temp][k])newnode(k,temp);
			}
		}
	}
	inline void dfs(int w)
	{
		
		if(!R[0])
		{
			ans=max(ans,w);
			return;
		}
		int S=R[0],W=T[S],i,j;
		for(i=R[S];i;i=R[i])if(T[i]<W)
		{
			W=T[i];
			S=i;
		}
		remove(S);
		for(i=D[S];i!=S;i=D[i])
		{
			int sc=score[(V[i]-1)/Su_Sdk+1][(V[i]-1)/Gi_Sdk%Gi_Sdk+1];
			for(j=R[i];j!=i;j=R[j])remove(C[j]);
			dfs(w+((V[i]-1)%Gi_Sdk+1)*sc);
			for(j=L[i];j!=i;j=L[j])resume(C[j]);
		}
		resume(S);
		return ;
	}
}dlx;
int main()
{
//	freopen("test.in","r",stdin);
//	freopen("my.out","w",stdout);
	dlx.init();
	for(int i=1;i<=Gi_Sdk;i++)
		for(int j=1;j<=Gi_Sdk;j++)
			scanf("%d",&TS[i][j]);
	dlx.build();
	dlx.dfs(0);
	printf("%d\n",dlx.ans>0?dlx.ans:-1);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值