• 状压DP之互不侵犯


    题目描述

    这里
    (N*N) 的棋盘里面放(k)个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

    输入格式

    只有一行,包含(N,K)两个数 。

    输出格式

    所得方案数。

    样例

    样例输入

    3 2

    样例输出

    16

    思路

    我们可以想到,对于当前行的影响有当前行的状态,上一行的状态(因为国王的攻击范围可以从上一行包括到这一行),以及当前行的国王数,那么我们可以用一个三维数组(f[n][k][1<<n-1]),用来代表第一维代表前(i)行((1<i<=n)),第二维代表在前(i)行放(j(0<=j<=k))个国王,第三维代表第(i)行的状态,对于f数组的初始化,只需要将(f[0][0][0])初始化为1即可;
    对于上一行的判断,我们现在用S表示当前行状态,用s表示上一行状态,那我们就有(if(S&s || (S<<1)&s || (S>>1)&s)continue),显然,我们还应该对当前行以及上一行进行判断(当前行和上一行的国王不能),显然有(if((s<<1)&s)continue),(if((S>>1)&S)continue);
    对于当前行的状态我们有(f[i][j][S]+=f[i-1][j-Q(S)][s])(Q函数用来求改状态下的国王个数,即1的个数,需要用到lowbit)。

    代码

    
    
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=(1<<9)-1;
    long long f[10][100][maxn];
    int lowbit(int x){
    	return x&-x;
    }
    int Q(int x){
    	int cnt=0;
    	for(int i=x;i;i-=lowbit(i))cnt++;
    	return cnt;
    	
    }
    int main(){
    	int n,k;
    	cin>>n>>k;
    	int maxs=1<<n;
    	f[0][0][0]=1;
    	for(int i=1;i<=n;i++){//枚举每一行
    		for(int S=0;S<maxs;S++){//枚举当前行状态
    			if((S>>1)&S)continue;
    			for(int s=0;s<maxs;s++){//枚举上一行的状态
    				if((s<<1)&s)continue;//去掉上一行排斥情况(可以无)
    				if(S&s || (S<<1)&s || (S>>1)&s)continue;//去掉当前行去上一行冲突情况
    				for(int j=Q(S);j<=k;j++){//枚举前i行的国王个数
    					f[i][j][S]+=f[i-1][j-Q(S)][s];
    				}
    			}
    		}	
    	}
    	long long ans=0;
    	for(int i=0;i<=maxs;i++){
    		ans+=f[n][k][i];
    	}
    	cout<<ans;
    
    }
    
    
    
  • 相关阅读:
    MySQL(一)序
    Mockito 小结
    如何入门一个开源软件
    面经
    琐碎的想法(四)键盘布局、快捷键的由来
    琐碎的想法(二)网络协议——人们给计算机的一组“约定”
    Java源码赏析(六)Class<T> 类
    Java随谈(五)Java常见的语法糖
    Java随谈(四)JDK对并发的支持
    Event Loop和宏任务微任务
  • 原文地址:https://www.cnblogs.com/soda-ma/p/13189741.html
Copyright © 2020-2023  润新知