P1074 靶形数独 dfs+预处理

https://www.luogu.org/problemnew/show/P1074

显然是dfs 而且没有什么剪枝记忆化之类的 但是预处理比较麻烦

我用三个二维数组存状态:visx[x][i]代表x行有没有选 i 这个数   visy[y][i]代表y列有没有选 i    visg[g][i]代表第g个九宫格有没有选i

当遍历的时候 发现visx[x][i]==0&&visy[y][i]==0&&visg[g][i]==0 说明 i 还没有被选 就可以向下dfs

至于遍历的顺序 我们玩数独的时候都知道 肯定先从已知点比较多的地方开始考虑

比如 第一行:7 0 0 9 0 0 0 0 1 有6个位置未知

第九行:0 8 0 5 0 4 0 1 2 只有4个位置未知 那么我们肯定先考虑第九行

所以只需要把行sort排序一下 调整一下遍历的顺序即可

不过排序又引申出来一个问题 就是我们不知道排序之后的第一行 原来是第几行 为了解决这个问题 我们可以用一个结构体存储每一行有几个未知位置:

typedef struct
{
    int nums=0;  //未知位置数量
    int x;    //行数
}node;

最后 我们需要维护一个一维数组,用来保存修正后的遍历的序列(当然二维也可以)

最后的的最后 每找到一组数独的解 都要更新最大值

具体看代码吧:

#include<bits/stdc++.h>

using namespace std;

typedef struct
{
    int nums=0;
    int x;
}node;
node e[15];

int m[15][15],anss,u,visx[15][15],visy[15][15],visg[15][15],q[100],pre,flag;
int s[11][11]={0,0,0,0,0,0,0,0,0,0,0,           //保存每个位置上的分数 比较懒 直接打表了
               0,6,6,6,6,6,6,6,6,6,0,
               0,6,7,7,7,7,7,7,7,6,0,
               0,6,7,8,8,8,8,8,7,6,0,
               0,6,7,8,9,9,9,8,7,6,0,
               0,6,7,8,9,10,9,8,7,6,0,
               0,6,7,8,9,9,9,8,7,6,0,
               0,6,7,8,8,8,8,8,7,6,0,
               0,6,7,7,7,7,7,7,7,6,0,
               0,6,6,6,6,6,6,6,6,6,0,
               0,0,0,0,0,0,0,0,0,0,0};


bool cmp(node a,node b)
{
    return a.nums<b.nums;
}

void dfs(int t)
{
    int i,j;
    if(t==u+1)
    {
        flag=1;
        int p=0;
        for(i=1;i<=9;i++)
        for(j=1;j<=9;j++)
        p+=m[i][j]*s[i][j];
        anss=max(anss,p);   //更新最大值
        return;
    }
    int x=(q[t]-1)/9+1;
    int y=q[t]-(x-1)*9;
    int g=((x-1)/3)*3+(y-1)/3+1;
    for(i=1;i<=9;i++)
    {
        if(visx[x][i]==0&&visy[y][i]==0&&visg[g][i]==0)
        {
            visx[x][i]=1;
            visy[y][i]=1;
            visg[g][i]=1;
            m[x][y]=i;
            dfs(t+1);
            visx[x][i]=0;
            visy[y][i]=0;
            visg[g][i]=0;
        } 
    }
}


int main()
{
    int i,j;
    for(i=1;i<=9;i++)
    e[i].x=i;
    for(i=1;i<=9;i++)
    for(j=1;j<=9;j++)
    {
        scanf("%d",&m[i][j]);
        if(m[i][j]==0)
        e[i].nums++;
        else
        {
            pre+=m[i][j]*s[i][j];
            visx[i][m[i][j]]=1;
            visy[j][m[i][j]]=1;
            int g=((i-1)/3)*3+(j-1)/3+1;   //计算当前点属于哪一个九宫格
            visg[g][m[i][j]]=1;  
        }
    }
    sort(e+1,e+10,cmp);
    for(i=1;i<=9;i++)
    for(j=1;j<=9;j++)
    {
        if(m[e[i].x][j]==0)
        {
            int nums=(e[i].x-1)*9+j;  //如果当前位置未知,把他放入准备遍历的序列里
            q[++u]=nums;  //u记录了有多少点未知
        }
    }
    dfs(1);
    if(flag==0)  //如果无解
    {
        cout<<-1<<endl;
        return 0;
    }
    cout<<anss<<endl;

} 
原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/10941485.html