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 博士拿出了他最近发明的 ...
随机推荐
- Oracle知识点总结2
1.聚合函数:也叫分组函数. 常用聚合函数:返回的都是NUMBER类型的值. 注:避免使用 COUNT(*) ,而使用 COUNT(ROWID) 2.分组统计:group by 字段名 having ...
- Django ORM 使用原生 SQL
使用原生sql的 方法 : raw # row方法:(掺杂着原生sql和orm来执行的操作) res = CookBook.objects.raw('select id as nid from epo ...
- CSAPP:第十章 系统级I/O
CSAPP:第十章 系统级I/O 10.1 unix I/O10.2 文件10.3 读取文件元数据10.4 读取目录内容10.5 共享文件10.6 我们该使用哪些I/O函数? 10.1 unix I/ ...
- UI自动化测试之Jenkins配置
前一段时间帮助团队搭建了UI自动化环境,这里将Jenkins环境的一些配置分享给大家. 背景: 团队下半年的目标之一是实现自动化测试,这里要吐槽一下,之前开发的测试平台了,最初的目的是用来做接口自动化 ...
- [题解]图的m着色问题
图的m着色问题(color) [题目描述] 给定无向连通图G和m种不同的颜色.用这些颜色为图G的各顶点着色,每个顶点着一种颜色.如果有一种着色法使G中每条边的2个顶点着不同颜色,则称这个图是m可着色的 ...
- [转帖]rsync简介
rsync用法详细解释 https://www.cnblogs.com/noxy/p/8986164.html 之前一直使用 scp 现在发现这个命令更好一些. 提要 熟悉 rsync 的功能及其特点 ...
- java基础问题巩固(1)
你对java垃圾回收了解吗?什么时候需要使用? 答: 垃圾回收器的作用是查找和回收(清理)无用的对象,从而让jvm更 有效的使用内存.但是运行因为垃圾回收本身会有开销,过于频繁的使用会导致性能下降.比 ...
- Python基础:编码规范(4)
1.命名规范 Python中不同代码元素采用不同命名方式: ◊ 包名:全部小写字母,中间可以由点分隔开.作为命名空间,包名需具有唯一性. ◊ 模块名:全部小写字母,如果是多个单词构成,使用下划线分隔. ...
- Event-Loop In JS
原文:最后一次搞懂 Event Loop 自打 ES 6 推出 Promise 改善了 js 的异步编程之后,eventloop 也越来越多地出现在视野当中.借用大佬们的话:“Event Loop 是 ...
- ES6中6种声明变量的方法
相关阅读:http://es6.ruanyifeng.com/#docs/let 相关阅读:https://www.cnblogs.com/ksl666/p/5944718.html 相关阅读:htt ...