NOIP2009 靶形数独(搜索)

本文探讨了在解决数独问题时如何通过位运算和排序优化来提高算法效率,包括利用位运算保存每一行、每列及每个九宫格内的数字使用情况,以及在输入阶段统计已知数字数量进行排序,从而减小搜索树规模。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以前做过一道hdu类似的数独题,那道题我没有加什么优化就过了,然后这道题不加优化可以得50~80分,要上80分就必须要优化

可以用位运算保存每一行每一列每个九宫格内已经使用的数,例如userow[i] = 011101111表示第i行还有1,5两个数字没有使用,那么我们枚举每个未知的格子(i,j)时,他不能填的数就是userow[i]|usecol[j]|usebox[i/3*3+j/3] ,其实这样做也不能优化什么时间,下面才是最NB的优化

在输入的时候我们可以统计每一行已知的数,于是我们能够把没有填数的格子按每一行从小到大来排序,这样,枚举这个点能够扩展出来的搜索树就会比原来小。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define RE ((1<<9)-1)
using namespace std;
int sum_a[10];
struct T
{
    int x,y;
    bool operator < (T t) const//已知数多的放在前面
    {
        if(sum_a[x] == sum_a[t.x]) 
            return x < t.x;
        return sum_a[x] > sum_a[t.x];
    }
}unknown[100];//保存没有填的格子的位置
int st[10][10];
int p[10][10];//权值数组
void init()
{
    for(int i = 0; i < 9; i++)
    {   
        p[0][i] = p[8][i] = p[i][0] = p[i][8] = 6;
    }
    for(int i = 1; i < 8; i++)
    {
        p[1][i] = p[7][i] = p[i][1] = p[i][7] = 7;
    }
    for(int i = 2; i < 7; i++)
    {
        p[2][i] = p[6][i] = p[i][2] = p[i][6] = 8;
    }
    for(int i = 3; i < 6; i++)
    {
        p[3][i] = p[5][i] = p[i][3] = p[i][5] = 9;
    }
    p[4][4] = 10;
}
int ans = -1;
int cnt,sorce;
int userow[10];//每一行使用的数
int usecol[10];//每一列使用的数
int usebox[10];//每一个九宫格内使用的数
void dfs(int cur)
{
    if(cur > cnt)
    {
        ans = max(sorce,ans);
        return;
    }
    int x = unknown[cur].x;
    int y = unknown[cur].y;
    int temp = (userow[x]|usecol[y]|usebox[x/3*3+y/3]);
    if(temp == RE) return;//没有可以使用的数
    for(int i = 1; i <= 9; i++)
    if(!((temp>>(i-1))&1))
    {
        st[x][y] = i;
        usebox[x/3*3+y/3] |= (1<<(i-1));
        userow[x] |= (1<<(i-1));
        usecol[y] |= (1<<(i-1));
        sorce += st[x][y]*p[x][y];
        dfs(cur+1);
        sorce -= st[x][y]*p[x][y];
        st[x][y] = 0;
        usebox[x/3*3+y/3] ^= (1<<(i-1));
        userow[x] ^= (1<<(i-1));
        usecol[y] ^= (1<<(i-1));
    }
}
int main()
{
    //freopen("sudoku.in","r",stdin);
    //freopen("sudoku.out","w",stdout);
    init();
    for(int i = 0; i < 9; i++)
    {   
        for(int j = 0; j < 9; j++)
        {
            scanf("%d",&st[i][j]);
            if(st[i][j] != 0)
            {
                sorce += st[i][j]*p[i][j];
                userow[i] |= (1<<(st[i][j]-1));
                usecol[j] |= (1<<(st[i][j]-1));
                usebox[i/3*3+j/3] |= (1<<(st[i][j]-1));
                ++sum_a[i];//统计i行已知的数
            }
        }
    }
    for(int i = 0; i < 9; i++)
        for(int j = 0; j < 9; j++)
        {
            if(!st[i][j])
            {
                ++cnt;
                unknown[cnt].x = i;
                unknown[cnt].y = j;
            }
        }
    sort(unknown+1,unknown+cnt+1);
    dfs(1);
    printf("%d\n",ans);
}


c++:# P1074 [NOIP 2009 提高组] 数独 ## 题目背景 **此为远古题,不保证存在可以通过任意符合要求的输入数据的程序**。 ## 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“数独”,作为这两个孩子比试的题目。 数独的方格同普通数独一样,在 $9$ 格宽且 $9$ 格高的大九宫格中有 $9$ 个 $3$ 格宽且 $3$ 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 $1$ 到 $9$ 的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个子一样,离中心越近则分值越高。(如图) ![](https://cdn.luogu.com.cn/upload/image_hosting/dhvuc32i.png) 上图具体的分值分布是:最里面一格(黄色区域)为 $10$ 分,黄色区域外面的一圈(红色区域)每个格子为 $9$ 分,再外面一圈(蓝色区域)每个格子为 $8$ 分,蓝色区域外面一圈(棕色区域)每个格子为 $7$ 分,最外面一圈(白色区域)每个格子为 $6$ 分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和 总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的数独游戏中,总分数为 $2829$。游戏规定,将以总分数的高低决出胜负。 ![](https://cdn.luogu.com.cn/upload/image_hosting/yx82mmnc.png) 由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的数独,能够得到的最高分数。 ## 输入格式 一共 $9$ 行。每行 $9$ 个整数(每个数都在 $0 \sim 9$ 的范围内),表示一个尚未填满的数独方格,未填的空格用“$0$”表示。每两个数字之间用一个空格隔开。 ## 输出格式 输出共 $1$ 行。输出可以得到的数独的最高分数。如果这个数独无解,则输出整数 $-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 ### 输入 #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 提高组 第三题
最新发布
07-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值