[Bzoj1925]&[SDOI2010]地精部落 DP

题目链接:Bzoj1925 .

—————————————-

概述

题目大意如下。

给定一个数 n ,问1 ~ n 的所有排列中有多少个排列满足要求:对于任意的i,都有 Ai1<Ai>Ai+1 或者 Ai1>Ai<Ai+1 (对于第一个数和最后一个数没有要求).答案对 p 取模。

其中,1n4200, p109.

—————————————-

分析

作为一个 DP 蒟蒻,这题折磨了我很久。

首先明确一点,这是一个计数问题,应该可以 DP (钦定算法2333)。那么接着的问题就是状态的选取了,这题的状态选取范围太广泛,不同的状态选取复杂度都不一样,我接下来讲一讲我在网上学习的做法,代码很短很巧妙。

定义 dp[i][j] 1 ~ i的排列, j 个数是不合要求的方案数,其中一段连续的不合要求的数必须递增。我下面将举例说明。

对于5 1 2 3 4这个序列, 3 4是不合要求的。因为不看 3 后面的数的话,3本应该比 2 小;不看4后面的数, 4 本应该比3小。所以这里不合法的数有 2 个。

对于5 1 2 4 3这个序列,只有 4 是不合要求的。因为不看4后面的数, 4 本应该比2小。所以这里不合法的数有 1 个。

对于4 5 3 2 1这个序列,我们不作考虑。因为它虽然有 3 2 1 这个不合法的序列,但是我们可以把它等效成 1 2 3 这个子序列,所以我们只需要考虑 4 5 1 2 3 这个序列,最后答案乘2就能把 4 5 3 2 1 的方案数统计进去了。

状态的含义讲完了,接下来是转移。假设当前序列不合法的数有 j 个,那么插入一个数得到的新序列的不合法数只可能是j1个、 j 个或j+1个。

那么我们考虑不合法数的改变情况,可以得到如下转移:

  1.  dp[i+1][j]=dp[i+1][j]+dp[i][j] ;

    我们将 i+1 插入到当前序列的末尾或者最后一个数前面,这两个位置中一定有且仅有一个不会改变新序列不合法数的个数,而且能使新序列不合法数的个数不变的位置只可能是这两个之一。

  2.  dp[i+1][j1]=dp[i+1][j1]+dp[i][j]j ;

    我们将 i+1 插入到当前序列不合法数的前面,可以使得新序列不合法数-1.

  3.  dp[i+1][j+1]=dp[i+1][j+1]+dp[i][j](ij) ;

    我们将 i+1 插入到当前序列剩下的位置,可以使新序列不合法数+1.

转移就是这样,最后答案乘2即可。

—————————————-

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define For(i, j, k) for(int i = j; i <= (int)k; ++ i)
#define Forr(i, j, k) for(int i = j; i >= (int)k; -- i)
#define INF 0x3f3f3f3f
using namespace std;

const int maxn = 4200 + 5;

int n;
ll mo;
ll dp[maxn][maxn];

int main(){
    scanf("%d%lld", &n, &mo);
    dp[1][0] = 1;
    For(i, 1, n)
        For(j, 0, i){
            (dp[i+1][j] += dp[i][j]) %= mo;
            (dp[i+1][j-1] += dp[i][j]*j) %= mo;
            (dp[i+1][j+1] += dp[i][j]*(i-j)%mo) %= mo;
        }

    printf("%lld", (dp[n][0]<<1) % mo);
    return 0;
}

—————————————-

小结

这题的方法其实还有很多,但是这一种比较简洁所以选择学一学,可能不太好理解吧。设好状态之后,解题的关键就是分位置讨论新的序列的不合法数,是道思维好题。

—————————————-

wrote by miraclejzd.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值