标准的状压题!
所谓状态压缩就是将状态用二进制存储下来,并用位运算实现转移
用二进制存储,可以不重不漏地遍历到所有的状态
我们设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;
}