一点前(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小记的更多相关文章

  1. ZOJ 1609 Equivalence(状压+dfs减枝)

    ZOJ Problem Set - 1609 Equivalence Time Limit: 5 Seconds      Memory Limit: 32768 KB When learning m ...

  2. bzoj1725: [Usaco2006 Nov]Corn Fields牧场的安排(状压dfs)

    1725: [Usaco2006 Nov]Corn Fields牧场的安排 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1122  Solved: 80 ...

  3. codeforces 285 D. Permutation Sum 状压 dfs打表

    题意: 如果有2个排列a,b,定义序列c为: c[i] = (a[i] + b[i] - 2) % n + 1 但是,明显c不一定是一个排列 现在,给出排列的长度n (1 <= n <= ...

  4. JZYZOJ1530 [haoi2013]开关控制 状压 dfs 折半搜索

    http://172.20.6.3/Problem_Show.asp?id=1530 元宵节快要到了,某城市人民公园将举办一次灯展.Dr.Kong准备设计出一个奇妙的展品,他计划将编号为1到N的N(1 ...

  5. UVALive 6255:Kingdoms(状压DFS)

    题目链接 题意 给出n个王国和n*n的矩阵,mp[i][j] 代表第 i 个王国欠第 j 个王国 mp[i][j] 块钱.如果当前的王国处于负债状态,那么这个王国就会被消除,和它相连的王国的债务都会被 ...

  6. 2018icpc南京网络赛-E AC Challenge(状压+dfs)

    题意: n道题,每道题有ai和bi,完成这道题需要先完成若干道题,完成这道题可以得到分数t*ai+bi,其中t是时间 1s, n<=20 思路: 由n的范围状压,状态最多1e6 然后dfs,注意 ...

  7. hdu 4620 Fruit Ninja Extreme(状压+dfs剪枝)

    对t进行从小到大排序(要记录ID),然后直接dfs. 剪枝的话,利用A*的思想,假设之后的全部连击也不能得到更优解. 因为要回溯,而且由于每次cut 的数目不会超过10,所以需要回溯的下标可以利用一个 ...

  8. HDU - 6341 多校4 Let Sudoku Rotate(状压dfs)

    Problem J. Let Sudoku Rotate Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K ...

  9. HDU - 6006 Engineer Assignment (状压dfs)

    题意:n个工作,m个人完成,每个工作有ci个阶段,一个人只能选择一种工作完成,可以不选,且只能完成该工作中与自身标号相同的工作阶段,问最多能完成几种工作. 分析: 1.如果一个工作中的某个工作阶段没有 ...

随机推荐

  1. 北京化妆时尚气息自适应CSS例子

    三里屯太古广场——北京化妆时尚气息的先锋阵地! “乐色起义”创意化妆设计大赛——国内最具创意的公益设计大赛! CNature——国内最具个性的时尚环保公益组织! 一个多么奇妙的组合!就在2010年的这 ...

  2. N1试卷常考词汇总结

    免れる まぬがれる 免去,幸免 軽率 けいそつ 轻率,草率 捩れる ねじれる 拧劲儿,扭歪,弯曲 裂ける さける 裂开,破裂 避ける さける 躲避,避开 つまむ 挟,捏,掐 追及 ついきゅう 追上.追 ...

  3. 从Spring看Web项目开发

    之前简单介绍过Spring框架,本文换个角度重新诠释Spring.使用Java语言开发的项目,几乎都绕不过Spring,那么Spring到底是啥,为何被如此广泛的应用,下面从以下两个问题出发来剖析Sp ...

  4. git push 到 github

    今天来简单整理一下,如何利用git命令把代码提交到GitHub平台上去,当然要提交代码到GitHub上去,您首先得要有GitHub账号,账号如何申请这里就不多做解释了 第一步:先到官网下载git安装包 ...

  5. 个人小应用服务器安装搭建,HP 360p Gen9 使用winpe安装centos[一]

    以前用aws的时候使用的ec2, 里面可选的windows server搭配umbraco的cms做了自己的个人网站,主要是当年项目需要,使用aws,我也办了国际币种卡,在组里各种联系亚马逊开服务,后 ...

  6. exec模块,元类与ORV的应用

    exec模块的补充 1.是什么? exec是一个Python内置模块. 2.exec的作用: ''' x = 10 def func1(): pass ''' 可以把"字符串形式" ...

  7. HDU - 6589 Sequence (生成函数+NTT)

    题目链接 设序列a的生成函数$\large f(x)=\sum\limits_{i=0}^{n-1}a_ix^i$,则操作1,2,3分别对应将$f(x)$乘上$\Large\frac{1}{1-x}, ...

  8. BZOJ1787 [Ahoi2008]Meet 紧急集合[结论题]

    location. 求到树上三点距离和最短的点及此距离. 这个不还是分类讨论题么,分两类大情况,如下图. 于是乎发现三个点对的lca中较深的那个lca是答案点.距离就是两两点对距离加起来除以2即可.这 ...

  9. vue data数据恢复初始化

    Object.assign(this.$data, this.$options.data())

  10. Python语法之垃圾回收机制

    目录 一 引入 二.什么是垃圾回收机制? 三.为什么要用垃圾回收机制? 四.垃圾回收机制原理分析 4.1.什么是引用计数? 4.2.引用计数扩展阅读 一 引入 解释器在执行到定义变量的语法时,会申请内 ...