思路:
状态压缩DP。
f[i][j]:i的二进制表示已经排过的牛,j表示最新的被加进来的牛。对于选择接下要加进来的牛u,只需要考虑他和j的关系(只和相邻的有关),及只要u和j的距离<=k,就可以将u加入i中。此时,i就被扩充为 i|(1<<u) 。考虑多个u后,可得状态转移方程 f[i|(1<<u)][u]+=f[i][j] (u不属于i) 。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <deque>
#include <set>
#include <cstring>
#include <map>
using namespace std;
int n,k;
int a[20]= {0};
long long f[70000][20]= {0};
int main() {
scanf("%d%d",&n,&k);
for(int i=0; i<n; i++) {
int x;
scanf("%d",&x);
a[i]=x;
}
for(int i=0;i<n;i++){
f[1<<i][i]=1;
}
int m=(1<<n)-1;
for(int i=0; i<=m; i++) { //状态
for(int j=0; j<n; j++) {
if(!(i&(1<<j))) continue;
for(int u=0;u<n;u++){
if(i&(1<<u)||abs(a[u]-a[j])<=k) continue; //u已经在i中或距离小于k
f[i|(1<<u)][u]+=f[i][j];
}
}
}
long long ans=0;
for(int i=0;i<n;i++){
ans+=f[m][i];
}
printf("%lld",ans);
return 0;
}