前言:
最近学了这个神奇的东东,真的好难啊[仙女叹气]~
感觉自己没大理解,就写笔记强迫自己学会吧QAQ
定义:
状态压缩动态规划,就是我们俗称的状压 D P DP DP,是利用计算机二进制的性质来描述状态的一种 D P DP DP方式。
自己的小理解:其实就是将状态压缩成二进制下的数( 1 / 0 1/0 1/0)来表示不同情况,进行 D P DP DP……
前置知识:
- 二进制 和 位运算;
- D P DP DP;
- 以及一个聪明的小脑袋瓜;
位运算介绍:
一个神奇的东西……
状压 D P DP DP里主要用到的几个技巧见下图:
引入:
题目 :互不侵犯
题意简述:
在 N × N N×N N×N的棋盘里面放 K K K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到他周围的8个格子。
题目分析:
d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示第 i i i行状态为 j j j,一共放了 k k k个国王时的方案数。
当前方案数仅与上一行有关 ( ( ( 每一行只需要判断与上一行是否攻击 ) ) )
状态转移方程:
d p [ i ] [ j ] [ k ] + = d p [ i − 1 ] [ s ] [ k − c n t ( s ) ] dp[i][j][k] += dp[i - 1][s][k - cnt(s)] dp[i][j][k]+=dp[i−1][s][k−cnt(s)]
s s s为上一行满足条件的状态, c n t cnt cnt是上一行摆放的国王数量。
最终答案:
再次枚举每一个状态:
a n s + = d p [ n − 1 ] [ s ] [ k ] ans += dp[n - 1][s][k] ans+=dp[n−1][s][k];
完整代码:
#include <cstdio>
#include <cmath>
#include <algorithm>
#define ll long long//不开long long见祖宗
using namespace std;
int n,k,cnt[1 << 15];
ll ans,dp[15][1 << 15][100];//状态:1表示该位置放了国王,0没有放
bool flag[1 << 15];
int Count(int x) {
int res = 0;
while(x) {
res += x & 1;
x >>= 1;
}
return res;
}
bool check1(int x) {
for(int i = 0; i + 1 < n; i ++) {
if((x & (1 << i)) && (x & (1 << (i + 1))))
return 0;//如果有相邻的国王,不符条件
}
return 1;
}
bool check2(int x,int y) {
for(int i = 0; i < n; i ++) {
if(x & (1 << i)) {
if(y & (1 << i)) return 0;//上为1
if(i + 1 < n && (y & (1 << (i + 1)))) return 0;//左上为1
if(i - 1 < n && (y & (1 << (i - 1)))) return 0;//右上为1
}
}
return 1;
}
signed main() {
scanf("%d %d",&n,&k);
int m = 1 << n;
for(int i = 0; i < m; i ++) {
flag[i] =