2019-10-30 xiaoh
题意
有n头牛(4<=n<=16),第i头牛的身高为si。求满足任意相邻的两头牛身高差都大于给定常数k(1<=k<=3400)的排列的数量
思路
瞄到的第一眼注意到了数据。4<=n<=16!!!这数据有点小的离谱了吧!很明显,只有两种可能:一是爆搜,而是状压
心中默默一算,暴力n!差不多在1e13多一点,就算剪枝也没什么卵用。所以显然是记搜或状压(其实是一个玩意)QAQ 再算算复杂度,O(nn * 2^n),大概是81e6左右吧。正好!所以基本状压实锤了。
既然是状压,那算法也就很明显了吧。定义dp[i][j],i为是否取每一头牛的状态压缩,j为当前最后一头牛的编号。那么我们只要先i从1取到2^n-1,每次j从1跑到n表示当前的最后一头牛的编号,先判断是否合法,若合法的话,在把l从1跑到n,表示倒数第二头牛的编号(即枚举前一种情况的所有可能解),如果l合法且j,l的身高差大于k的话,就把当前的答案加上前一种情况的解的数量就好啦QAQ
(热烈庆祝状压编码时间第一次<30min,并且一次过)
Code
#include<bits/stdc++.h>
using namespace std;
inline int read()//快读
{
int ret=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret;
}
int n,k;
int s[20];
long long dp[1<<20][20];
long long ans=0;
int main()
{
n=read(),k=read();
for(int i=1;i<=n;i++) s[i]=read();
for(int i=1;i<=n;i++) dp[1<<(i-1)][i]=1;//初始化
for(int i=1;i<(1<<n);i++)
{
if(!(i&(i-1))) continue;
for(int j=1;j<=n;j++)//遍历前一种情况并计算
if(i&(1<<(j-1)))
for(int l=1;l<=n;l++)
if(i&(1<<(l-1))&&abs(s[j]-s[l])>k)
dp[i][j]+=dp[i^(1<<(j-1))][l];
}
for(int i=1;i<=n;i++) ans+=dp[(1<<n)-1][i];
printf("%lld\n",ans);
}