ACcoders Problem 2058 题解

题意


我们要填一个数独,每行每列每个九宫格都将 1 1 1 9 9 9 填入且不重复。如上图,越靠近中间得分越高,每个格子的得分为所填的数值与每个格子的基本得分的乘积,问得分最大为多少。

思路

我们先考虑爆搜,其中引入两个参数,分别为当前没填的格子的数量 c n t cnt cnt 和最终的得分 s c o r e score score。如果 c n t cnt cnt 0 0 0,那么就更新答案,与 s c o r e score score 取最大值。每次枚举没填过的数值,然后填入数独,进行下一轮搜索,之后回溯。这样一定会超时,我们考虑剪枝。

剪枝1:顺序剪枝

每次我们都找出没填的选择分支最少(行列九宫格没填的数最少)的空格来填,使其搜索范围缩小,优化时间复杂度。

剪枝2:二进制优化

我们先构造出来三个数组 r o w row row c o l col col c e l l cell cell,表示当前行列九宫格内所表示的状态,填过数表示 0 0 0,没填过表示 1 1 1(下标为填的数的数值)。因为一开始都是没有被填过,所以为 111111111 111111111 111111111,即为 ( 1 < < 9 ) − 1 (1<<9)-1 (1<<9)1。每次填入一个数,就代表当前位置为 0 0 0,即减去 1 < < ( v a l − 1 ) 1<<(val-1) 1<<(val1),三个数组都是如此。

据上一个剪枝,我们可以得出没填的选择分支最少的空格来填就为这个方格所在三个数组中的下标的交集,也就是按位运算符的与,最后得出最小值。

算出最小值后,就依次处理其分支,将其中二进制表达下为 1 1 1 的数位取出(这里可以用 lowbit \text{lowbit} lowbit),再将其传化为十进制,这里可以用一个 log ⁡ \log log 数组来预处理出 log ⁡ \log log,直接得出十进制,将其填入,进行搜索,回溯。

代码

#include<bits/stdc++.h>
using namespace std;
int num[1<<9],logg[1<<9];
int row[9],col[9],cell[3][3];
int f[9][9];
int ans=-1;
int lowbit(int x){return x&-x;}
int get(int x,int y,int val){//得出当前方格的分数
    return (min(min(x,8-x),min(y,8-y))+6)*val;
}
int find(int x,int y){
    return row[x]&col[y]&cell[x/3][y/3];
}
void insert(int x,int y,int val)
{
    int s=1;
    if(val>0) f[x][y]=val;
    else
    {
        s=-1;
        val=-val;
        f[x][y]=0;
    }
    val--;
    row[x]-=(1<<val)*s;
    col[y]-=(1<<val)*s;
    cell[x/3][y/3]-=(1<<val)*s;
}
void dfs(int cnt,int score)
{
    if(!cnt)
    {
        ans=max(ans,score);
        return ;
    }
    int minx=0x3f3f3f3f,x,y;
    for(int i=0;i<9;i++) 
        for(int j=0;j<9;j++)
            if(!f[i][j])
            {
                int state=find(i,j);
                if(num[state]<minx)
                {
                    minx=num[state];
                    x=i;y=j;
                }
            }
    for(int i=find(x,y);i;i-=lowbit(i))
    {
        int val=logg[lowbit(i)]+1;
        insert(x,y,val);
        dfs(cnt-1,score+get(x,y,val));
        insert(x,y,-val);
    }
}
int main()
{
    int cnt=0,score=0;
    for(int i=0;i<9;i++)
        logg[1<<i]=i;
    for(int i=0;i<(1<<9);i++)
        for(int j=i;j;j-=lowbit(j))
            num[i]++;
    for(int i=0;i<9;i++)
        row[i]=col[i]=cell[i/3][i%3]=(1<<9)-1;
    for(int i=0;i<9;i++) 
        for(int j=0;j<9;j++)
        {
            int x;
            scanf("%d",&x);
            if(x)
            {
                insert(i,j,x);
                score+=get(i,j,x);
            }
            else cnt++;
        }
    dfs(cnt,score);
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值