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

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

由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。
输入输出格式
输入格式:
一共 9 行。每行9个整数(每个数都在0−9 的范围内),表示一个尚未填满的数独方格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。
输出格式:
输出共 1 行。输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数−1。
输入输出样例
输入样例#1:
7 0 0 9 0 0 0 0 1
1 0 0 0 0 5 9 0 0
0 0 0 2 0 0 0 8 0
0 0 5 0 2 0 0 0 3
0 0 0 0 0 0 6 4 8
4 1 3 0 0 0 0 0 0
0 0 7 0 0 2 0 9 0
2 0 1 0 6 0 8 0 4
0 8 0 5 0 4 0 1 2
输出样例#1:
2829
输入样例#2:
0 0 0 7 0 2 4 5 3
9 0 0 0 0 8 0 0 0
7 4 0 0 0 5 0 1 0
1 9 5 0 8 0 0 0 0
0 7 0 0 0 0 0 2 5
0 3 0 5 7 9 1 0 8
0 0 0 6 0 1 0 0 0
0 6 0 9 0 0 0 0 1
0 0 0 0 0 0 0 0 6
输出样例#2:
2852
说明
【数据范围】
40%的数据,数独中非 0 数的个数不少于30。
80%的数据,数独中非 0 数的个数不少于26。
100%的数据,数独中非0数的个数不少于24。
NOIP 2009 提高组 第四题
这真是一道恶心的搜索题,首先你得先熟悉数独。
玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复。
————百度百科
所以我们有三个数组
分别代表行,列,九宫格中数字使用情况。
行和列都还好,循环判断即可,但九宫格,就需要一个函数
int ninth( int i , int j ) {
if( i <= && j <= ) return ;
if( i <= && j <= ) return ;
if( i <= ) return ;
if( i <= && j <= ) return ;
if( i <= && j <= ) return ;
if( i <= ) return ;
if( j <= ) return ;
if( j <= ) return ;
return ;
}
接着我们处理每个格子的分数,我是用函数判断,而ly用数组,最后时间来看,我还是太蒟了
inline int check(int x,int y)
{
if(x==||y==||x==||y==)return ;
if(x==||y==||x==||y==)return ;
if(x==||y==||x==||y==)return ;
if(x==||y==||x==||y==)return ;
if(x==&&y==)return ;
} ————by cx
int point[ ] = { , , , , , } ;
————by ly
预处理完后便可以开始考虑如何搜索,这里我与ly有了不同的搜法。
1.ly
ly选择的是一行一行搜下去,全部答案搜出来后,再求最大值。
int dfs( int h , int x , int y ) {
if( h == ) {
print( );
return ;
}
if( y == ) {
dfs( h + , sss[ h + ].line , );
return ;
}
if( !map[ x ][ y ] ) {
for( int i = ; i <= ; ++i ) {
if( line[ x ][ i ] == && list[ y ][ i ] == && nine[ ninth( x , y ) ][ i ] == ){
line[ x ][ i ] = , list[ y ][ i ] = , nine[ ninth( x , y ) ][ i ] = ;
map[ x ][ y ] = i ;
dfs( h , x , y + );
map[ x ][ y ] = ;
line[ x ][ i ] = , list[ y ][ i ] = , nine[ ninth( x , y ) ][ i ] = ;
}
}
}
else dfs( h , x , y + );
}
2.cx
我选择的是用一个数组存要填的的点,一个一个搜,我一开始以为我的方法会快一点,结果我被打脸了
void dfs(int x,int y)
{
if(tot==pos-){maxn=max(ans,maxn);return;}
for(register int i=;i<=;++i)
{
if(!line[x][i]&&!list[y][i]&&!nine[ninth(x,y)][i])
{
pos++;ans+=i*check(x,y);
line[x][i]=list[y][i]=;
nine[ninth(x,y)][i]=;
dfs(b[][pos].w,b[][pos].w);
pos--;ans-=i*check(x,y);
line[x][i]=list[y][i]=;
nine[ninth(x,y)][i]=;
}
}
}
接下来便是重点,搜索剪枝
像我这种没玩过数独的乡里人,不知道玩数独有这样一个方法:
从数多的一行开始填,这样要选择的数就少了,不合法的情况就可以省掉一些
所以我们定义一个(struck),用一个
来存每行
的个数。
用它作为关键字一遍后,再从最少的开始搜。
ly程序:(用时: 2678ms / 内存: 920KB)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std ; inline int read( ) {//日常快读
int x = , f = ;
char c = getchar( ) ;
while( c > '' || c < '' ) {
if( c == '-' ) f = - ;
c = getchar( );
}
while( c >= '' && c <= '' ) x = x * + c - '' , c = getchar( ) ;
return f == ? x : -x ;
} int ninth( int i , int j ) {//判断i行j列在第几个九宫格里,因为九个九宫格不重复,所以某些地方可以少判断一些条件。
if( i <= && j <= ) return ;
if( i <= && j <= ) return ;
if( i <= ) return ;
if( i <= && j <= ) return ;
if( i <= && j <= ) return ;
if( i <= ) return ;
if( j <= ) return ;
if( j <= ) return ;
return ;
} int point[ ] = { , , , , , } ;//存放分数;
int map[ ][ ] ;//记录某个位置上的数;
long long ans = - ;//无解则输出-1;
struct node {
int line , sum ;
}sss[ ] ;//记录每行需要填的零的个数;
bool cmp( node i , node j ) {
return i.sum < j.sum ;
}
bool line[ ][ ] , nine[ ][ ] , list[ ][ ] ;//进行数独游戏的判断;
//为了方便观察,函数都扔下去;
int dfs( int , int , int );
int print( ); int main( )
{
for( int i = ; i <= ; ++i ) {
int k = ;
for( int j = ; j <= ; ++j ) {
map[ i ][ j ] = read( ) ;
if( !map[ i ][ j ] ) ++k;
line[ i ][ map[ i ][ j ] ] = ;
nine[ ninth( i , j ) ][ map[ i ][ j ] ] = ;
list[ j ][ map[ i ][ j ] ] = ;
}
sss[ i ].sum = k , sss[ i ].line = i ;
}
sort( sss + , sss + , cmp );
dfs( , sss[ ].line , ) ;
printf( "%lld" , ans );
return ;
}
int dfs( int h , int x , int y ) {
if( h == ) {//全部搜完了并成立,进行算分
print( );
return ;
}
if( y == ) {//为避免特判过多而加的中转;
dfs( h + , sss[ h + ].line , );
return ;
}
if( !map[ x ][ y ] ) {
for( int i = ; i <= ; ++i ) {
if( line[ x ][ i ] == && list[ y ][ i ] == && nine[ ninth( x , y ) ][ i ] == ){
line[ x ][ i ] = , list[ y ][ i ] = , nine[ ninth( x , y ) ][ i ] = ;
map[ x ][ y ] = i ;
dfs( h , x , y + );
//记得回溯
map[ x ][ y ] = ;
line[ x ][ i ] = , list[ y ][ i ] = , nine[ ninth( x , y ) ][ i ] = ;
}
}
}
else dfs( h , x , y + );
}
int print( ) {//统计当前方案的分数
long long sum = ;
for( int i = ; i <= ; ++i ) {
for( int j = ; j <= ; ++j ) {
sum += ( map[ i ][ j ] * point[ min( min( i , - i ) , min ( j , - j ) ) ] );
//越靠近中心,x与y越接近5,否则远离5
//所以min( min( i , 10 - i ) , min ( j , 10 - j ) )与
//point数组搭配即可算出当前位置的分值
}
}
ans = max( ans , sum );//更新最大值
}
cx程序:(用时: 3361ms / 内存: 928KB)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int max(int x,int y){return(x)<(y)?(y):(x);}
bool line[][],list[][],nine[][];
int ans=,tot=,maxn=-,pos=,Line=,a[];
//a数组记录每行0的个数,maxn记录最大值,tot记录总共要填的点数
struct node
{int w,list;}b[][];//list记录那行0的个数
inline void read(int &x)
{
x=;int f=;
char ch=getchar();
while(ch<''||ch>'')
{if(ch=='-') f=-; ch=getchar();}
while(ch>=''&&ch<='')
{x=x*+ch-'';ch=getchar();}
x*=f;
}
inline void write(int x)
{
if(x<){putchar('-');write(~x+);}
else{if(x>)write(x/);putchar(x%+'');}
}
inline int ninth(int i,int j)
{
if(i<=&&j<=)return ;if(i<=&&j<=)return ;if(i<=)return ;
if(i<=&&j<=)return ;if(i<=&&j<=)return ;if(i<=)return ;
if(j<=)return ;if(j<=)return ;return ;
}
inline int check(int x,int y)
{
if(x==||y==||x==||y==)return ;
if(x==||y==||x==||y==)return ;
if(x==||y==||x==||y==)return ;
if(x==||y==||x==||y==)return ;
if(x==&&y==)return ;
}
void dfs(int x,int y)
{
if(tot==pos-){maxn=max(ans,maxn);return;}//搜完了
for(register int i=;i<=;++i)//判断数字1-9
{
if(!line[x][i]&&!list[y][i]&&!nine[ninth(x,y)][i])//判断数字是否被填过
{
pos++;ans+=i*check(x,y);
line[x][i]=list[y][i]=;
nine[ninth(x,y)][i]=;
dfs(b[][pos].w,b[][pos].w);
pos--;ans-=i*check(x,y);
line[x][i]=list[y][i]=;
nine[ninth(x,y)][i]=;//回溯
}
}
}
bool cmp(node i,node j)
{return i.list<j.list;}
int main()
{
bool flag1=,flag2=;
for(register int i=;i<=;++i)
{
for(register int j=;j<=;++j)
{
register int k;read(k);
if(!k)b[][tot+].w=i,b[][tot+].w=j,tot++,Line++;//代表要填
else//代表填过
{
if(i==||i==)if(j==)if(k==||k==)flag1=flag2=;
if((i==&&j==&&k==))flag1=;if(i==&&j==&&k==)flag2=;
ans+=k*check(i,j);nine[ninth(i,j)][k]=;
line[i][k]=,list[j][k]=;
}
}
a[i]=Line;Line=;//记录每行0的个数
}
if(!flag1&&!flag2)//特判,为什么后面讲了
{
for(register int i=;i<=tot;++i)b[][i].list=b[][i].list=a[b[][i].w];
std::sort(b[]+,b[]+tot+,cmp);
std::sort(b[]+,b[]+tot+,cmp);//按行中0的个数排序
}
dfs(b[][pos].w,b[][pos].w);//搜索
write(maxn);
}
因为我的搜法不同,在之后仍有两个点过不去,所以我特判了一下不
的情况,仔细反思一下,我好像懂了。
ly是一行行搜,所以他一直搜同一行,而我存的是点,又因为不稳定,我的程序可能搜完一个,又去搜另一行,所以不行,兴许可以改成
或桶排,相较之下,很明显还是ly程序更优。
P1074 靶形数独题解的更多相关文章
- P1074 靶形数独
P1074 靶形数独正着搜80分,完全倒置95分,完全倒置后左右再倒置,就会A掉,到时候脑洞要大一些. #include<iostream> #include<cstdio> ...
- 洛谷——P1074 靶形数独
P1074 靶形数独 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z ...
- 洛谷P1074 靶形数独 [搜索]
题目传送门 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了 ...
- 洛谷 P1074 靶形数独 Label:search 不会
题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了他最近发明的 ...
- P1074 靶形数独 dfs回溯法
题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶 ...
- [洛谷P1074] 靶形数独
洛谷题目链接:靶形数独 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博 ...
- 洛谷 P1074 靶形数独
题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了他最近发明的 ...
- [NOIP2009] 提高组 洛谷P1074 靶形数独
题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了他最近发明的 ...
- luogu P1074 靶形数独
题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了他最近发明的 ...
随机推荐
- pipe size设置
我所用的软件架构,使用pipe来实现线程之间的大量数据的传输.在实际操作中,pipe中传输的是数据的指针,而不是数据本身. 但是在调试过程中,我发现,如果我尝试往pipe里面write10000个指 ...
- ctrl+shift+r / ctrl+f5 强制(不使用缓存)刷新google chrome网页
我改了csdn图片后, 一直看到的是旧图片, n天之后, 还是旧图片.猜测应该是用了缓存(且缓存更新逻辑失败, 定是csdn的bug), 用ctrl+shift+r, 或者ctrl+f5, 强制刷新页 ...
- 在Winform开发中使用Grid++报表
之前一直使用各种报表工具,如RDLC.DevExpress套件的XtraReport报表,在之前一些随笔也有介绍,最近接触锐浪的Grid++报表,做了一些测试例子和辅助类来处理报表内容,觉得还是很不错 ...
- dedecms织梦的不同栏目调用不同banner图的方法
在做织梦站的时候我们会有不同的栏目,比如联系我们,产品中心等等,banner也不一样,方法如下: 我们可以使用织梦的顶级栏目ID标签,把图片命名成顶级栏目typeid ,代码如下: <img s ...
- java+testng接口测试入门
testNG是一个测试框架,它能组织测试用例按照你想要的方式进行运行,并输出一定格式的便于阅读的测试报告(结果),通过java+testng的方式说明一下接口测试的基本使用方法. 一.环境搭建 a)千 ...
- 在Asp.Net Core中集成Kafka
在我们的业务中,我们通常需要在自己的业务子系统之间相互发送消息,一端去发送消息另一端去消费当前消息,这就涉及到使用消息队列MQ的一些内容,消息队列成熟的框架有多种,这里你可以读这篇文章来了解这些MQ的 ...
- jquery ajax几种书写方式的总结
Ajax在前端的应用极其广泛,因此,我们有必要对其进行总结,以方便后期的使用. AJAX优点: 可以异步请求服务器的数据,实现页面数据的实时动态加载, 在不重新加载整个页面的情况下,可以与服务器交换数 ...
- apache+php项目部署
先安装apache和php然后进行如下操作(以63服务器的安装路径为例) 1.查看php项目运行的报错信息 路径: cd /var/log/httpd/error_log 如果错误如下: 可以尝试 ...
- NLP语义匹配
参考资料 [搜狗语义匹配技术前沿]https://www.jiqizhixin.com/articles/2018-10-25-16?from=synced&keyword=%E6%90%9C ...
- 支持“XXX”上下文的模型已在数据库创建后发生更改。请考虑使用 Code First 迁移更新数据库(http://go.microsoft.com/fwlink/?LinkId=238269)。
在Global.asax文件中的Application_Start()方法中加入以下代码 Database.SetInitializer<XXX>(null);