【Luogu P1074】靶形数独

ac4bd11373f08202419f75c548fbfbedab641b3e.jpg

金宣宗镇楼

简要说一下题面:

有一个鬼畜的数独,有九个宫(不是皇宫!!) ,九行和九列。保证每一个宫里没有重复的嫔妃 数字,每一行每一列也是如此。与正常数独不同的是,这个鬼畜的数独是有分数的,由于分数的分布像一个靶子,所以我们管它叫“靶形数独”。

28.png

每个格子的分数是格子中的数字与该格子的分数的乘积。现给出一个尚未填完的数独,求可能的最大分数。

以下是我的思考过程


乍一看,感觉这题非常恶心,就先从暴力的角度来思考:

因为数独已经给出来了,而且它是9x9不变的,所以可以考虑暴力搜索没有填数的格子,将其填上可能填写的数字。判断重复的话,for枚举一下就行了(逃

搜索思路嘛……就从(1,1)开始上下左右的搜,如果是空格就填,不是空格就继续上下左右......预计广搜实现。

所以理论时间复杂度是@¥¥&&¥@……&#%¥ 不太会算

反正掰指头算算,肯定是会T掉的,现在考虑优化。

从原来的暴力思路里面挑挑毛病,可以显而易见地发现判重的bug实在是大:总时间复杂度上面还要再乘一个9,谁都不会愿意的@_@

优化方案:搞三个布尔数组

\(row[i][j]\):第i行有没有j

\(line[i][j]\):第i列有没有j

\(gong[i][j]\):第i个宫有没有j

输入数独的时候更新一下即可。

同时维护一个队列存储空格子的坐标及所在宫,方便很多,也可以用dfs解决问题。


这时候发现好像似乎大概应该能卡过,但是直觉告诉我有可能卡不过(后来测了一次试试,75),于是找到一个优化:

以样例1为例:

7 0 0 9 0 0 0 0 1 //6个0
1 0 0 0 0 5 9 0 0 //6个0
0 0 0 2 0 0 0 8 0 //7个0
0 0 5 0 2 0 0 0 3 //6个0
0 0 0 0 0 0 6 4 8 //6个0
4 1 3 0 0 0 0 0 0 //6个0
0 0 7 0 0 2 0 9 0 //6个0
2 0 1 0 6 0 8 0 4 //4个0
0 8 0 5 0 4 0 1 2 //4个0

众所周知,搜索越到后面,剩余的格子可以填的数字方案就越少,所以刚开始尽量选择空格子少的行开始搜。考虑建立一个结构体数组,存入每一行的编号及空格子数量并进行排序,优先操作空格子少的行,于是在运行的时候又可以省下来一笔时间。


其实印象中这题应该要剪一堆的枝,用到一堆高深的搜索方法,结果把这个思路实现了之后交上去,竟然就过了……

就是这样。

code:

#include<bits/stdc++.h>
using namespace std;
int point[10][10]=
{0,0,0,0,0,0,0,0,0,0,
0,6,6,6,6,6,6,6,6,6,
0,6,7,7,7,7,7,7,7,6,
0,6,7,8,8,8,8,8,7,6,
0,6,7,8,9,9,9,8,7,6,
0,6,7,8,9,10,9,8,7,6,
0,6,7,8,9,9,9,8,7,6,
0,6,7,8,8,8,8,8,7,6,
0,6,7,7,7,7,7,7,7,6,
0,6,6,6,6,6,6,6,6,6};
int num[10][10],dark=0,maxans=-1,q[1010][4],dt=0;
bool gong[10][10],row[10][10],line[10][10];
struct data{
    int zero,no;
}cnt[10];
bool cmp(data a,data b){
    return a.zero<b.zero;
}
int g(int x,int y){
    if(x<=3){
        if(y<=3)return 1;
        if(y>=7)return 3;
        else return 2;
    }
    if(x>=7){
        if(y<=3)return 7;
        if(y>=7)return 9;
        else return 8;
    }
    else{
        if(y<=3)return 4;
        if(y>=7)return 6;
        else return 5;
    }//返回宫编号
}
void read(){
    for(register int i=1;i<=9;i++){
        for(register int j=1;j<=9;j++){
            cin>>num[i][j];
        }
    }//读入
}
void dfs(int ddt,int score){
    if(ddt==dt+1){
        maxans=max(score,maxans);
        return ;
    }
    for(register int i=1;i<=9;i++){
        if(!gong[q[ddt][3]][i]&&!line[q[ddt][2]][i]&&!row[q[ddt][1]][i]){
            gong[q[ddt][3]][i]=line[q[ddt][2]][i]=row[q[ddt][1]][i]=1;
            num[q[ddt][1]][q[ddt][2]]=i;
            dfs(ddt+1,score+i*point[q[ddt][1]][q[ddt][2]]);
            gong[q[ddt][3]][i]=line[q[ddt][2]][i]=row[q[ddt][1]][i]=0;
            num[q[ddt][1]][q[ddt][2]]=0;
        }
    }
}
void doing(){
    read();
    for(register int i=1;i<=9;i++){
        cnt[i].no=i;
        for(register int j=1;j<=9;j++){
            if(!num[i][j]){
                cnt[i].zero++;
            }
            else{
                row[i][num[i][j]]=1;
                line[j][num[i][j]]=1;
                gong[g(i,j)][num[i][j]]=1;
                dark+=num[i][j]*point[i][j];
            }
        }
    }
    sort(cnt+1,cnt+10,cmp);
    for(register int i=1;i<=9;i++){
        for(register int j=1;j<=9;j++){
            if(!num[cnt[i].no][j]){
                q[++dt][1]=cnt[i].no;
                q[dt][2]=j;
                q[dt][3]=g(cnt[i].no,j);
            }
        }
    }
    dfs(1,dark);
}
int main(){
    doing();
    cout<<maxans;
}

~~92行,水得一批&_&~~

转载于:https://www.cnblogs.com/ironwheel/p/9524617.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值