数独模板靶形数独

卡了 2h ,再也不想写数独了。

普通数独

思路

显然是对每个格子进行枚举,类似八皇后的方法去做,朴素方法是由 \((1,1)\) 到 \((9,9)\) 遍历过去。

优化

我们在做数独时,会优先选择已填格数多的行、列、区域,这样可以保证尝试次数少。

同样,这一点在本题中也可以应用,但是有两种思路。

  1. 按照行里没填的格子的个数进行从小到大排序。
  2. 根据单个格子可能会出现的数字的数量进行从小到大排序。

目的只有一个:减少搜索树的大小。

这里采用第二种方法,则计算这个格子可能的数字,就是竖向的数字、横向的数字、区域的数字以外的数字。

因此,我们将这三种数字开个 bitset,然后分别或一下,设结果为 \(ans\),如果 \(ans=0\),则证明可以填这个数。

bitset 可以用状压的方式优化,大幅减少常数。但我写的位运算版还没 bitset 版跑得快。

然后还要注意,如果搜到结果,直接 exit(0) 就好。

注意把 sort 改成 stable_sort ,因为尽可能要连续填。

于是就把数独过了。

数独代码:

第一种,bitset 版,跑洛谷的点很快,比位运算版要快:610ms。

#include <bits/stdc++.h>
using namespace std;
int a[15][15];
bitset<15>xvis[15],yvis[15],svis[15];
int s(int x,int y)
{
return ((x-1)/3*3+(y+2)/3);
}
int cg(int x,int y)
{
bitset<15>ans;
int res=0;
ans=xvis[x]|yvis[y]|svis[s(x,y)];
for(int i=1;i<=9;i++)if(ans[i]==0)res++;
return res;
}
struct tile{
int x,y,g;
};
vector<tile>vct;
bool cmp(tile a,tile b)
{
return a.g<b.g;
}
void outp()
{
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
cout<<a[i][j]<<' ';
}
cout<<endl;
}
}
void dfs(int now)
{
if(now>=vct.size())
{
outp();
exit(0);
}
int x=vct[now].x,y=vct[now].y;
bitset<15>ans;
ans=xvis[x]|yvis[y]|svis[s(x,y)];
for(int i=1;i<=9;i++)
{
if(ans[i]==0)
{
svis[s(x,y)][i]=1;
xvis[x][i]=1;
yvis[y][i]=1;
a[x][y]=i;
dfs(now+1);
svis[s(x,y)][i]=0;
xvis[x][i]=0;
yvis[y][i]=0;
a[x][y]=0;
}
}
}
int main()
{
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
cin>>a[i][j];
if(a[i][j]!=0)
{
svis[s(i,j)][a[i][j]]=1;
xvis[i][a[i][j]]=1;
yvis[j][a[i][j]]=1;
}
}
}
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
if(a[i][j]!=0)continue;
tile tmp;
tmp.x=i,tmp.y=j;
tmp.g=cg(i,j);
if(tmp.g!=0)vct.push_back(tmp);
}
}
stable_sort(vct.begin(),vct.end(),cmp);
dfs(0);
return 0;
}

第二种代码,位运算版,实际比 bitset 慢:3.28s。

#include <bits/stdc++.h>
using namespace std;
int a[15][15];
int xvis[15],yvis[15],svis[15];
int s(int x,int y)
{
return ((x-1)/3*3+(y+2)/3);
}
int cg(int x,int y)
{
int ans;
int res=0;
ans=xvis[x]|yvis[y]|svis[s(x,y)];
for(int i=1;i<=9;i++)if(ans>>i==0)res++;
return res;
}
struct tile{
int x,y,g;
};
vector<tile>vct;
bool cmp(tile a,tile b)
{
return a.g<b.g;
}
void outp()
{
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
cout<<a[i][j]<<' ';
}
cout<<endl;
}
}
void dfs(int now)
{
if(now>=vct.size())
{
outp();
exit(0);
}
int x=vct[now].x,y=vct[now].y;
int tmps=s(x,y);
int ans=xvis[x]|yvis[y]|svis[tmps];
for(int i=1;i<=9;i++)
{
if(((ans>>i)&1)==0)
{
xvis[x]|=(1<<i);
yvis[y]|=(1<<i);
svis[tmps]|=(1<<i);
a[x][y]=i;
dfs(now+1);
xvis[x]=(xvis[x]^(1<<i));
yvis[y]=(yvis[y]^(1<<i));
svis[tmps]=(svis[tmps]^(1<<i));
a[x][y]=0;
}
}
}
int main()
{
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
cin>>a[i][j];
if(a[i][j]!=0)
{
svis[s(i,j)]|=1<<a[i][j];
xvis[i]|=1<<a[i][j];
yvis[j]|=1<<a[i][j];
}
}
}
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
if(a[i][j]!=0)continue;
tile tmp;
tmp.x=i,tmp.y=j;
tmp.g=cg(i,j);
vct.push_back(tmp);
}
}
stable_sort(vct.begin(),vct.end(),cmp);
dfs(0);
return 0;
}

