[USACO08NOV]奶牛混合起来Mixed Up Cows——[状压DP]

【题目描述】
约翰家有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;
}

猜你喜欢

转载自blog.csdn.net/Mega_Aspirin/article/details/81486930