• 队测 逆序对 permut


    本人水平有限,题解不到为处,请多多谅解

    本蒟蒻谢谢大家观看

    题目:

                permut
    题目描述:
    求由 1 到 n 一共 n 个数字组成的所有排列中,逆序对个数为 k 的有多少个
    输入格式
    第一行为一个整数 T,为数据组数。
    以下 T 行,每行两个整数 n,k,意义如题目所述。
    输出格式
    对每组数据输出答案对 10000 取模后的结果
    Sample Input
    1
    4 1
    Sample Output
    3 
    数据范围及约定
    对于 30% 的数据,满足 n12
    对于所有数据,满足 n≤1000, k≤1000,T≤10
    测试时全程开启O2优化
     
    此题为逆序对:逆序对定义如下:
    设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。
    如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。
     
    首先看一下题面:由1~n组成的所有排列中,求逆序对个数等于k的排列数
    我首先想到的是用全排列,不断去枚举用dfs去爆搜,结果只拿了20分)解法如下:
    #include<bits/stdc++.h>
    #pragma GCC optimize(3)
    #define mod 10000
    using namespace std;
    int n,a[13],ans,cnt,t,k;
    int f[1001][20002];
    bool b[13];
    void inint(){
        freopen("permut.in","r",stdin);
        freopen("permut.out","w",stdout);
    }
    void dfs(int num){
        if(num==n+1){
            cnt=0;//cnt每次做完一次排列后要清零,从重新统计 
            for(int i=1;i<=n;i++){
                for(int j=i+1;j<=n;j++){
                    if(a[i]>a[j]&&j>i)cnt++;    
                }
                //cout<<a[i]<<" ";//"  cnt= "<<cnt<<" ";
            }
            //cout<<endl;
            if(cnt==k)
                ans++;
            //cout<<endl;
            //cout<<ans<<endl;
            return ;
        }
    //    cout<<ans<<endl;
        for(int i=1;i<=n;i++){
            if(b[i]==false){
                b[i]=true;
                a[num]=i;
                dfs(num+1);
                b[i]=0;
                a[num]=0;
            }
        }
        return ;
    }
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*f;
    }
    int main()
    {
        //inint();
        t=read();
        while(t--){
            ans=0;
            n=read(),k=read();
            dfs(1);
            printf("%d
    ",ans);
        } 
        return 0;
    }

    同理:ans一定要不断的重新清零。

    显然解法1会TLE,当n==10是其已经不能胜任在1s内跑完。

    这时,通过dfs我们可以输入几个数,发现可以使用DP(dfs在一定有规律时可以转化成DP)

    解法2横空出世:设f[i][j]表示1~i的逆序对数为j的排列方案数。次设法刚好符合题意,我们就可以直接输出f[n][k]即可

    在任意一个1到i-1排列中插入i可能产生0,1,2……i-1个逆序对。

       i-1  

    f[i][j]=∑f[i-1][j-k](j>=k)

       k=0

    因为有多组数据,所以枚举时n,k都要取max,来优化时间复杂度,其n,k都要用数组形式存储,防止数据更新。
    code:
    #include<bits/stdc++.h>
    #pragma GCC optimize(3)
    using namespace std;
    int f[5001][4951],n[11],k[11],t,maxn,maxk;
    inline int read(){
        int x=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){
            if(ch=='-')f=-1;
            ch=getchar();
        }
        while(ch<='9'&&ch>='0'){
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    int main()
    {
        t=read();
            for(int i=1;i<=t;i++){
            n[i]=read(),k[i]=read();
            maxn=max(maxn,n[i]);
            maxk=max(maxk,k[i]);
        }
            f[0][0]=1;
            f[1][0]=1;
            f[2][0]=1;
            f[2][1]=1;
            for(int i=3;i<=maxn;i++){
                for(int j=0;j<=maxk;j++){
                    for(int kk=0;kk<=i-1&&kk<=j;kk++){
                        f[i][j]=(f[i][j]+f[i-1][j-kk])%10000;
                    }
                }
            }
            for(int i=1;i<=t;i++)
            printf("%d
    ",f[n[i]][k[i]]);
    } 
     
     
  • 相关阅读:
    vue项目搭建
    轮播 删除中间的还是居中
    随内容增加,背景不设高度自适应,背景图不拉伸和变形
    緢点连接
    左侧背景,右侧数据,根据数据左侧背景自适应
    问题
    手机访问网站,点击手机号码直接拨打电话
    长度超出之后文字变成省略号
    css中vw,vh单位对于UC的兼容性问题
    线性渐变的兼容性写法
  • 原文地址:https://www.cnblogs.com/nlyzl/p/11352534.html
Copyright © 2020-2023  润新知