靶形数独:增加权重的数独

大致与普通数独相同,讲几个重要剪枝:

  1. 增加估价函数 \(g(x)\),计算方式为:当前剩下的没填的格子数 \(\times\) 当前没填的数字的和 \(\times 9+\) 先前加过的分。当估价函数小于当前答案时,直接剪枝。其中,\(9\) 是期望值。
  2. 把 vector 改为普通数组。
  3. 乱搞排序,先按能填的个数从小到大排,再按横坐标从右到左排。这个方法不适用所有题,只是针对 CCF 的数据。
  4. 位运算,这里的位运算是要比 bitset 快的。
  5. 使用 stable_sort

这里不能搜到结果就直接结束,因为要使结果最大。

代码如下,本题没有完美的做法:1.77s,还是比较快的。

#include <bits/stdc++.h>
using namespace std;
int a[15][15],ans=-1,lst=81,cnt=0;
int xvis[15],yvis[15],svis[15];
int w[15][15]={{},{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}};
inline int s(int x,int y)
{
return ((x-1)/3*3+(y+2)/3);
}
int cg(int x,int y)
{
int ans;
int res=0;
ans=xvis[x]|yvis[y]|svis[s(x,y)];
for(int i=1;i<=9;i++)if(ans>>i==0)res++;
return res;
}
struct tile{
int x,y,g;
};
tile vct[105];
bool cmp(tile a,tile b)
{
if(a.g!=b.g)return a.g<b.g;
return a.x>b.x;
}
void dfs(int now,int sw,int lsm)
{
if(now>=cnt)
{
ans=max(ans,sw);
return;
}
if(sw+9*lsm*lst<ans)return;
int x=vct[now].x,y=vct[now].y;
int tmps=s(x,y);
int ans=xvis[x]|yvis[y]|svis[tmps];
for(int i=9;i>=1;--i)
{
if(((ans>>i)&1)==0)
{
xvis[x]|=(1<<i);
yvis[y]|=(1<<i);
svis[tmps]|=(1<<i);
a[x][y]=i;
--lst;
dfs(now+1,sw+i*w[x][y],lsm-i);
xvis[x]=(xvis[x]^(1<<i));
yvis[y]=(yvis[y]^(1<<i));
svis[tmps]=(svis[tmps]^(1<<i));
a[x][y]=0;
++lst;
}
}
}
int main()
{
int lsm=405,prsum=0;
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
cin>>a[i][j];
if(a[i][j]!=0)
{
svis[s(i,j)]|=1<<a[i][j];
xvis[i]|=1<<a[i][j];
yvis[j]|=1<<a[i][j];
lst--;
lsm-=a[i][j];
prsum+=w[i][j]*a[i][j];
}
}
}
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
if(a[i][j]!=0)continue;
tile tmp;
tmp.x=i,tmp.y=j;
tmp.g=cg(i,j);
vct[cnt++]=tmp;
}
}
stable_sort(vct,vct+cnt,cmp);
dfs(0,prsum,lsm);
cout<<ans;
return 0;
}

