[USACO08NOV]奶牛混合起来Mixed Up Cows(状态压缩DP)

题目描述

Each of Farmer John's N (4 <= N <= 16) cows has a unique serial number S_i (1 <= S_i <= 25,000). The cows are so proud of it that each one now wears her number in a gangsta manner engraved

in large letters on a gold plate hung around her ample bovine neck.

Gangsta cows are rebellious and line up to be milked in an order called 'Mixed Up'. A cow order is 'Mixed Up' if the sequence of serial numbers formed by their milking line is such that the serial

numbers of every pair of consecutive cows in line differs by more than K (1 <= K <= 3400). For example, if N = 6 and K = 1 then 1, 3, 5, 2, 6, 4 is a 'Mixed Up' lineup but 1, 3, 6, 5, 2, 4 is not (since

the consecutive numbers 5 and 6 differ by 1).

How many different ways can N cows be Mixed Up?

For your first 10 submissions, you will be provided with the results of running your program on a part of the actual test data.

POINTS: 200

约翰家有N头奶牛,第i头奶牛的编号是Si,每头奶牛的编号都是唯一的。这些奶牛最近 在闹脾气,为表达不满的情绪,她们在挤奶的时候一定要排成混乱的队伍。

在一只混乱的队 伍中,相邻奶牛的编号之差均超过K。比如当K = 1时,1, 3, 5, 2, 6, 4就是一支混乱的队伍, 而1, 3, 6, 5, 2, 4不是,因为6和5只差1。请数一数,有多少种队形是混乱的呢?

输入输出格式

输入格式:

 

  • Line 1: Two space-separated integers: N and K

  • Lines 2..N+1: Line i+1 contains a single integer that is the serial number of cow i: S_i

 

输出格式:

 

  • Line 1: A single integer that is the number of ways that N cows can be 'Mixed Up'. The answer is guaranteed to fit in a 64 bit integer.

 

输入输出样例

输入样例#1:
4 1 
3 
4 
2 
1 
输出样例#1:
2 

说明

The 2 possible Mixed Up arrangements are:

3 1 4 2

2 4 1 3

 

思路:

看到n的范围这么小就能猜到是状态压缩的动态规划。

设f[i][j]为j在二进制表示的那些牛中以第i只牛为尾合法的队形总数,则我们就可以得到动态转移方程f[i][j | (1 << p-1)]  += f[i][j] ,其中f[i][j | (1 << p-1)] 为状态j加上(1 << p-1)这只牛后的状态。其中 1 <= p <= n。

该动态转移方程必须符合以下条件:

状态j中没有包括第p只牛,且abs(s[p]-s[i]) > k。

所以,我们枚举每一个状态,并且在每一个状态中枚举第每一只牛,看这只牛是否在该状态中,如果在,我们则在此情况下进一步枚举,看那一只牛不在当前状态里,找到不在当前状态中的牛之后就进行累加,即为:

                     f[没在当前状态中的牛的输入顺序编号][当前状态加上不在状态中的牛后的新状态]  += f[当前状态的结尾牛输入顺序编号][当前状态];

初始化应该为f[i][1 << i] = 1, 因为,当队列中只有一只牛时最后一只牛的标号就是它本身且此情况上的子问题答案是1。

 

另外,这题因为答案大,所以要使用longlong,这里用到了条件编译,使得在任何平台下,该程序将不受longlon影响。

下面贴代码,有问题留言。

 

#include<cstdio>
#define N 1 << 17
#define S 20
using namespace std;

long long  f[S][N];
int s[20];

#ifdef WIN32            //条件编译,省去longlong给程序带来的影响
#define LL "%I64d\n"
#else
#define LL "%lld\n"
#endif

int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i = 1; i <= n; i++)scanf("%d",&s[i]);
    int mxx = (1 << n)-1;
    for(int i = 1; i <= n; i++)f[i][1 << (i-1)] = 1;
    for(int i = 0; i <= mxx+1;i++){                //枚举每一个状态
        for(int j = 1; j <= n; j++){
            if(i & (1 << j-1)){                //第j只牛是否在状态i中
                for(int p = 1; p <= n; p++)          //进一步枚举没有在状态i中的牛
                  if(!(i & (1 << p-1)) && (s[j]-s[p] > k || s[p]-s[j] > k)){   //如果k不在队列中且差值大于k 
                        f[p][i | (1 << p-1)] += f[j][i];
                  }
            }
        }
    }
    long long  ans = 0;
    for(int i = 1; i <= n; i++)ans += f[i][mxx];
    printf(LL,ans);
    return 0;
}

 

posted @ 2017-10-20 09:41  君焰w  阅读(318)  评论(0编辑  收藏  举报