状压dfs小记
一点前(tu)言(cao)
真的考起dfs来可谓是什么都能往dfs上套
状压不止能dp,还能与dfs结合成为搜索好(duliu)题
剪枝卡常司空见惯(打开题解一看并不是纯dfs,emmmm)
开始正文
说到搜索,大家都做过八皇后对叭。现在我们让八皇后变成N皇后
还是N皇后
这里和八皇后不同的是地图中有些地方不可以放皇后(而且数据范围还比八皇后大1)然后它就蓝了.jpg
我们考虑像八皇后一样直接标记列,对角线爆搜,发现会T。
何以解T?唯有剪压(全称剪枝状压)
我们发现\(n \leq 14\),放在dp中这个数据一般是让你状压,于是我们可以尝试一下状压剪枝。在某个位置放皇后,会对列,左斜的对角线,右斜的对角线有影响,所以我们可以把它们分别状压成三个量,表示下一行的状态。用1表示不能放,用0表示能放。由于我们是表示的下一行的状态,所以对于左斜的对角线来说,每往下传递一行,要左移一位,右斜的对角线就要右移一位。这样很可能会导致某些1被移出棋盘。被移到棋盘右边的1在位运算里直接没了,不用管。对于被移到棋盘左边的1,我们可以拿放满的状态(即棋盘范围内全是1)和它进行and操作,消除棋盘外的1。对于每一行来说,当前行的状态就是列,左斜,右斜的三个数或起来,当然别忘了最开始输入的不可放的位置。
我们用0表示可以放的位置,但是找到当前最低位的0似乎并没有太好的做法。这时候通过脑洞想到lowbit可以找到当前二进制表示中最低位的1。于是我们可以进行取反操作,然后对取反后的数不断取它的lowbit值来找到当前可以放皇后的位置。
同时我们还要状压一下哪些行可以放皇后,如果能放满,就说明这是个合法的方案。
由于位运算常数小,所以在我们没有剪枝的情况下也是可以轻松AC这道题的。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define pa pair<int,int>
using namespace std;
typedef long long ll;
inline ll read()
{
char ch=getchar();
ll x=0;bool f=0;
while(ch<'0'||ch>'9')
{
if(ch=='-')f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f?-x:x;
}
ll n,m,ok[19],k,ans,en;//ok[i]表示输入的地图中第i行的状态,en就是每行都放满的状态
char mp[19][19];
ll lowbit(int x)
{
return x&(-x);
}
void dfs(int now,ll lie,ll x1,ll x2,ll hang)//x1是左斜对角线,x2是右斜对角线,now记录dfs到了第几行,hang记录哪些行放了皇后
{
if(hang==en){ans++;return ;}
ll kk=~(ok[now]|x1|x2|lie);
kk&=en;
while(kk)
{
ll v=lowbit(kk);
kk-=v;
dfs(now+1,lie|v,(x1|v)<<1,(x2|v)>>1,hang|(1<<now));//注意更新在当前列放皇后的影响
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
scanf("%s",mp[i]+1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
if(mp[i][j]=='.')
ok[i]|=(1<<j);
}
en=(1<<n+1)-1;
en^=1;//在这里2^1表示第一位,而不是2^0
dfs(1,0,0,0,0);
printf("%lld\n",ans);
}
状压+dfs不止能解决皇后问题,还能解决数独问题。
靶形数独
咱也不造为啥状压完了不用剪枝就能过.jpg
显然我们需要先处理每个九宫格的表示方法和每个格子的分数,由于我太菜了,所以打了个表然后进行\(O(1)\)询问即可
打的表:
int gt[15][15]=//分数
{ {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},
},
gz[15][15]=//每个格子
{ {0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
};
各位大佬如果有公式求告知
我们先考虑如果人类玩数独应该怎么玩。显然我们要选一个确定的数比较多的地方开始搜索。在这里为了调试的时候比较优雅我只会从行搜,我们选取已知数字较多的行开始搜索。
我们先参照上一个题的思路,考虑对行,列,九宫格进行状压。wait!对九宫格进行状压!压个\(peach\)
所以我们就这么干脆的放弃了全部状压。其实对列,九宫格状压没有什么意义,只要标记一下哪一些填了哪些数就可以了。bool数组肯定是开的下的。那么我们考虑对每一行中填了哪些数进行状压,以便快速的找到第一个可以填数的位置。状压方法和上一个题相同,也是用0表示可以填的位置,1表示不可以填的位置,询问的时候取反+lowbit即可。
判断无解:在输入的时候先统计好确定的数能拿到的总分数,如果最终分数没变,即无解
由于这题玄学的数据和我也不会算的复杂度,我们可以以最高666ms/一个数据点的速度AC掉这道题
具体看代码叭(博主语文能力有限无法详细表达.jpg
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline ll read()
{
char ch=getchar();
ll x=0;bool f=0;
while(ch<'0'||ch>'9')
{
if(ch=='-') f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f?-x:x;
}//被某些毒瘤题卡掉过的快读
int mp[10][10],ans,jg[10][10],hh[15];//mp为初始的数独,jg记录每个九宫格里面哪个数被用过了
struct ifm{
int id,wz,lz;//wz记录信息完整度(也就是有多少个非零数),lz记录状压之后的数
}hang[15],lie[15];//在博主一番调(mo)试(gai)之后lie没有用了
int cpy[15][15],wwz;//wwz记录有多少个位置不是0,cpy在博主的一番调(mo)试(gai)之后也没有用了
bool bjh[15][10],bjl[15][10];//bjh[i][j]表示在第i行里,j这个数是否使用过,bjl是记录列
int gt[15][15]=
{ {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},
},
gz[15][15]=
{ {0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
};//上面打好的表
int er[1029];//er[2^x]=x,在lowbit之后算出是第几列
bool cmp(ifm a,ifm b)
{
return a.wz>b.wz;
}
int lowbit(int x)
{
return x&(-x);
}
void dfs(int ha,int ann,int now)//ha表示当前填第几行,ann表示当前的分数,now表示这是在排序后的hang里的第几个
{
if(wwz==81)
{
ans=max(ans,ann);
return;
}
if(hh[ha]!=1022)//由于窝习惯用2^1表示第一个数,所以是1023-1=1022
{
int kkk=~hh[ha];
kkk^=1;
int v=lowbit(kkk);
for(int i=1;i<=9;i++)
{
if(!bjh[ha][i]&&!bjl[er[v]][i]&&!jg[gz[ha][er[v]]][i])//判断能否填数
{
bjh[ha][i]=1;
hh[ha]|=v;
cpy[ha][er[v]]=i;
bjl[er[v]][i]=1;
jg[gz[ha][er[v]]][i]=1;//一堆神烦的标记
if(hh[ha]!=1023)//如果填了之后,这一行还没填满,就去填这一行的下一个数
{
wwz++;
dfs(ha,ann+i*gt[ha][er[v]],now);
}
else wwz++,dfs(hang[now+1].id,ann+i*gt[ha][er[v]],now);//这一行填满了,搜下一个要搜的行
bjl[er[v]][i]=0;
jg[gz[ha][er[v]]][i]=0;
hh[ha]^=v;
bjh[ha][i]=0;
wwz--;
cpy[ha][er[v]]=0;
}
}
}
else//如果当前行已经填满了,就填下一个
dfs(hang[now+1].id,ann,now+1);
}
int main()
{
er[1]=0;
for(int i=1;i<=9;i++)
er[1<<i]=i;
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
{
mp[i][j]=read();
if(mp[i][j])
{
hang[i].wz++;
lie[j].wz++;
hang[i].lz|=(1<<j);
lie[j].lz|=(1<<i);//应该没有用了叭
jg[gz[i][j]][mp[i][j]]=1;
ans+=mp[i][j]*gt[i][j];
wwz++;
bjh[i][mp[i][j]]=1;
bjl[j][mp[i][j]]=1;
}
}
for(int i=1;i<=9;i++)
hang[i].id=i,lie[i].id=i;
for(int i=1;i<=9;i++)
hh[i]=hang[i].lz;
sort(hang+1,hang+10,cmp);//保证先搜已知数字多的行
sort(lie+1,lie+10,cmp);//大概没有用了叭
int qwq=ans;
dfs(hang[1].id,ans,1);
if(ans==qwq) ans=-1;//判断无解
printf("%d\n",ans);
}
提交一下
草.jpg
状压dfs小记的更多相关文章
- ZOJ 1609 Equivalence(状压+dfs减枝)
ZOJ Problem Set - 1609 Equivalence Time Limit: 5 Seconds Memory Limit: 32768 KB When learning m ...
- bzoj1725: [Usaco2006 Nov]Corn Fields牧场的安排(状压dfs)
1725: [Usaco2006 Nov]Corn Fields牧场的安排 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 1122 Solved: 80 ...
- codeforces 285 D. Permutation Sum 状压 dfs打表
题意: 如果有2个排列a,b,定义序列c为: c[i] = (a[i] + b[i] - 2) % n + 1 但是,明显c不一定是一个排列 现在,给出排列的长度n (1 <= n <= ...
- JZYZOJ1530 [haoi2013]开关控制 状压 dfs 折半搜索
http://172.20.6.3/Problem_Show.asp?id=1530 元宵节快要到了,某城市人民公园将举办一次灯展.Dr.Kong准备设计出一个奇妙的展品,他计划将编号为1到N的N(1 ...
- UVALive 6255:Kingdoms(状压DFS)
题目链接 题意 给出n个王国和n*n的矩阵,mp[i][j] 代表第 i 个王国欠第 j 个王国 mp[i][j] 块钱.如果当前的王国处于负债状态,那么这个王国就会被消除,和它相连的王国的债务都会被 ...
- 2018icpc南京网络赛-E AC Challenge(状压+dfs)
题意: n道题,每道题有ai和bi,完成这道题需要先完成若干道题,完成这道题可以得到分数t*ai+bi,其中t是时间 1s, n<=20 思路: 由n的范围状压,状态最多1e6 然后dfs,注意 ...
- hdu 4620 Fruit Ninja Extreme(状压+dfs剪枝)
对t进行从小到大排序(要记录ID),然后直接dfs. 剪枝的话,利用A*的思想,假设之后的全部连击也不能得到更优解. 因为要回溯,而且由于每次cut 的数目不会超过10,所以需要回溯的下标可以利用一个 ...
- HDU - 6341 多校4 Let Sudoku Rotate(状压dfs)
Problem J. Let Sudoku Rotate Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K ...
- HDU - 6006 Engineer Assignment (状压dfs)
题意:n个工作,m个人完成,每个工作有ci个阶段,一个人只能选择一种工作完成,可以不选,且只能完成该工作中与自身标号相同的工作阶段,问最多能完成几种工作. 分析: 1.如果一个工作中的某个工作阶段没有 ...
随机推荐
- vue-时间插件,效果见图
<template> <div class="select-time-wrapper"> <h5 class="titie"> ...
- 设置Windows静态IP+动态IP
静态IP 设置以太网属性 进入IPv4属性 设置IPv4 动态IP 同上方法,只不过选成了自动
- hadoop的价值在哪里
新霸哥注意到了在移动互联网时代,很多的传统的系统架构已经不能满足现有的业务需要了,在大数据时代,我们需要新的架构才能满足海量数据处理能力. 在实际的应用中数据增长非常的迅速,数据类型也变的丰富了,业务 ...
- django 自带认证系统(login,logout,authenticate,login_required)
from django.contrib.auth import login,authenticate,logoutfrom django.contrib.auth.decorators import ...
- CUDA升级后
打开工程文件.vcxproj,找到 <Import Project="$(VCTargetsPath)\BuildCustomizations\CUDA 10.0.props" ...
- ios json数据null的处理
此处我把json数据中的null转换成了@"",防止出现null程序崩掉,可以把下边宏代码复制到pch中,就可以在整个程序中引用了 #define DSStringValue(va ...
- Linux perf命令详解及常用参数解析
perf 相关命令:暂无相关命令 perf是Linux下的一款性能分析工具,能够进行函数级与指令级的热点查找. Perf List利用perf剖析程序性能时,需要指定当前测试的性能时间.性能事件是指在 ...
- spark 三种数据集的关系(一)
Catalyst Optimizer: Dataset 数据集仅可用Scala或Java.但是,我们提供了以下上下文来更好地理解Spark 2.0的方向数据集是在2015年作为Apache Spark ...
- Git账号Window10系统配置密钥
Git 拉取 推送 报错 . Window10系统 需要配置Git账号密钥.
- python-文件校验
使用hashlib的md5方法对文件进行加密,目的是为了保证文件在传输的过程中是否发生变化. #!/usr/bin/python3 # coding:utf-8 # Auther:AlphaPanda ...