NOIP 2009 提高组 第四题——靶型数独正解

NOIP 2009 提高组 第四题——靶型数独正解

乍眼一看,有点蒙(毕竟本人是蒟蒻嘛)
本人蒟蒻,所以只能写出蒟蒻程序。

言归正传:

本题为深搜题;

本题的重点——从哪里开始搜索;(这是一个剪枝的机会)
解决方法:
用结构体排序,两个参量,一个用于储存0的个数,另一个用于储存行号,例如

0 0 0 2 0 0 0 8 0 //7个
7 0 0 9 0 0 0 0 1 //6个
1 0 0 0 0 5 9 0 0 //6个
0 0 5 0 2 0 0 0 3 //6个
0 0 0 0 0 0 6 4 8 //6个
4 1 3 0 0 0 0 0 0 //6个
0 0 7 0 0 2 0 9 0 //6个
2 0 1 0 6 0 8 0 4 //4个
0 8 0 5 0 4 0 1 2 //4个

如果从第一行开始扫,肯定没有从最后一行扫要快(可以计算一下)
所以,本蒟蒻就定义了以下结构体:

struct node
{
	int num1;//储存行号
	int num2;//储存零的个数
}

有了这个剪枝,与不怕超时了(可能还有其他剪枝,但这一个就够了)

AC代码:

#include <bits/stdc++.h>
using namespace std;

int Queue1[110];//存储行
int Queue2[110];//存储列
int Queue3[110];//存储宫
int Queue4[110];//存储在靶中的分数

int INnumber[10][10];//输入数据
bool Palace[10][10];//判断宫
bool Line1[10][10];//判断行
bool Line2[10][10];//判断列

int ACanswer=-1;//答案
const int N=9;//大小
int Num=1;//记录0在数据中出现的总个数

struct Int
{
	int num1;
	int num2;
}NMnumber[10];//用于排序

int Pala(int x,int y)//此函数是用于判断第x行,第y列的所在宫
{
	if(x<=3)
	{
		if(y<=3){return 1;}
		else if(3<y&&y<=6){return 2;}
		else if(6<y&&y<=9){return 3;}
	}
	else if(3<x&&x<=6)
	{
		if(y<=3){return 4;}
		else if(3<y&&y<=6){return 5;}
		else if(6<y&&y<=9){return 6;}
	}
	else if(6<x&&x<=9)
	{
		if(y<=3){return 7;}
		else if(3<y&&y<=6){return 8;}
		else if(6<y&&y<=9){return 9;}
	}
}

int Mark(int x,int y)//此函数是用于判断第x行,第y列的靶型数独的分值
{
	if((x==1)||(x==N)||(y==1)||(y==N)){return 6;}
	if((x==2)||(x==N-1)||(y==2)||(y==N-1)){return 7;}
	if((x==3)||(x==N-2)||(y==3)||(y==N-2)){return 8;}
	if((x==4)||(x==N-3)||(y==4)||(y==N-3)){return 9;}
	return 10;
}

void Depth_First_Search(int mark,int num)//总分数,扫到的0个数
{
	if(num==Num+1){ACanswer=max(ACanswer,mark);return;}//刷新
	
	for(int i=1;i<=N;i++)
	{
		if(Line1[Queue1[num]][i]==false)//判断行是否被占领
		{
			if(Line2[Queue2[num]][i]==false)//判断列是否被占领
			{
				if(Palace[Queue3[num]][i]==false)//判断宫是否被占领
				{
					Line1[Queue1[num]][i]=true;
					Line2[Queue2[num]][i]=true;
					Palace[Queue3[num]][i]=true;//占领
					
					Depth_First_Search(mark+i*Queue4[num],num+1);
					
					Line1[Queue1[num]][i]=false;
					Line2[Queue2[num]][i]=false;
					Palace[Queue3[num]][i]=false;//回溯
				}
			}
		}
	}
}

bool cmp(Int Num1,Int Num2)
{
	return Num1.num2<Num2.num2;
}

int main(void)
{
	int Sum=0;
	for(int i=1;i<=N;i++)
	{NMnumber[i].num1=i;}//记录行号
	
	for(int i=1;i<=N;i++)
	{
		for(int j=1;j<=N;j++)
		{
			cin>>INnumber[i][j];
			if(INnumber[i][j]>0)
			{
				Line1[i][INnumber[i][j]]=true;//在所在行标记
				Line2[j][INnumber[i][j]]=true;//在所在列标记
				Palace[Pala(i,j)][INnumber[i][j]]=true;//在所在宫标记
				Sum=Sum+INnumber[i][j]*Mark(i,j);//将已知数的分数存起来
			}
			else{NMnumber[i].num2++;}//记录0在每一行的个数,用于排序
		}
	}
	
	sort(NMnumber+1,NMnumber+1+N,cmp);//进行排序
	
	for(int i=1;i<=N;i++)
	{
		for(int j=1;j<=N;j++)
		{
			if(INnumber[NMnumber[i].num1][j]==0)
			{
				Queue2[Num]=j;//存储列
				Queue1[Num]=NMnumber[i].num1;//存储行
				Queue3[Num]=Pala(NMnumber[i].num1,j);//存储宫
				Queue4[Num]=Mark(NMnumber[i].num1,j);//存储分数
				Num++;//记录零的个数
			}
		}
	}
	Num--;
	
	Depth_First_Search(Sum,1);//搜索
	
	cout<<ACanswer<<endl;//输出
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值