51Nod 排列与交换 —— DP
Posted zinn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51Nod 排列与交换 —— DP相关的知识,希望对你有一定的参考价值。
题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1250
看了半天...
把第一问想成逆序对的话似乎很容易想了,新加入一个数,可以往前挪动,增加的逆序对数就是它后面那些数的个数;
所以 f[i][j] = ∑(k = max( 0 , j - i + 1)) f[i-1][k],用前缀和即可;
第二问正好用第一类斯特林数;
第一类斯特林数 str[i][j] 表示把 i 个数分成 j 个环,环有顺序的方案数,str[i][j] = str[i-1][j-1] (自成一环) + ( i - 1 ) * str[i-1][j] (跟到某个后面)
对应到这道题,原来每个数自己是一个环,表示它就在自己的位置上;
一个有顺序的环,按一个顺序往过数,一个数到下一个数相互交换,就对应了一种交换;
str[i][j] 中,i - j 个数与别的环合并,也就是发生了一次交换,所以答案就是 ∑(n - k <= i <= n ) str[n][i]
注意不要 MLE ...
用滚动数组求斯特林数得注意一点,初值 str[0][0] 不要赋成1,然后后面 i == j 时也求出来即可,感性理解一下的话~
这题...好像也不难啊。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const maxn=3005,mod=1000000007; int n,m; ll f[5][maxn],s[5][maxn],str[5][maxn],ans1,ans2; void init() { // for(int i=0;i<=1;i++)str[i][i]=1; str[1][1]=1;//滚动数组!让初始化符合意义 for(int i=2;i<=n;i++) for(int j=1;j<=i;j++)//因为滚动所以 str[i][i]也在这里求 { int p=(i&1),q=!p; str[p][j]=(str[q][j-1]+(i-1)*str[q][j]%mod)%mod; } } int main() { scanf("%d%d",&n,&m); init(); for(int i=0;i<=1;i++) { f[i][0]=1,s[i][0]=1; for(int j=1;j<=m;j++)s[i][j]+=s[i][j-1]; } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int p=(i&1),q=!p; if(j-i+1<=0)f[p][j]=s[q][j]; else f[p][j]=(s[q][j]-s[q][j-i]+mod)%mod; if(j)s[p][j]=(s[p][j-1]+f[p][j])%mod; else s[p][j]=f[p][j]; } int tmp=m; while(tmp>=0)ans1=(ans1+f[n&1][tmp])%mod,tmp-=2; for(int i=n-m;i<=n;i++)ans2=(ans2+str[n&1][i])%mod; printf("%lld %lld ",ans1,ans2); return 0; }
以上是关于51Nod 排列与交换 —— DP的主要内容,如果未能解决你的问题,请参考以下文章