CCPC-Wannafly Winter Camp Day4 Div1 - 置置置换 - [DP]

题目链接:https://zhixincode.com/contest/18/problem/G?problem_id=265

题目描述

wls有一个整数 $n$,他想请你算一下有多少 $1...n$ 的排列(permutation)满足:对于所有的 $i(2 \le i \le n)$,若 $i$ 为奇数,则 $a[i−1]<a[i]$,否则 $a[i−1]>a[i]$。请输出答案 $\mod 1e9 + 7$。

输入描述

一行一个整数 $n$。

$1≤n≤1000$

输出描述

一行一个整数表示答案。

样例输入 1

3
样例输出 1

2

题解:

这个数大概叫 up/down number ?参见OEIS:https://oeis.org/A000111

$dp[i][j]$ 表示前 $i$ 个满足字符串条件的结尾为 $j$ 的 $i$ 的排列,这里需要注意的是 $1 \sim i$ 的排列。

那么,如果第 $i$ 位是奇数位(山峰),$dp[i][j] = dp[i-1][1] + dp[i-1][2] + \cdots + dp[i-1][j-1]$。这个是很明显的。

那么,如果第 $i$ 位是偶数位(山谷),$dp[i][j] = dp[i-1][j] + dp[i-1][j+1] + \cdots + dp[i-1][i-1]$。因为要令当前位为 $j$,那么如果前面出现过大于等于 $j$ 的数字,就令这些数字全部加上 $1$,就能构造出排列。

这样的话,枚举 $i$,然后枚举 $j$,对于每个 $dp[i][j]$ 还需要 $O(n)$ 计算,这样一来就是 $O(n^3)$ 的时间复杂度,用前缀和可以优化到 $O(n^2)$。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1005;
const int mod=1e9+7;

int n;
ll dp[maxn][maxn],sum[maxn][maxn];

int main()
{
    cin>>n;
    sum[1][1]=dp[1][1]=1;
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            if(i%2) //奇数
            {
                dp[i][j]=sum[i-1][j-1];
                sum[i][j]=sum[i][j-1]+dp[i][j], sum[i][j]%=mod;
            }
            else //偶数
            {
                dp[i][j]=(sum[i-1][i-1]-sum[i-1][j-1]+mod)%mod;
                sum[i][j]=sum[i][j-1]+dp[i][j], sum[i][j]%=mod;
            }
        }
    }
    cout<<sum[n][n]<<endl;
}

猜你喜欢

转载自www.cnblogs.com/dilthey/p/10406433.html