标准的状压题!
所谓状态压缩就是将状态用二进制存储下来,并用位运算实现转移
用二进制存储,可以不重不漏地遍历到所有的状态
我们设0表示牛已在队伍中,1表示牛不在队伍中,最后需要求得全部为1的状态
设状态为f(i,j)表示第i头牛在队尾,队伍状态为j(一个二进制数)时的方案数
答案为∑n1f(i,(1111...1)2),这里(1111...1)2表示由n个1组成的二进制数
在讲到转移之前,有必要提一下位运算。将1左移N位,得到的二进制数其实是一个1后面带上N个0,这和我们需要的N个1不符
但是这个时候我们发现(10000)2−1=(1111)2
而位运算的优先级又是很低的,所以代码里就需要写成i<=(1<<n)-1
转移过程则是先从小到大地枚举队伍状态i(一个二进制数),再不断地向队尾添加元素。
枚举队尾j,再枚举一个可以和队尾相接的元素p,就可以得到转移方程:
f[p][i|(1<<p-1)] += f[j][i]
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
int a[20],n,k;
long long f[20][1<<16],ans;
int main() {
cin >> n >> k;
for(int i=1; i<=n; i++)
cin >> a[i];
for(int i=1; i<=n; i++)
f[i][1<<(i-1)] = 1;
for(int i=1; i<=(1<<n)-1; i++)
for(int j=1; j<=n; j++)
if(i&(1<<j-1))
for(int p=1; p<=n; p++)
if(!(i&(1<<p-1)) && abs(a[p]-a[j]) > k) //转移
f[p][i|(1<<p-1)] += f[j][i];
for(int i=1; i<=n; i++)
ans += f[i][(1<<n)-1];
cout << ans << endl;
return 0;
}

本文详细介绍了一种使用二进制存储状态并运用位运算实现状态转移的算法——状态压缩DP(状压DP)。通过实例讲解了如何利用二进制数表示状态,如何进行状态转移以及如何求解最终状态的方法。
1967

被折叠的 条评论
为什么被折叠?



