【题目描述】
约翰家有N(n<=16)头奶牛,第i头奶牛的编号是Si,每头奶牛的编号都是唯一的。这些奶牛最近 在闹脾气,为表达不满的情绪,她们在挤奶的时候一定要排成混乱的队伍。在一只混乱的队 伍中,相邻奶牛的编号之差均超过K。比如当K = 1时,1, 3, 5, 2, 6, 4就是一支混乱的队伍, 而1, 3, 6, 5, 2, 4不是,因为6和5只差1。请数一数,有多少种队形是混乱的呢?
【输入格式】
第1行:两个数n,K
第2~n+1行:每只奶牛的编号Si。
【输出格式】
一行,为混乱的队形个数。
【样例输入】
4 1
3
4
2
1
【样例输出】
2
【题意分析】
参考:(https://www.cnblogs.com/Ronald-MOK1426/p/8456945.html)
对状压DP的描述
以及:(http://www.cnblogs.com/bingdada/p/7697887.html) 的代码
花了好长时间终于看懂了
何谓状压DP?就是把一堆复杂的状态用二进制位存储信息的形式描述了,例如1表示选,0表示不选,然后这一堆状态会各自以一个十进制数来表示(通常状态的上限转化成十进制数要比较小,一般(1 << 20)左右),以达到简化的目的。
因为是二进制数,所以涉及到很多抽象的位运算:<<,~,&,|,另外这几个位运算互相组合还能代表更多的意义,因此建议看看第一个链接。
现在来看本题:将一些编号排列,认为相邻两个元素绝对值之差都小于一个给定值的排列是合法排列。
n<=16,想到状压dp,将牛状态压缩。dp[i][j]表示状态为j(二进制)且以编号为i的奶牛结尾时的答案总数
Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll; //答案很大,要开long long
int a[20],n,delta;
ll dp[20][70000];
int main(){
scanf("%d%d",&n,&delta);
for (register int i=1;i<=n;i++)
scanf("%d",&a[i]);
int INF=1 << n;
for (register int i=1;i<=n;i++)
dp[i][1 << (i-1)]=1;
//初始化,只有一头牛且这头牛是它自己
for (register int i=1;i<INF;i++)
//一个一个状态枚举
for (register int j=1;j<=n;j++)
//一头一头牛枚举
if (1 << (j-1)&i) //如果不在状态中
for (register int k=1;k<=n;k++)
//再枚举差值对象
if ((abs(a[j]-a[k])>delta)&&(!((1 << (k-1))&i)))
//如果相差符合条件且不在状态中
dp[k][(1 << (k-1))|i]+=dp[j][i];//转移
ll ans=0;
for (register int i=1;i<=n;i++)
ans+=dp[i][INF-1]; //统计结果,开long long
printf("%lld",ans);
return 0;
}