题目描述 Description

小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他
们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向Z 博士请教,
Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有9 个3 格宽×3 格
高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些

数字,利用逻辑推理,在其他的空格上填入1 到9 的数字。每个数字在每个小九宫格内不能
重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即
每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。

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

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

输入描述 Input Description

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

输出描述 Output Description

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

样例输入 Sample Input

【输入输出样例 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

【输入输出样例 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

样例输出 Sample Output

【输入输出样例 1】

2829

【输入输出样例 1】

2852

数据范围及提示 Data Size & Hint

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

思路:
(摘自sth课件)

我们看每个空白的格子,看看它还可以填几个数。 如果是我们在做数独的话,我们显然会先从可以填的数少的格子开始试。 所以我们可以找出每个格子可以填几个数,然后排序,先从可以填的数少的格子开始搜。

可行性剪枝:显然,这道题的不合法解就是同一行或同一列或同一个九宫格出现了相同数字,所以我们只需要知道一个格子可以填哪些数,然后只搜索这些可以填的数就可以了

最优性剪枝: 如果当前已有得分+未来可能得到的最大的分<=当前已得到的最大总分(已经找到的合法解中的最优解),则直接退出。显然未来得分不可能超过(没有填的格子数*90)。

我们再想一下,爆搜的大部分时间都浪费在了哪? 在找这个格子能够填哪些数。 这个问题能够快速解决吗? 搜索问题中,解决这种问题有一个通用方法,就是:位运算!

既然每行每列每个九宫格一共就9个数,我们就记录一下哪些数没用过。 一共9个数,想到了啥? 2^9 压位! 若第i位的二进制为1,则表示第i个数在这一行/这一列/这个九宫格还没有出现过,还可以使用。

那我们怎么找一个格子所在的该行该列该九宫格都还未出现过的数字? 三个信息and一下就可以了。 假设得到的是x,那么,x的二进制上为1的就是我们可以在这个格子里填的数字。

