P2915 [USACO08NOV]Mixed Up Cows G 状压DP

约翰家有N头奶牛,第i头奶牛的编号是Si,每头奶牛的编号都是唯一的。这些奶牛最近 在闹脾气,为表达不满的情绪,她们在挤奶的时候一定要排成混乱的队伍。在一只混乱的队 伍中,相邻奶牛的编号之差均超过K。比如当K = 1时,1, 3, 5, 2, 6, 4就是一支混乱的队伍, 而1, 3, 6, 5, 2, 4不是,因为6和5只差1。请数一数,有多少种队形是混乱的呢?
(4<=N<=16,1<=si<=25000)

数据范围这么小很明显就算是状压DP(一般不大于20),
定义f[ i ] [ j ]表示状态为i时以j为末尾时的方案数(i二进制下第k位为1表示第k只牛在队列内,反之不在)。
首先将牛的编号离散化 排好序方便找,那么当状态s不包含第i只牛时,第j只牛与第i只牛编号之差大于k时,s再加上i的状态下以第i只牛为末尾的方案就等于状态s下以j为末尾的方案的和(所有满足的j)
所以状态转移方程:f [ s+1<<(i-1) ][ i ]+= f [ s ][ j ];

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=17;
int n,m,s[N];
ll f[1<<N][N];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",s+i);
	sort(s+1,s+n+1);
	for(int i=1;i<=n;i++) f[1<<(i-1)][i]=1;//初始化只有1个情况
	for(int j=1;j<(1<<(n));j++)//暴力枚举所有状态,所以状压dp也是个暴力算法
	{
		for(int k=1;k<=n;k++)//枚举末尾的牛
		{
			if(j&(1<<(k-1))) continue;
			int l=lower_bound(s+1,s+n+1,s[k]-m)-s;//第一个大于等于s[k]-m的位置,l前的牛都可以与k相邻,是s[k]不是k!!!
			int r=upper_bound(s+1,s+n+1,s[k]+m)-s;//第一个大于s[k]+m的牛,从r到n牛也都可以
			for(int o=1;o<l;o++)//不用判断j是否包含o,如果不包含f[j][o]一定等于0
					f[j+(1<<(k-1))][k]+=f[j][o];
			for(int o=r;o<=n;o++)
					f[j+(1<<(k-1))][k]+=f[j][o];
		}
	}
	ll ans=0;
	for(int i=1;i<=n;i++)//将所有满状态下的方案相加
		ans+=f[(1<<n)-1][i];
	printf("%lld",ans);
}
原文地址:https://www.cnblogs.com/neflibata/p/12871761.html