[Usaco2008 Nov]mixup2 混乱的奶牛
题目
Description
混乱的奶牛 [Don Piele, 2007] Farmer John的N(4 <= N <= 16)头奶牛中的每一头都有一个唯一的编号S_i (1 <= S_i <= 25,000). 奶牛为她们的编号感到骄傲, 所以每一头奶牛都把她的编号刻在一个金牌上, 并且把金牌挂在她们宽大的脖子上. 奶牛们对在挤奶的时候被排成一支"混乱"的队伍非常反感. 如果一个队伍里所以任意两头相邻的奶牛的编号相差超过K (1 <= K <= 3400), 它就被称为是混乱的. 比如说,当N = 6, K = 1时, 1, 3, 5, 2, 6, 4 就是一支"混乱"的队伍, 但是 1, 3, 6, 5, 2, 4 不是(因为5和6只相差1). 那么, 有多少种能够使奶牛排成"混乱"的队伍的方案呢?
Input
- 第 1 行: 用空格隔开的两个整数N和K
- 第 2..N+1 行: 第i+1行包含了一个用来表示第i头奶牛的编号的整数: S_i
Output
第 1 行: 只有一个整数, 表示有多少种能够使奶牛排成"混乱"的队伍的方案. 答案保证是 一个在64位范围内的整数.
Sample Input
4 1 3 4 2 1
Sample Output
2 //两种方法分别是: 3 1 4 2 2 4 1 3
思路
首先如果状压$dp$ 的用法不熟悉的读者,可以看看;
$状压dp入门 $
其实这一题和-传球游戏之最小总代价-简直一模一样;
这一题有一个限制条件,就是任意两个相邻的奶牛的编号相差超过$m$;
那么我们设$dp[i][k]$ 表示队伍里以$i$ 结尾状态为 $k$,有多少组成混乱队伍的方案;
那么初始状态就是 $dp[i][1<<(i-1)]=1$ ;
转移方程就是 $dp[i][k]+=dp[j][k \oplus (1<<(i-1))]$ ;
代码
#include<bits/stdc++.h> #define re register typedef long long ll; using namespace std; inline ll read() { ll a=0,f=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*f; } ll n,m; ll a[20]; ll dp[20][1<<20]; int main() { n=read(); m=read();//读入 for(re ll i=1;i<=n;i++) a[i]=read();//读入 for(re ll i=1;i<=n;i++) dp[i][1<<(i-1)]=1;//初始化 for(re ll k=1;k<=(1<<n)-1;k++)//枚举状态 for(re ll i=1;i<=n;i++)//枚举结尾的奶牛 { if(!(k&(1<<(i-1))))//因为转移是dp[i][k] 以 i 结尾,所以集合也要包涵 i continue; for(re ll j=1;j<=n;j++) { if(i==j)//不能从同一个奶牛转移过来 continue; if(!(k&(1<<(j-1))))//从 j 转移过来 集合要包涵 j continue; if(abs(a[i]-a[j])>m)//相邻编号相差超过 m,才为混乱的队伍 dp[i][k]+=dp[j][k^(1<<(i-1))];//转移方程 // cout<<i<<" "<<k<<" "<<j<<" "<<dp[i][k]<<endl; } } ll ans=0; for(re ll i=1;i<=n;i++) ans+=dp[i][(1<<n)-1];//统计以每个奶牛结尾状态的方案数 printf("%lld\n",ans);//输出 //return 0; }