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 博士拿出了他最近发明的 ...
随机推荐
- C#的扩展方法(this)
先在StringLibrary类中定义一个静态方法,如下: public static class StringLibrary { //第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰 ...
- eclipse 导入gradle引入多模块项目,引入eclipse后变成了好几个工程
1.eclipse 导入gradle 项目 ,选择项目文件夹. 2.导入完成后,文档结构变成 ,多个子项目并列了,而且互不依赖,没有层级结构了. 3.点击项目目录,右上角这个小箭头,选择projec ...
- PowerShell 官方下载地址
PowerShell 官方下载地址 Powershell 目前在 GitHub 维护, 所以下载地址为 https://github.com/PowerShell/PowerShell/release ...
- pytorch中文文档-torch.nn常用函数-待添加-明天继续
https://pytorch.org/docs/stable/nn.html 1)卷积层 class torch.nn.Conv2d(in_channels, out_channels, kerne ...
- Codeforces round 1086
Div1 528 我菜哭了.jpg 这个C的出题人能不能停止出题啊QaQ A 这不是裸的斯坦纳树嘛! 然后我就写上了.jpg 然后我没调出来... 然后我发现...这不是傻逼题吗... 直接按照$x$ ...
- python open 函数的读写追加
- scala的多种集合的使用(8)之队列和栈的操作方法
1.使用队列 队列是一种那个先进先出的队列.1)创建一个队列. scala> import scala.collection.mutable.Queue import scala.collect ...
- EntityFramework Core笔记:保存数据(4)
1. 基本保存 每个DBContext实例都有一个ChangeTracker,负责跟踪需要写入数据库的更改.当实例发生更改时,更改会被记录在ChangeTracker中,在调用 SaveChanges ...
- Centos7 安装gitLab
我这里使用的是centos 7 64bit,我试过centos 6也是可以的! 1. 安装依赖软件 yum -y install policycoreutils openssh-server open ...
- Spring MVC 使用介绍(十三)数据验证 (一)基本介绍
一.消息处理功能 Spring提供MessageSource接口用于提供消息处理功能: public interface MessageSource { String getMessage(Strin ...