这又是个状压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\n",ans);
return 0;
}

点点关注

谢谢观看>)<

暑假集训Day2 互不侵犯(状压dp)的更多相关文章

  1. BZOJ1087[SCOI2005]互不侵犯——状压DP

    题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入 只有一行,包含两个数N,K ( ...

  2. NOI P1896 互不侵犯 状压DP

    题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 注:数据有加强(2018/4/25) ...

  3. P1896 [SCOI2005]互不侵犯 状压dp

    正解:状压dp 解题报告: 看到是四川省选的时候我心里慌得一批TT然后看到难度之后放下心来觉得大概没有那么难 事实证明我还是too young too simple了QAQ难到爆炸TT我本来还想刚一道 ...

  4. SCOI2005 互不侵犯 [状压dp]

    题目传送门 题目大意:有n*n个格子,你需要放置k个国王使得它们无法互相攻击,每个国王的攻击范围为上下左走,左上右上左下右下,共8个格子,求最多的方法数 看到题目,是不是一下子就想到了玉米田那道题,如 ...

  5. [SCOI2005]互不侵犯 (状压$dp$)

    题目链接 Solution 状压 \(dp\) . \(f[i][j][k]\) 代表前 \(i\) 列中 , 已经安置 \(j\) 位国王,且最后一位状态为 \(k\) . 然后就可以很轻松的转移了 ...

  6. luogu1896 [SCOI2005]互不侵犯 状压DP

    题目大意 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子.( 1 <=N <=9, 0 ...

  7. [清华集训2015 Day1]主旋律-[状压dp+容斥]

    Description Solution f[i]表示状态i所代表的点构成的强连通图方案数. g[i]表示状态i所代表的的点形成奇数个强连通图的方案数-偶数个强连通图的方案数. g是用来容斥的. 先用 ...

  8. 暑假集训Day2 状压dp 特殊方格棋盘

    首先声明 : 这是个很easy的题 可这和我会做有什么关系 题目大意: 在n*n的方格棋盘上放置n个车,某些格子不能放,求使它们不能互相攻击的方案总数. 注意:同一行或同一列只能有一个车,否则会相互攻 ...

  9. BZOJ 1087: [SCOI2005]互不侵犯King [状压DP]

    1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3336  Solved: 1936[Submit][ ...

随机推荐

  1. 使用 git add -p 整理 patch

    背景 当我们修改了代码准备提交时,本地的改动可能包含了不能提交的调试语句,还可能需要拆分成多个细粒度的 pactch. 本文将介绍如何使用 git add -p 来交互式选择代码片段,辅助整理出所需的 ...

  2. [06]HTML基础之表单标签

    1. <form>标签 表单容器,指定method属性和action属性是个良好的习惯. <form methor="POST" action="htt ...

  3. rewrite和return的简单需求

    Rewrite 需求作业 背景:现在我有一个网站,www.linux.com www.linux.com访问主页面 friend.linux.com访问交友页面 blog.linux.com访问博客页 ...

  4. Java实现 蓝桥杯VIP 算法训练 连通分块(并查集)

    试题 算法训练 连通分块 资源限制 时间限制:200ms 内存限制:8.0MB 问题描述 连通分块 输入格式 输入的第一行包含两个整数n, m n代表图中的点的个数,m代表边的个数 接下来m行,每行2 ...

  5. Java实现 蓝桥杯VIP 算法训练 装箱问题

    题目描述 有一个箱子容量为V(正整数0≤V≤20000),同时有n个物品(0<n≤30,每个物品有一个体积(正整数). 要求nn个物品中,任取若干个装入箱内,使箱子的剩余空间为最小. 输入输出格 ...

  6. Java中数组二分法查找

    算法:当数组的数据量很大适宜采用该方法.采用二分法查找时,数据需是有序不重复的,如果是无序的也可通过选择排序.冒泡排序等数组排序方法进行排序之后,就可以使用二分法查找. 基本思想:假设数据是按升序排序 ...

  7. Java实现 蓝桥杯 算法训练 Multithreading

    问题描述 现有如下一个算法: repeat ni times yi := y y := yi+1 end repeat 令n[1]为你需要算加法的第一个数字,n[2]为第二个,-n[N]为第N个数字( ...

  8. java实现最大连续和问题

    /* 10 5 -3 12 -31 15 22 -7 6 -8 -9 10 .... 暴力:O(n^3) 分治:[ mid ) 三种情况求最大 基线法: O(n) 2个数组: 从左到本位:出现的最大累 ...

  9. STL中的set和multiset

    注意: 1.count() 常用来判断set中某元素是否存在,因为一个键值在set只可能出现0或1次. 2.erase()用法 erase(iterator)  ,删除定位器iterator指向的值 ...

  10. Zookeeper实现服务注册/发现

    what that? Zookeeper在分布式开发中使用频繁,但许多框架都对其进行了封装,初学者可能无法较好的理解其工作原理,该文章演示了使用Zookeeper实现服务注册,服务发现的简单demo, ...