状压dp题解

数位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即可。

猜你喜欢

转载自www.cnblogs.com/hbhszxyb/p/12710188.html
今日推荐