AT_dp_t Permutation 题解
AT_dp_t Permutation
解析
真的是一道很好的dp。
首先,题目只要求我们排列中数字的大小关系,不要求数字具体是多少,所以这里可以有个类似离散化的思想。
比如:
4 7 9 1 2
就可以看成
3 4 5 1 2
现在数字是几不关心了,我们需要的是某一时刻某一位上数字在排列中的大小。这样我们能想到记录第 \(i\) 位在 \(1\) 到 \(i\) 中排第几大。
设计 \(dp_{i,j}\) 为在第 \(i\) 个位置,这个数是当前已经排好的数中第 \(j\) 大。
\( dp_{i,j} = \begin{cases} \sum_{k=1}^{j-1}dp_{i-1,k} & s_{i-2}='<'\\ \sum_{k=j}^{i-1}dp_{i-1,k} & s_{i-2}='>'\\ \end{cases}\)
这里补充一点:
Q:当 \(s_{i-2}='>'\) 时,为什么求和是从 \(j\) 开始?
A:还未插入 \(a_i\) 时,\(a_{i-1}\) 是第 \(j\) 大;插入 \(a_i\) 后,\(a_i\) 是第 \(j\) 大了,说明 \(a_i>a_{i-1}\)。
现在dp转移复杂度是 \(O(n)\),用前缀和 \(sum\) 数组优化一下。
\(sum_{i,j}=sum_{i,j-1}+dp_{i,j}\)
最后的转移为:
\( dp_{i,j} = \begin{cases} sum_{i-1,j-1} & s_{i-2}='<'\\ sum_{i-1,i-1}-sum_{i-1,j-1} & s_{i-2}='>'\\ \end{cases}\)
答案记得取模。
AC Code
老师自己也说不清为什么用滚动数组,不过挺简洁的。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3005,mod=1e9+7;
string s;
int n,dp[3][N],sum[3][N];
signed main() {
ios::sync_with_stdio(0), cin.tie(0);
cin>>n>>s;
dp[1][1]=sum[1][1]=1;
for(int i=2;i<=n;i++){
int nw=i%2,nxt=nw^1;
for(int j=1;j<=i;j++){
int& d=dp[nw][j];
d=(s[i-2]=='>')?(sum[nxt][i-1]-sum[nxt][j-1]+mod)%mod
:sum[nxt][j-1];
(sum[nw][j]=sum[nw][j-1]+dp[nw][j])%=mod;
}
}
cout<<sum[n%2][n];
return 0;
}

浙公网安备 33010602011771号