【交换】
【题目描述】
给定一个{0, 1, 2, 3, … , n - 1}的排列 p。一个{0, 1, 2 , … , n - 2}的排列q被认为是优美的排列,当且仅当q满足下列条件:
对排列s = {0, 1, 2, 3, ..., n - 1}进行n – 1次交换。
1. 交换s[q0],s[q0 + 1]
2. 交换s[q1],s[q1 + 1]
…
最后能使得排列s = p.
问有多少个优美的排列,答案对10^9+7取模。
【输入格式】
第一行一个正整数n.
第二行n个整数代表排列p.
【输出格式】
仅一行表示答案。
【样例输入】
3
1 2 0
【样例输出】
1
【样例解释】
q = {0,1} {0,1,2} ->{1,0,2} -> {1, 2, 0}
q = {1,0} {0,1,2} ->{0,2,1} -> {2, 0, 1}
【数据范围】
30%: n <= 10
100%: n <= 50
题解:
①顺着思考很麻烦,逆向考虑:最后一次交换发生的位置
②倒过来后,记忆化搜索,枚举当前状态的交换发生在哪个位置,然后区间一分为二处理子问题
③注意事项:交换可以发生的位置是左边和右边两部分都含有其应该有的数(顺序乱也可以)
④转移方程式:f[l][r]+=DFS(l,i)*DFS(i+1,r)*C[r-l-1][i-l]
表示当前区间为[l,r],交换i,i+1,由于两边独立决策,所以存在一个组合关系,可以转化为有2n个空位放n个数的方案数,这样是因为每一边内部是有序的(这是一个重要结论)。
f[l][r]+=(((DFS(l,i)*DFS(i+1,r))%M*C[r-l-1][i-l])%M))%#include<stdio.h> #define ll long long #define M 1000000007 #define S(x,y) (x^=y^=x^=y) #define go(i,a,b) for(int i=a;i<=b;i++) const int N=55;int n,a[N],can;ll C[N][N],f[N][N]; ll DFS(int l,int r) { if(l==r)return f[l][r]=1; if(f[l][r]>-1)return f[l][r];f[l][r]=0; go(i,l,r-1) { S(a[i],a[i+1]);can=1; if(can)go(j,l,i)if(a[j]<l||a[j]>i){can=0;break;} if(can)go(j,i+1,r)if(a[j]<i+1||a[j]>r){can=0;break;} if(can)(f[l][r]+=(((DFS(l,i)*DFS(i+1,r))%M*C[r-l-1][i-l])%M))%=M; S(a[i],a[i+1]); } return f[l][r]; } int main() { scanf("%d",&n); go(i,1,n){scanf("%d",a+i),a[i]++;go(j,1,n)f[i][j]=-1;} go(i,0,50){C[i][0]=1;go(j,1,i)C[i][j]=(C[i-1][j]+C[i-1][j-1])%M;} printf("%I64d\n",(DFS(1,n)%M+M)%M); return 0; }//Paul_Guderian
Life's a little bit messy. We all make mistakes.
No matter what type of animal you are, change starts with you.————Judy·Hopps