[Noip2009] 靶形数独

Posted brimon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Noip2009] 靶形数独相关的知识,希望对你有一定的参考价值。

题目描述

小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。

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

技术分享图片

上图具体的分值分布是:最里面一格(黄色区域)为 101010 分,黄色区域外面的一圈(红色区域)每个格子为9 9 9分,再外面一圈(蓝色区域)每个格子为8 88 分,蓝色区域外面一圈(棕色区域)每个格子为7 7 7分,最外面一圈(白色区域)每个格子为6 6 6分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和

总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 2829。游戏规定,将以总分数的高低决出胜负。

技术分享图片

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#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%的数据,数独中非 000 数的个数不少于30 3030。

80%的数据,数独中非 000 数的个数不少于262626。

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

NOIP 2009 提高组 第四题

 


 

 

十分显然的爆搜, 但是与普通的数独不一样,我们这次要搜索完整颗搜索树。

所以不加任何剪枝是70分。

我们用位运算来代替枚举。

把用数组存储数字i是否出现,变成用一个状态记录。

这样我们把位置(x, y)所在的行,列,块的状态$&$起来,就是所有可以填的数字的集合。

然后用lowbit取出每一个1,这样大大优化了常数。

这样是80分。

考虑我们平时玩的数独,一定是首先填出来好填的,也就是可以填的数最少的。

于是我们可以把整个棋盘分为上下两部分,比较上下两部分出现0的数量,然后从少的那一边开始搜索, 搜索树的规模大大减小。

这样剪枝+优化常数之后轻松AC。

 


 

 

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <bitset>
using namespace std;
#define reg register
inline int read() {
    int res=0;char ch=getchar();bool fu=0;
    while(!isdigit(ch)) {if(ch==-)fu=1;ch=getchar();}
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return fu?-res:res;
}

int bin[15], num[1<<10];
int mp[15][15];
bool vis1[15][15], vis2[15][15], vis3[15][15];
inline int id(int x, int y)
{
    int tx = (x - 1) / 3 + 1, ty = (y - 1) / 3 + 1;
    return (tx  - 1) * 3 + ty;
}
inline int val(int x, int y)
{
    if (x == 5 and y == 5) return 10;
    if (abs(x - 5) == 4) return 6;
    if (abs(x - 5) == 3) {
        if (y == 1 or y == 9) return 6;
        return 7;
    }
    if (abs(x - 5) == 2) {
        if (y == 1 or y == 9) return 6;
        if (y == 2 or y == 8) return 7;
        return 8;
    }
    if (abs(x - 5) == 1) {
        if (y == 1 or y == 9) return 6;
        if (y == 2 or y == 8) return 7;
        if (y == 3 or y == 7) return 8;
        return 9;        
    }
    if (abs(x - 5) == 0) {
        if (y == 1 or y == 9) return 6;
        if (y == 2 or y == 8) return 7;
        if (y == 3 or y == 7) return 8;
        if (y == 4 or y == 6) return 9;        
        return 10;
    }
}
int ans;

int sit1[15], sit2[15], sit3[15];

inline int calc()
{
    int res = 0;
    for (reg int i = 1 ; i <= 9 ; i ++)
        for (reg int j = 1 ; j <= 9 ; j ++)
            res += mp[i][j] * val(i, j);
    return res;
}

void dfs(int x, int y)
{
    if (x > 9) {ans = max(ans, calc());return ;}
    if (mp[x][y]) {
        if (y == 9) dfs(x + 1, 1);
        else dfs(x, y + 1);
        return ;
    }
    int can = ((sit1[x] & sit2[y]) & sit3[id(x, y)]);
    while(can) {
        int low = can & -can;
        int w = num[low];
        sit1[x] -= low, sit2[y] -= low, sit3[id(x, y)] -= low;
        mp[x][y] = w;
        if (y == 9) dfs(x + 1, 1);
        else dfs(x, y + 1);        
        mp[x][y] = 0;
        sit1[x] += low, sit2[y] += low, sit3[id(x, y)] += low;
        can -= low;
    }
}

void efs(int x, int y)
{
    if (x < 1) {ans = max(ans, calc());return ;}
    if (mp[x][y]) {
        if (y == 9) efs(x - 1, 1);
        else efs(x, y + 1);
        return ;
    }
    int can = ((sit1[x] & sit2[y]) & sit3[id(x, y)]);
    while(can) {
        int low = can & -can;
        int w = num[low];
        sit1[x] -= low, sit2[y] -= low, sit3[id(x, y)] -= low;
        mp[x][y] = w;
        if (y == 9) efs(x - 1, 1);
        else efs(x, y + 1);        
        mp[x][y] = 0;
        sit1[x] += low, sit2[y] += low, sit3[id(x, y)] += low;
        can -= low;
    }
}

int cnt1, cnt2;

signed main()
{
    bin[0] = 1;for(int i = 1 ; i <= 10 ; i ++) bin[i] = bin[i-1] << 1; 
    num[1] = 1;for(int i = 2 ; i <= (1<<10) ; i ++) num[i] = num[i>>1] + 1;
    for (reg int i = 1 ; i <= 9 ; i++) sit1[i] = bin[9] - 1, sit2[i] = bin[9] - 1, sit3[i] = bin[9] - 1;
    for (reg int i = 1 ; i <= 9 ; i ++)    
        for (reg int j = 1 ; j <= 9 ; j ++) {
            mp[i][j] = read();
            if (mp[i][j]) sit1[i] -= bin[mp[i][j]-1], sit2[j] -= bin[mp[i][j]-1], sit3[id(i, j)] -= bin[mp[i][j]-1];
            if (!mp[i][j]) {
                if (j <= 5) cnt1++;
                else cnt2++;
            }
        }
    if (cnt1 <= cnt2) dfs(1, 1);
    else efs(9, 1);
    if (!ans) puts("-1");
    else cout << ans << endl;
    return 0;
}

 

 

 

以上是关于[Noip2009] 靶形数独的主要内容,如果未能解决你的问题,请参考以下文章

[NOIP2009]靶形数独 题解

[Noip2009] 靶形数独

[NOIp 2009]靶形数独

[NOIP2009]靶形数独 题解

Noip2009靶形数独

NOIP2009 靶形数独