题目
问有多少个1∼n1\sim n1∼n的排列对于∀i\forall i∀i在1<i<n1<i<n1<i<n的情况下都满足a[i−1]<a[i]>a[i+1]a[i-1]<a[i]>a[i+1]a[i−1]<a[i]>a[i+1]或a[i−1]>a[i]<a[i+1]a[i-1]>a[i]<a[i+1]a[i−1]>a[i]<a[i+1]
分析
思维好题
首先考虑性质,
- 对于不相邻的两个数j,j+1j,j+1j,j+1,可以互换
- 合法的排列翻转依旧合法
- 把合法排列每个数jjj都变成n−j+1n-j+1n−j+1,排列依旧合法
考虑dp,设dp[i][j]dp[i][j]dp[i][j]表示选择1∼i1\sim i1∼i,第一个数为jjj,且第一个数大于第二个数的方案,所以最后答案要乘2
因为jjj和j−1j-1j−1在不相邻时可以互换,所以dp[i][j]dp[i][j]dp[i][j]可以由dp[i][j−1]dp[i][j-1]dp[i][j−1]转移而来,接着如果jjj和j−1j-1j−1相邻,那么也就转换成选择1∼i−11\sim i-11∼i−1,第一个数为j−1j-1j−1,且第一个数小于第二个数的方案,但是这和定义不符,考虑转换成选择1∼i−11\sim i-11∼i−1,第一个数为i−j+1i-j+1i−j+1,且第一个数大于第二个数的方案,因为如果进行第三条性质,那么原来大的反而小,初始化dp[2][2]=1dp[2][2]=1dp[2][2]=1
综上所述,dp[i][j]=dp[i][j−1]+dp[i−1][i−j+1]dp[i][j]=dp[i][j-1]+dp[i-1][i-j+1]dp[i][j]=dp[i][j−1]+dp[i−1][i−j+1]
代码
#include <cstdio>
#define rr register
using namespace std;
int n,mod,dp[2][4211],ans;
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
signed main(){
scanf("%d%d",&n,&mod),dp[0][2]=1;
for (rr int i=3;i<=n;++i)
for (rr int j=2;j<=i;++j)
dp[i&1][j]=mo(dp[i&1][j-1],dp[(i&1)^1][i-j+1]);
for (rr int i=2;i<=n;++i) ans=mo(ans,dp[n&1][i]);
return !printf("%d",mo(ans,ans));
}
本文深入探讨了一种特定排列计数问题的算法解决方案,通过分析排列的性质,提出了基于动态规划的方法来计算满足特定条件的排列数量。文章详细解释了算法的思路,包括如何利用性质进行状态转移,以及具体的实现代码。
8637

被折叠的 条评论
为什么被折叠?



