题目描述 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. Linux学习系列八:操作网口

    一些相对高性能的单片机会带以太网接口,网口在MCU里算是比较复杂的外设了,因为它涉及到网络协议栈,通常情况下网络协议栈会运行在一个RTOS中,所以对普通单片机开发者来说网口使用起来相对难度较大一些.在 ...

  2. LOGO免费在线设计

    http://www.logomaker.com.cn/ 藏经阁技术资料分享群二维码

  3. 启动tomcat报错:ImageFormatException

    启动某工程报错: java.lang.NoClassDefFoundError: com/sun/image/codec/jpeg/ImageFormatException 查找此类存在于jdk的rt ...

  4. 292 Nim Game Nim游戏

    您和您的朋友,两个人一起玩 Nim游戏:桌子上有一堆石头,每次你们轮流拿掉 1 到 3 块石头. 拿掉最后一块石头的人就是胜利者.由您来开局.你们两个都是聪明人,相信都有最佳的游戏策略. 请编写一个函 ...

  5. 网站开发综合技术 第一部分HTML 1.3.2表单

    <form id="" name="" method="post/get" action="负责处理的服务端"&g ...

  6. 6.12---知道参数的重要性------插入数据-删除数据-修改数据注意Map

    ---------------

  7. angular6 NG-ZORRO 的使用

    1:关于 NG-ZORRO中使用它自己组件改变样式时得使用样式穿透 “class” :: ng-deep "class"

  8. 2) 十分钟学会android--建立第一个APP,执行Android程序

    通过上一节课创建了一个Android的Hello World项目,项目默认包含一系列源文件,它让我们可以立即运行应用程序. 如何运行Android应用取决于两件事情:是否有一个Android设备和是否 ...

  9. Android基础TOP6_3:Gally和ImageSwitcher实现画廊

    结构: Activity: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ...

  10. JSON字符串的生成

    public class Corporation { public string remark { get; set; } public string version { get; set; } pu ...