数位dp题解:
A. 特殊方格棋盘
-
题意:在\(n\times n\)的方格棋盘上放置
n
个车,某些格子不能放,求使它们不能互相攻击的方案总数。注意:同一行或同一列只能有一个车,否则会相互攻击。 -
分析:
- 首先,每一行只能有一个车,只要一行行地放,每行只放一个,保证同一行之间不会有攻击。
- 其次,每一列只能放一个车,只要能记录下哪一列有车,下次不再考虑这一列就可以了。
- 用一个二进制数
S
来表示某一列是否已经放置了车。例如n=5, S=01101
,就表示第一、三、四列(从低位开始)已经放置了车。我们最终求出状态S=11111
时的方案数。 - 定义
f[s]
表示在状态s
下的方案数。\(s\in [0\sim 2^n-1]\)- 假设
s=01101
,显然有:f[01101]= f[01100]+f[01001]+f[00101]
- 但如果在当前位置有障碍呢?所以我们再放置车的时候判断当前位是否有障碍。具体实现见代码。
- 假设
-
代码实现:
#include<bits/stdc++.h> const int maxn=(1<<20)-1; typedef long long LL; LL f[maxn],a[25];//a[x]记录第x行的障碍状态 int lowbit(int x){return x & -x;}; int main(){ int n,m;scanf("%d%d",&n,&m); for(int i=1;i<=m;++i){ int x,y;scanf("%d%d",&x,&y); a[x]+=1<<(y-1); } f[0]=1;//一个也不放也是一种方案 int maxs=1<<n;//最大的状态 for(int S=1;S<maxs;++S){//从状态1开始枚举 int cnt=0;//计算状态S里的1的个数,几个1说明处理到第几行 for(int i=S;i;i-=lowbit(i))cnt++; for(int i=S;i;i-=lowbit(i)){//依次判断当前状态每一个1 if(!(a[cnt] & lowbit(i))){//状态S当前的1所在列如果没有障碍 int s=S^lowbit(i);//说明lowbit(i)处可以放车,当前就在此处放车 f[S]+=f[s];//s<S,保证能转移 } } } printf("%lld\n",f[maxs-1]); return 0; }
B. 互不侵犯
- 题意:在\(n\times n\) 的棋盘里面放
n
个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共 个格子。 - 分析:
- 此题和我们讲的例题玉米地很相似,少了障碍,但多了个数限制,一般情况多一个限制我们就增加一维的状态。
- 设
dp[ i ][ j ][ k ]
为第i
行摆放国王状态为j
,棋盘上国王数目为k
的方案数. - 阶段很明显,就是我们可以一行一行的处理,第一行比较特殊,我们可以预处理
- 判断冲突:假设
s
为当前行,s'
为上一行。- 行冲突,即不能相邻,只需判断
(s & (s<<1))==0
,不为零说明有相邻。 - 列冲突:
- 上下冲突:判断
(s & s')==0
,如果不为0,说明上下冲突。 - 左上冲突:判断
((s<<1) & s')==0
,如果不为0
,说明冲突。 - 右上冲突:判断
((s>>1) & s')==0
,如果不为0
,说明冲突。
- 上下冲突:判断
- 判断当前状态下状态的
1
的个数,用lowbit即可。
- 行冲突,即不能相邻,只需判断