NOIP2009 靶形数独

时间:2021-12-04 11:17:16

4.靶形数独

(sudoku.pas/c/cpp)

【问题描述】

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

Z 博士拿出了他 近发明的“靶形数独”,作为这两个孩子比试的题目。

靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有 9 个 3 格宽×3 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 1 到 9 的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)

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

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

【输入】

输入文件名为 sudoku.in。

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

【输出】

输出文件 sudoku.out 共 1 行。

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

【输入输出样例 1】

sudoku.in

sudoku.out

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

2829

【输入输出样例 2】

sudoku.in

sudoku.out

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

2852

【数据范围】

40%的数据,数独中非 0 数的个数不少于 30。 80%的数据,数独中非 0 数的个数不少于 26。

100%的数据,数独中非 0 数的个数不少于 24。

【思路】

Dancing Links X

这道题用搜索做只过了80分。

很多人提到可以用DLX算法求解数独,在下还没有理解这里只贴一个别人的代码:

【搜索_80】

 #include<iostream>
#include<algorithm>
#include<vector>
#define FOR(a,b,c) for(int a=(b);a<(c);a++)
using namespace std;
const int maxn = +;
const int N=;;
struct Node{
int x,y;
Node(const int x,const int y) {
this->x=x; this->y=y;
}
vector<int> can;
bool operator <(const Node& rhs) const{
return can.size()<rhs.can.size(); //?ù?Y?é??μ?êy×??àéù′óD?μ?′ó??Dò
}
};
vector<Node> nodes;
int R[maxn][maxn],C[maxn][maxn],S[maxn][maxn];
int vis[maxn][maxn];
int ans=-; inline int ID(int i,int j) { return (i/)*+(j/); }
inline int score(int i,int j,int k) {
if(i== && j==) return *k;
if(i>= && i<= && j>= && j<=) return *k;
if(i>= && i<= && j>= && j<=) return *k;
if(i>= && i<= && j>= && j<=) return *k;
return *k;
}
inline int jisuan(int x,int y) {
if(vis[x][y]) return (<<);
int res=;
for(int i=;i<N;i++) {
//if(vis[x][i]) res++;
//if(vis[i][y]) res++;
}
for(int i=;i<N;i++) if(S[ID(x,y)][i]) res++;
return res;
}
int nc;
void dfs(int d,int sum) {
if(d==nc){
ans=max(ans,sum);
return ; //
}
int _max=(<<),_maxi;
for(int i=;i<nc;i++) {
int tmp=jisuan(nodes[i].x,nodes[i].y);
if(tmp<_max) {
_max=tmp;
_maxi=i;
}
}
int x=nodes[_maxi].x , y=nodes[_maxi].y;
FOR(j,,nodes[_maxi].can.size()) {
int i=nodes[_maxi].can[j];
if((R[x][i])||(C[y][i])||(S[ID(x,y)][i])) continue;
R[x][i]=C[y][i]=S[ID(x,y)][i]=; vis[x][y]=;
dfs(d+,sum+score(x,y,i));
R[x][i]=C[y][i]=S[ID(x,y)][i]=; vis[x][y]=;
}
}
int main() {
ios::sync_with_stdio(false);
int x;
int sum=;
FOR(i,,N) FOR(j,,N) {
cin>>x;
if(x==) nodes.push_back(Node(i,j)); //???????′ì?μ?μ?
else {
vis[i][j]=;
R[i][x]=C[j][x]=S[ID(i,j)][x]=;
sum+= score(i,j,x); //±eíüá?í3???-à′μ?·?êy
}
}
for(int i=;i<nodes.size();i++){
int x=nodes[i].x,y=nodes[i].y;
for(int j=;j<=;j++) if((!R[x][j])&&(!C[y][j])&&(!S[ID(x,y)][j])) nodes[i].can.push_back(j);
}
nc=nodes.size();
dfs(,sum);
cout<<ans;
return ;
}

【DLX_AC】

 #include <stdio.h>
#include <stdlib.h>
#define con(i) (1 << (i))
#define getid(i, j) ((i) / 3 * 3 + (j) / 3)
#define getidorder(i, j) (3 * ((i) - (i) / 3 * 3) + (j) - (j) / 3 * 3)
int map[][];
int row[];
int n_row[], n_line[];
int small[];
int f[]; int ans;
int sore[][] = { {, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , } }; void getgrade(void)
{
int i, j;
int t = ;
for(i = ; i < ; i++){
for(j = ; j < ; j++){
t += map[i][j] * sore[i][j];
}
}
if(ans < t){
ans = t;
}
} int so[], count[];
int end; void srch(int now)
{
int i, j, p;
int pos, k;
if(now == ){
getgrade();
return;
}
i = so[now];
if(count[i] == ){
srch(now + );
return;
}
count[i]--;
p = ^ row[i];
p = p & -p;
row[i] |= p;
j = f[p]; pos = ^ (n_row[i] | n_line[j] | small[getid(i, j)]);
while(pos > ){
k = pos & -pos;
pos ^= k;
n_row[i] |= k;
n_line[j] |= k;
small[getid(i, j)] |= k;
map[i][j] = f[k] + ;
srch(now);
n_row[i] ^= k;
n_line[j] ^= k;
small[getid(i, j)] ^= k;
} count[i]++;
row[i] ^= p;
} int main(int argc, char **argv)
{
int i, j, t;
for(i = , j = ; i <= ; i <<= , j++){
f[i] = j;
}
for(i = ; i < ; i++){
for(j = ; j < ; j++){
scanf("%d", &map[i][j]);
if(map[i][j] != ){
row[i] |= con(j);
t = con(map[i][j] - );
if(((n_row[i] & t) != ) || ((n_line[j] & t) != ) || ((small[getid(i, j)] & t) != )){
printf("-1\n");
return ;
}
n_row[i] |= t;
n_line[j] |= t;
small[getid(i, j)] |= t;
}else{
count[i]++;
}
}
}
for(i = ; i < ; i++){
so[i] = i;
}
for(i = ; i < ; i++){
for(j = i + ; j < ; j++){
if(count[so[i]] > count[so[j]]){
so[i] ^= so[j];
so[j] ^= so[i];
so[i] ^= so[j];
}
}
}
for(i = ; count[so[i]] == ; i++){ ; }
srch(i);
if(ans == ){
printf("-1\n");
return ;
}
printf("%d\n", ans);
return ;
}