Luogu P1784 数独 [ 模板 ] / P1074 靶形数独 题解 [ 蓝 ] [ 深搜 ] [ 剪枝 ] [ 卡常 ]的更多相关文章

  1. 一本通例题-生日蛋糕——题解<超强深搜剪枝,从无限到有限>

    题目传送 显然是道深搜题.由于蛋糕上表面在最底层的半径确认后就确认了,所以搜索时的面积着重看侧面积. 找维度/搜索面临状态/对象:当前体积v,当前外表面面积s,各层的半径r[],各层的高度h[]. 可 ...

  2. P1074 靶形数独

    P1074 靶形数独正着搜80分,完全倒置95分,完全倒置后左右再倒置,就会A掉,到时候脑洞要大一些. #include<iostream> #include<cstdio> ...

  3. 洛谷——P1074 靶形数独

    P1074 靶形数独 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z ...

  4. P1074 靶形数独 dfs+预处理

    https://www.luogu.org/problemnew/show/P1074 显然是dfs 而且没有什么剪枝记忆化之类的 但是预处理比较麻烦 我用三个二维数组存状态:visx[x][i]代表 ...

  5. P1074 靶形数独题解

    题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶 ...

  6. luogu P1074 靶形数独

    题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了他最近发明的 ...

  7. 洛谷P1074 靶形数独 [搜索]

    题目传送门 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了 ...

  8. 洛谷P1074 靶形数独【dfs】【剪枝】

    题目:https://www.luogu.org/problemnew/show/P1074 题意: 数独的分数如下.一个数独的总分数就是权值乘所填数字之和. 现在给一个未完成的数独,问分数最高的数独 ...

  9. 洛谷—— P1074 靶形数独

    https://www.luogu.org/problem/show?pid=1074 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但 ...

  10. 洛谷 P1074 靶形数独 Label:search 不会

    题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了他最近发明的 ...

随机推荐

  1. 导航管理工具之OneNav

    github:https://github.com/helloxz/onenav 解决痛点:经常使用的链接,时常被问起, 还要翻找之前的很老的记录,反反复复比较浪费平常的开发时间, 如果可以把这些常用 ...

  2. postgres 在centos 安装

    执行如下命令安装POSTGRES sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86 ...

  3. uniapp 消息推送

    1.前言 作为一个非原生App的开发者,对于手机系统的推送机制了解是是非有限的,只有了解清楚这些机制,后期的开发才会少踩很多坑,本文将对推送机制逻辑进行一个简单的梳理与记录 2.推送流程 推送流程1. ...

  4. 重磅推出 Sdcb Chats:一个全新的开源大语言模型前端

    重磅推出 Sdcb Chats:一个全新的开源大语言模型前端 在当前大语言模型(LLM)蓬勃发展的时代,各类 LLM 前端层出不穷.那么,为什么我们还需要另一个 LLM 前端呢? 最初的原因在于质感的 ...

  5. .NET 模拟&编辑平滑曲线

    本文介绍不依赖贝塞尔曲线,如何绘制一条平滑曲线,用于解决无贝塞尔控制点的情况下绘制曲线.但数据点不在贝塞尔曲线的场景. 在上一家公司我做过一个平滑曲线编辑工具,用于轮椅调整加减速曲线.基于几个用户可控 ...

  6. docker安装配置redis

    ​ 安装redis docker pull redis 配置数据路径 mkdir -p /home/redis/data docker启动 docker run -d -v /home/redis/d ...

  7. 在 Windows 上运行 Podman: 操作指南

    在 Windows 上运行 Podman: 操作指南 https://www.redhat.com/sysadmin/run-podman-windows 2021 年 9 月的时候,我写过一篇关于如 ...

  8. 微服务之调用链(Feign+SpringCloud)

    终于到了我们的重点,微服务了. 与使用OkHttp3来实现的客户端类似,Feign接口本来也就是一个Http调用,依然可以使用Http头传值的方式,将 Trace 往下传. 本文更多的是关于 Spri ...

  9. Windows10 64环境下用Qt5.12.12自带的mingw730_64构建编译OpenCV4.1.0时cmake-3.20.6中的参数配置

    一.环境条件说明: 操作系统:Windows10 64环境 编译工具:用Qt5.12.12自带的mingw730_64构建 构建对象:编译OpenCV4.1.0的Release 64位和Debug 6 ...

  10. IDEA中基于SSM框架进行web开发部署项目到Tomcat时报错:Error:Cannot build artifact '******:war exploded' because it is included into a circular depency的解决办法

    在Idea中使用Maven创建父子工程,第一个Model的那个项目可以很好的运行,在创建一个Model运行时报这个错.原因是tomcat部署了多个Web项目,可能最开始是两个项目的配置文件混用用,最后 ...