洛谷P2915 [USACO08NOV]奶牛混合起来Mixed Up Cows【状压DP】

时空限制 1000ms / 128MB

题目描述

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.

题目分析

d p [ s ] [ j ] dp[s][j] dp[s][j]表示 当前已经有哪些牛排入了队列(二进制状态S表示),且当前队尾是编号为 j j j的牛的方案数

直接记搜即可

lt DP(lt s,lt j)
{
    if(s==(1<<n)-1) return 1;//状态S全为1,表示已经排完
    if(dp[s][j]) return dp[s][j];
    lt res=0;
    for(int i=1;i<=n;++i)//枚举下一位牛的编号
    if( abs(a[i]-a[j])>k && (s&(1<<i-1))==0 )//(s&(1<<i-1))==0判断这个编号是否已排入队
    res+=DP(s|(1<<i-1),i);
    return dp[s][j]=res;
}

完整代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long lt;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=80010;
lt n,k;
lt a[20],ans;
lt dp[maxn][20];

lt DP(lt s,lt j)
{
    if(s==(1<<n)-1) return 1;
    if(dp[s][j]) return dp[s][j];
    lt res=0;
    for(int i=1;i<=n;++i)
    if( abs(a[i]-a[j])>k && (s&(1<<i-1))==0 )
    res+=DP(s|(1<<i-1),i);
    return dp[s][j]=res;
}

int main()
{
    n=read();k=read();
    for(int i=1;i<=n;++i) a[i]=read();
    
    for(int i=1;i<=n;++i)
    ans+=DP(1<<i-1,i);
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值