怎么遍历可以填的所有数字?也就是x的所有二进制位1的位?显然不能一位一位的遍历。。。。那样就成了暴力了。。。。我们希望只遍历二进制是1的位。 For (;x!=0;x-=x&-x) { y=log[x&-x]+1;//+1是因为数字是1~9,不是0~8 ……. ……. }

注意,在给这个格子选择某个数填上,dfs进入下一层之前,我们要先修改该行该列该九宫格还能填的剩余数字。 Dfs回到上一层之后,也记得修改该行该列该九宫格还能填的剩余数字。 For (;x!=0;x-=x&-x) { y=log[x&-x]+1; 记录这个格子填y,将该格子得分加入当前总分,并将该行该列该九宫格记录的还能填的数字信息and (2^10-1-(x&-x)) dfs(………); 将该格子得分从当前总分中减去,并将该行该列该九宫格记录的还能填的数字信息or (x&-x) }

代码:

①自己写的,用了map

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define mx 111
#define maxint 10000
using namespace std;
struct Node{
int x;
int y;
int a;
};
vector<Node> node;
int a[mx][mx],line[mx],col[mx],pal[mx],block[mx][mx],ok[mx][mx],sub[mx],Log[maxint],lft,maxans,ansleft;
int s_jud[][] = {,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,};
int g_jud[][] = {,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,
,,,,,,,,,};
bool cmp(Node x,Node y){
return x.a < y.a;
}
void init(){
lft = maxans = ansleft = ;
int vd = pow(,) - ,acc;
memset(line,vd,sizeof(line));
memset(col,vd,sizeof(col));
memset(block,vd,sizeof(block));
memset(pal,vd,sizeof(pal));
memset(ok,,sizeof(ok));
for(int c = ,x = ;c < maxint;c *= ,x++) Log[c] = x;
for(int i = ;i <= ;i++) sub[i] = vd - pow(,i-);
for(int i = ; i <= ;i++){
for(int j = ;j <= ;j++){
cin>>a[i][j];
if(a[i][j]){
lft++;
line[i] = line[i] & sub[a[i][j]];
col[j] = col[j] & sub[a[i][j]];
pal[g_jud[i-][j-]] = pal[g_jud[i-][j-]] & sub[a[i][j]];
ok[i][j] = ;
ansleft += a[i][j] * s_jud[i-][j-];
}
}
}
lft = - lft;
Node temp;
for(int i = ;i <= ;i++){
for(int j = ;j <= ;j++){
block[i][j] = line[i] & col[j] & pal[g_jud[i-][j-]];
acc = ;
for(int x = block[i][j];x!=;x-=x&-x) acc++;
if(ok[i][j]) continue;
temp.y = i;
temp.x = j;
temp.a = acc;
node.push_back(temp); }
}
sort(node.begin(),node.end(),cmp);
}
void opt_init(){
for(int i = ;i <lft;i++){
cout<<"No."<<i<<" ("<<node[i].x<<","<<node[i].y<<") "<<node[i].a<<endl;
}
}
int dfs(int deep,int score){
if(deep > lft){
maxans = max(score,maxans);
return score;
} int nowx = node[deep-].x,nowy = node[deep-].y,test;
int x = line[nowy] & col[nowx] & pal[g_jud[nowy-][nowx-]],y;
for(;x!=;x-=x&-x){
y=Log[x&-x]+;
score += y * s_jud[nowy-][nowx-];
test = pow(,) - - (x&-x);
line[nowy] = line[nowy] & test;
col[nowx] = col[nowx] & test;
pal[g_jud[nowy-][nowx-]] = pal[g_jud[nowy-][nowx-]] & test;
dfs(deep+,score);
score -= y * s_jud[nowy-][nowx-];
line[nowy] = line[nowy] | (x&-x);
col[nowx] = col[nowx] | (x&-x);
pal[g_jud[nowy-][nowx-]] = pal[g_jud[nowy-][nowx-]] | (x&-x);
}
}
int main(){
init();
dfs(,);
if(maxans) cout<<maxans + ansleft<<endl;
else cout<<-<<endl;
return ;
}

②ida*

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define MAX(a,b) a>b?a:b
using namespace std; int Map[][] = {}; //这个就不用解释了吧
int Nine[][] = {}; //小九宫格里每个数的使用标记,小九宫格的编号左到右,上到下,1,2,3,4,5,6,7,8,9
int Line[][] = {}; //行上每个数的使用标记
int Row[][] = {}; //列上每个数的使用标记
int FQueL[] = {}; //被启发后的行访问顺序
int FQueR[] = {}; //被启发后的列访问顺序
int Value[][] = {}; //地图各点的价值因子
int QueL[] = {}; //
int QueR[] = {}; //与上面那个,是根据启发后制定的搜索顺序的行列参数
int QLen = ; //搜索队列的长度
int Ans = -; //最优解
int Count = ; //废物变量,纪念原来我错误的写法,懒得删掉了 int Swap(int *a,int *b)
{
int o=*a;
*a=*b;
*b=o;
} void Heu() //启发函数
{
for(int i=;i<=;++i){
FQueL[i] = i;
FQueR[i] = i;
} for(int i=;i<=;++i)
for(int j=;j>=i+;--j){
if(Line[FQueL[j]][] > Line[FQueL[j-]][])
Swap(&FQueL[j],&FQueL[j-]);
if(Row[FQueR[j]][] > Row[FQueR[j-]][])
Swap(&FQueR[j],&FQueR[j-]);
}
for(int i=;i<=;++i)
for(int j=;j<=;++j)
if(Map[FQueL[i]][FQueR[j]] == ){
QueL[QLen]=FQueL[i];
QueR[QLen]=FQueR[j];
QLen++;
}
// for(int i=1;i<=9;++i)
// cout<<FQueL[i]<<' '<<Line[FQueL[i]][0]<<' '<<FQueR[i]<<' '<<Row[FQueR[i]][0]<<endl;
} int belong(int i,int j) //判断行列参数为i,j的点属于哪一个九宫格。
{
int xt = ,yt = ;
for(int k=;k>=;k-=){
if(i-k>){xt=(k+)/;break;}
}
for(int k=;k>=;k-=){
if(j-k>){yt=(k+)/;break;}
}
// cout<<xt<<' '<<yt<<' '<<xt+(yt-1)*3<<endl;
// cout<<i<<' '<<j<<' '<< yt+(xt-1)*3 <<endl;
return yt+(xt-)*;
} int Score() //成绩计算
{
int Temp = ;
for(int i=;i<=;++i)
for(int j=;j<=;++j)
Temp += Map[i][j]*Value[i][j];
Ans = MAX(Temp,Ans);
// cout<<Ans<<endl;
} int Dfs(int step) //深度优先搜索主体
{
// cout<<step<<' '<<81-Count<<endl;
// for(int i=1;i<=9;++i,cout<<endl)
// for(int j=1;j<=9;++j)
// cout<<Map[i][j]<<' ';
if(step == QLen){Score();return ;}
if(!Map[QueL[step]][QueR[step]])
for(int k=;k<=;++k){
if(!Nine[belong(QueL[step],QueR[step])][k])
if(!Line[QueL[step]][k] && !Row[QueR[step]][k]){
//Heu();
// cout<<FQueL[i]<<' '<<FQueR[j]<<' '<<k<<endl;
Map[QueL[step]][QueR[step]] = k;
Nine[belong(QueL[step],QueR[step])][k] = ;
Line[QueL[step]][k]=Row[QueR[step]][k] = ;
Line[QueL[step]][]++;Row[QueR[step]][]++;
Dfs(step+);
Map[QueL[step]][QueR[step]] = ;
Nine[belong(QueL[step],QueR[step])][k] = ;
Line[QueL[step]][k]=Row[QueR[step]][k] = ;
Line[QueL[step]][]--;Row[QueR[step]][]--;
}
}
return ;
} int main()
{
for(int i=;i<=;++i)
for(int j=+(i-);j<=-(i-);++j){
Value[i][j] = +(i-);
Value[-(i-)][j] = +(i-);
Value[j][-(i-)] = +(i-);
Value[j][i] = +(i-);
} //init value 对价值表的初始,好像其他人都是直接用{}初始的.......
// for(int i=1;i<=9;++i,cout<<endl)
// for(int j=1;j<=9;++j)
// cout<<Value[i][j]<<' ';
for(int i=;i<=;++i)
for(int j=,x,y;j<=;++j){
scanf("%d",&Map[i][j]);
if(Map[i][j] != ){
Line[i][Map[i][j]] = ;
Line[i][]++;
Row[j][Map[i][j]] = ;
Row[j][]++;
Nine[belong(i,j)][Map[i][j]] = ;
Count++;
}
} // for(int i=1;i<=9;++i,cout<<endl)
// for(int j=1;j<=9;++j)
// cout<<Nine[i][j]; Heu();
Dfs(); cout<<Ans;
return ;
}

codevs1174 靶形数独的更多相关文章

  1. Vijos1775 CodeVS1174 NOIP2009 靶形数独

    靶形数独 描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z博士请教, Z 博士拿出了他最近发 ...

  2. NOIP2009靶形数独[DFS 优化]

    描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z博士请教,Z 博士拿出了他最近发明的“靶形数独 ...

  3. 靶形数独(codevs 1174)

    1174 靶形数独 2009年NOIP全国联赛提高组  时间限制: 4 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题解  查看运行结果     题目描述 Descri ...

  4. 洛谷 P1074 靶形数独 Label:search 不会

    题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了他最近发明的 ...

  5. 【CodeVS】p1174 靶形数独

    题目描述 Description 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向Z 博士请教,Z 博士 ...

  6. [NOIP2009] 靶形数独(搜索+剪枝)

    题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了他最近发明的 ...

  7. 靶形数独 (codevs 1174)题解

    [问题描述] 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向Z 博士请教,Z 博士拿出了他最近发明的“ ...

  8. NOIP2009 靶形数独

    4.靶形数独 (sudoku.pas/c/cpp) [问题描述] 小城和小华都是热爱数学的好学生, 近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了, ...

  9. Luogu1074靶形数独【启发式搜索】

    Luogu1074靶形数独 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, ...

随机推荐

  1. 前端javascript 错误 Uncaught SyntaxError: Unexpected token ILLEGAL

    前端控制台报Uncaught SyntaxError: Unexpected token ILLEGAL 错误时,就是非法字符错误,首先检查符号是否正确,不要出现中文标点! 然后检查参数之类的类型是否 ...

  2. Asp.NET 知识点总结(二)

    1.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对? 答:不对,有相同的 hash code 编码格式. 2.swtich是否能作用在byte ...

  3. Shape Drawable Resources

    1,示例 它们的代码如下: shape_oval.xml <?xml version="1.0" encoding="utf-8"?> <sh ...

  4. 无法连接到已配置的web服务器

    问题:如题,asp.net WebForm的项目,在vs中选择aspx文件在浏览器中查看时候回报该错误. 网上最多的解决方案是防火墙的原因,说关闭防火墙就可以了.但问题是我另一个项目没有问题啊,所以不 ...

  5. iOS基础笔试题 - 集锦一

    前言 下文转载自https://mp.weixin.qq.com/s?__biz=MzA4ODk0NjY4NA==&mid=454115946&idx=1&sn=c7f1b50 ...

  6. IIS配置负载均衡

    一.下载Nginx安装包 二.修改nginx.conf文件信息 如图: 三.重新加载Nginx (nginx -s reload) 启动Nginx: start nginx 停止Nginx:nginx ...

  7. get、post、put、delete、head请求方式

    对资源的增,删,改,查操作,其实都可以通过GET/POST完成,不一定要用PUT和DELETE. 一:Jersey框架,实现了restful风格,常用的注解@GET.@POST.@PUT.@DELET ...

  8. 呼啦圈(keyframes和transform结合)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. Java———较大二进制文件的读、写

    由于项目需要,需要对二进制文件进行读写.转换. 文件说明:由其他程序得到的二进制文件,文件内容为:包含23543个三角形.13270个顶点的三角网所对应的721组流速矢量(u.v)文件,通俗些说,一条 ...

  10. QT,折腾的几天-----关于 QWebEngine的使用

    几天前,不,应该是更早以前,就在寻找一种以HTML5+CSS+Javascript的方式来写桌面应用的解决方案,为什么呢?因为前端那套可以随心所欲的写样式界面啊,恩.其实我只是想使用H5的一些新增功能 ...