这里是22平方傻君 , 长久没有更过博客了嘞,那么今天就来谈谈一道有趣的状态压缩题(孙便让各位进入轮廓线的深坑之中,轮廓线赛高,miku sama赛高)
problem:
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。
Input
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output
方案数。
Sample Input
3 2
Sample Output
16
解法一:轮廓线dp:
首先声明,轮廓线是一种特殊的状态压缩模型。。。这东西开始学起来的时候是需要毁一下我们对状态压缩的三官,但是学会后其实会发现它是一个挺巧妙的dp思想;
轮廓线dp一般适用于方格中的操作,比如说放插头填充方块(插头dp问题),互补干扰放置问题(乱取的名字),这道题其实就是后者,各个国王的放置需要满足一定的放置条件;这一类问题几乎都是可以用另外的装压dp来解决,但是这些装压dp的思路有的会十分巧妙难想(就好比一些几何题目的巧解),而用轮廓线解题则是有规律的而且好想的(可以想成是解几何题是用的建坐标系爆算),而且只要把所有情况考虑周全便会变的佷简单;
若是没有学过轮廓线或是对它还不太熟悉的话,建议可以看看刘汝佳大大的蓝书中的对应章节;在学习之前我建议可以把轮廓线理解为是一个会滑动的状态。。。就像一条蛇一样。。。就像这样:
蛇身上的每个格子就是一个状态,然后它就开始沿着整个区域开始s型滑动;
那么这道题也是如此:
一下便是我的代码:
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100000
using namespace std;
int dp[2][81][1<<11] , n , m , cur ;
long long ans(0);
int main(){
scanf("%d %d",&n,&m);
int all = (1<<n+1) - 1;
for(int i = 1 ; i <= n*n ; cur^=1, i++){
memset(dp[cur] , 0 , sizeof dp[cur]);
dp[cur^1][0][0] = 1;
for(int k = 1 ; k <= m ; k++){
for(int j = 0 ; j <= all ; j++){
dp[cur][k][j>>1] += dp[cur^1][k][j];
if(i%n == 1 && (j&6) == 0 )dp[cur][k][(j>>1) | (1<<n)]+=dp[cur^1][k-1][j];
else if(i%n == 0 && (j&3) == 0 && (j&(1<<n)) == 0)dp[cur][k][(j>>1) | (1<<n)]+=dp[cur^1][k-1][j];
else if((j&7) == 0 && (j&(1<<n)) == 0)dp[cur][k][(j>>1) | (1<<n)]+=dp[cur^1][k-1][j];
}
}
}
for(int i = 0 ; i <= all ; i++)ans+=dp[cur^1][m][i];
printf("%lld",ans);
}
解法二:状态压缩dp
然而这道题目。。。那个N <= 9 的条件已经很明显的说明了这道题是状压了嘛 , 而且还是一道相对比较裸的题目,裸题啊。。。然后。。。我就开心的去写轮廓线去了(虽然说轮廓线其实也是一种状压)。。。考试后看了一下黄学长的博文 , 结果发现确实状态压缩的思路还是佷巧妙的。。。
链接:http://hzwer.com/1461.html