冒泡排序 题解

首先一个事情就是:
每个位置必须交换一次并且只能交换一次
从这个结论里就有几个个特判:
1.应当满足n-1次交换
找逆序对
如果逆序对个数不等于n-1就直接puts(0)
一次冒泡减少一个逆序对,所以就是这样
由于\(n\leqslant 5000\)所以直接冒泡逆序对就行了
2.每个位置最多交换一次:
就是一个位置不能换两次,这个也是个小特判
这个用bool维护就可以搞一下
3.每个位置必须交换一次
这个处理显然就是看a[i]==i
解释就是如果a[i]==i这个节点不用交换
违背了每个位置必须交换1次

这三个做法一过去就该想正解了
首先给序列就肯定不是白干的:
排除了一个叫组合数学的选项
于是计数问题只剩DP了

考虑DP的话这个就比较amazing了
就是以第\(i\)位为阶段(这个挺常规的)
然后就是拓扑序上的第\(b\)个为状态?
转移就是从第\(i-1\)转移:
如果移动方向向关系为右移:
\(f[i][j]=f[i-1][j-1]+f[i][j-1]\)
否则:
\(f[i][j]=f[i][j+1]+f[i-1][j]\)

然后移动方向关系的预处理:
首先就是不在原位置上的一个数\(a[i]\)
\(a[i]\)右边找到数\(i\)
然后就是让中间的移动关系都向右
顺便打上2特判的标记
最后让\(i-1\)打上左移

甚至可以滚动数组再压一位

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int o=5555,mod=1e9+7;
int n,ans,cnt;
int a[o],f[o],g[o];
char s[o];
bool u[o];
void in(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[i]++;
    }
}
void pre(){
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            if(a[i]>a[j]){
                cnt++;
            }
        }
    }
    if(cnt!=n-1){
        cout<<0;
        exit(0);
    }
    for(int i=1;i<=n;i++){
        if(a[i]^i){
            for(int j=i+1;j<=n;j++){
                if(a[j]==i){
                    for(int k=i;k<j;k++){
                        if(u[k]){
                            cout<<0;
                            exit(0);
                        }
                        u[k]=1;
                        s[k]='>';
                    }
                    if(i>1){
                        s[i-1]='<';
                        break;
                    }
                }
            }
        }
    }
}
void work(){
    n--;
    f[1]=1;
    for(int i=2;i<=n;i++){
        fill(g,g+i+1,0);
        if(s[i-1]=='<'){
            for(int j=1;j<=i;j++){
                g[j]=(g[j-1]+f[j-1])%=mod;
            }
        }
        else{
            for(int j=i;j>0;j--){
                g[j]=(g[j+1]+f[j])%=mod;
            }
        }
        swap(f,g);
    }
}
void out(){
    for(int i=1;i<=n;i++){
        ans=(ans+f[i])%mod;
    }
    cout<<ans;
}
int main(){
	freopen("mp.in","r",stdin);
	freopen("mp.out","w",stdout);
    in();
    pre();
    work();
    out();
    return 0;
}
posted @ 2022-06-06 21:09  2K22  阅读(84)  评论(1)    收藏  举报