codevs 1174 靶形数独

去题面的传送门
QAQ这道题已经调了很久了,一直是TLE
第一次交,codevs 35分,洛谷 70分(好吧看到这样子我还以为洛谷的数据水来着)
后来加了一个优化,codevs 95分,洛谷 80分(这才知道其实是codevs数据水)
优化是这样的:
一开始读入之后,把所有没有填的空位,它的所在行、列、九宫格里的已经填了的数字统计一下,放到结构体里,然后按照数字数量由大到小排序(所在区域已经填了的数字越多,越优先搜索),dfs时按照结构体顺序搜索,改掉原来一行一行地搜索
但是这样肯定不行,因为我还是T了(╮(╯▽╰)╭)
想想这样做有什么缺点:
①只统计了数字个数,而不是已经填了的数字的种类
②随着搜索的进行,下一个搜索的最优目标可能会改变,这时,初始时设定的已经排列好的顺序就不是最优的了,还是会有很多冗杂的操作

所以,最后的优化就是,在搜索过程中,用函数计算下一个要搜索的点(其实是启发式搜索的启发函数)。计算的标准就是,改点所在的行、列、九宫格内,还没有没使用的数字的数量,越少越好

(虽然一开始觉得这样很暴力,但是可以让搜索树变得超级小)

一开始T掉的点跑了大约6秒(手动测),优化之后变成717ms,时间限制是4s

ps:启发式搜索还是要选好启发函数

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int ans,cnt,sum;
int ju[10][10],X[10][10],Y[10][10],S[10][10];
bool vis[10][10],used[10][10];
bool flg=0;
struct hh
{
    int x,y;
};
int muti[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}};

hh getit()
{
    hh ret;
    int cm=15,sum=0;
    for(int i=1;i<=9;++i)
      for(int j=1;j<=9;++j)
        if(!ju[i][j])
        {
            sum=0;
            int wh=(i-1)/3*3+(j-1)/3+1;
            for(int k=1;k<=9;++k)
              if(!X[i][k]&&!Y[j][k]&&!S[wh][k])
                sum++;
            if(sum<cm)
            {
                cm=sum;
                ret.x=i;
                ret.y=j;
            }
        }
    return ret;
}
void dfs(int x,int y,int k,int tot)
{
    if(x>=1&&y<=9&&x<=9&&y>=1&&!vis[x][y]&&!used[x][y])
    {
        int wh=(x-1)/3*3+(y-1)/3+1;
        for(int i=1;i<=9;++i)
          if(!X[x][i]&&!Y[y][i]&&!S[wh][i])
          {
            X[x][i]=Y[y][i]=S[wh][i]=vis[x][y]=1;
            ju[x][y]=i; 
            if(k==cnt) ans=max(ans,tot+i*muti[x][y]);
            else
            {
                hh xx=getit();
                dfs(xx.x,xx.y,k+1,tot+ju[x][y]*muti[x][y]);
            }
            ju[x][y]=0;
            X[x][i]=Y[y][i]=S[wh][i]=vis[x][y]=0;
          }
    }
}
int main()
{
    for(int i=1;i<=9;++i)
      for(int j=1;j<=9;++j)
      {
         scanf("%d",&ju[i][j]);
         if(!ju[i][j]) 
         {
            cnt++;
            continue;
         }
         sum+=ju[i][j]*muti[i][j];
         used[i][j]=vis[i][j]=true;
         int wh=(i-1)/3*3+(j-1)/3+1;
         X[i][ju[i][j]]=S[wh][ju[i][j]]=Y[j][ju[i][j]]=1;
      }
    hh xx=getit();
    dfs(xx.x,xx.y,1,sum); 
    if(ans==0) ans--; 
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值