zoukankan      html  css  js  c++  java
  • 暑假集训Day2 互不侵犯(状压dp)

    这又是个状压dp大型自闭现场

    题目大意:

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

    输入格式:

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

    输出格式:

    所得的方案数。

    算法分析:

    1.显然这又是一道状压的题
    2.显然一样是用f数组表示方案数

    But 这个f数组需要开三维

    为什么呢
    我们首先分析一下f的转移情况 f的状态与什么有关呢 首先我们很容易知道我们的dp是从上往下一点点递推实现的 而这个国王会攻击到自己的身旁八个位置
    所以呢 那f就会对自己的下一行产生影响 而且只会对自己的下一行产生影响 换而言之 f只和自己上一行的状态有关
    这样的话我们就会用到两维,一维存储前i行 另一维存储状态 但是看到这个题只用两维是不够的 因为这个题需要放置的国王个数恰好等于K
    所以我们将前i行放置的国王个数作为一维
    和平时的状压题一样我们将状态作为最后一维 所以关于f[i][j][k]的定义我们有 前i行共放了j个国王而且第i行所放国王的状态为k 的方案数
    所以我们就可以写出这样的状态转移方程

    int sum(int x){
    	int cnt = 0;
    	for(int i = x;i;i-=lowbit(i))cnt++;
    	return cnt;
    }
    
    for(int i = 1;i <= n;++i)
    		for(int j = 0;j < maxs;++j){//枚举本行状态
    			if(j & (j<<1))continue;//如果该状态冲突则跳过
    			for(int k = 0;k < maxs;++k){//枚举上一行状态
    				if(j & k || j & (k << 1) || j & (k>>1))continue;//如果当前行状态和上一行状态冲突则跳过
    				for(int s = sum(j);s <= m;++s)//枚举前i行所放的国王个数
    					f[i][s][j] += f[i-1][s - sum(j)][k];//+=上一行的成立状态数
    			}
    		}
    

    这个代码的注释已经很清晰了
    但是我们再具体分析一下思想(如果不理解sum函数的去翻暑假集训day2 特殊方格棋盘
    首先我们枚举行数
    然后枚举本行的状态
    本行状态与自己冲突的情况就是(j & (j<<1))为真 j有两位二进制位同时为1 那么才可能(j&j<<1)为真
    举个栗子:j = 01100 那么j<<1就是11000和j取与后并不等于0 所以冲突(即连续两个格子放置了国王,他们互相攻击)

    3.然后就要枚举上一行的状态
    关于本行与上一行状态冲突的判定具体方法可参见上一条

    最后的累加:

    划重点: sum(j) 为当前行所放的国王个数,前i行的国王个数肯定就是大于等于这个数的 枚举前i行国王个数,然后减去sum(j)就是前i-1行共放的国王个数
    因此f[i-1][s-sum(j)][k] 就是 前i-1行共s-sum(j)个国王并且第i-1行的国王个数为k的方案数
    通过这个的累加就是我们的递推过程

    代码

    #include<bits/stdc++.h>
    using namespace std;
    long long f[10][100][1000],ans;//f[i][j][k]表示前i行放j个国王并且当前行状态为k的成立方案数
    int lowbit(int x){return x & -x;}
    
    int sum(int x){
    	int cnt = 0;
    	for(int i = x;i;i-=lowbit(i))cnt++;
    	return cnt;
    }
    
    int main(){
    	int n,m;scanf("%d%d",&n,&m);
    	f[0][0][0] = 1;
    	int maxs = 1<<n;
    	for(int i = 1;i <= n;++i)
    		for(int j = 0;j < maxs;++j){//枚举本行状态
    			if(j & (j<<1))continue;//如果该状态冲突则跳过
    			for(int k = 0;k < maxs;++k){//枚举上一行状态
    				if(j & k || j & (k << 1) || j & (k>>1))continue;//如果当前行状态和上一行状态冲突则跳过
    				for(int s = sum(j);s <= m;++s)//枚举前i-1行所放的国王个数
    					f[i][s][j] += f[i-1][s - sum(j)][k];//+=上一行的成立状态数
    			}
    		}
    	for(int i = 0;i < 1<<n;++i)ans += f[n][m][i];//ans+=前n行放m个国王并且当前行状态为i
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    点点关注
    谢谢观看>)<

    如初见 与初见
  • 相关阅读:
    LeetCode Single Number
    Leetcode Populating Next Right Pointers in Each Node
    LeetCode Permutations
    Leetcode Sum Root to Leaf Numbers
    LeetCode Candy
    LeetCode Sort List
    LeetCode Remove Duplicates from Sorted List II
    LeetCode Remove Duplicates from Sorted List
    spring MVC HandlerInterceptorAdapter
    yum
  • 原文地址:https://www.cnblogs.com/HISKrrr/p/13189560.html
Copyright © 2011-2022 走看看