zoukankan      html  css  js  c++  java
  • 逆序对数列(DP+前缀和)

    又是DP又是前缀和,小白做题太慢了/(ㄒoㄒ)/~~
    洛谷P2513,Acwing2692

    题目

    对于一个数列 {ai},如果有 i<j 且 ai>aj,那么我们称 ai 与 aj 为一对逆序对数。
    若对于任意一个由 1∼n 自然数组成的数列,可以很容易求出有多少个逆序对数。
    那么逆序对数为 k 的这样自然数数列到底有多少个?

    输入输出

    输入:共一行,两个整数 n,k。
    输出:输出一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对 10000 求余数后的结果。

    思路

    求1-n的全排列中逆序对数为k的排序,可用动态规划来做。
    动态规划数组:dp[i][j](0 <= j <= k)为1-i个数的全排列中逆序对数为j的方案数。
    可以由1-(i-1)得出的结果来推断 1-i的结果
    将i插入1-(i-1)的排列中,会增加逆序对
    动态规划的初始化结果为dp[i][0] = 1
    考虑i放置的位置,放i-1个位置都能增加逆序对数量
    当i插在1-(i-1)的排列最后一个位置时,没有增加逆序对数,dp[i][j] += dp[i-1][j]
    当i插在1-(i-1)的排列倒数第二个位置时,增加了一个逆序对数,dp[i][j] += dp[i-1][j-1]
    ......
    当i插在1-(i-1)的排列倒数第m个位置时,增加了m-1个逆序对数,dp[i][j] += dp[i-1][j-m+1]
    ......
    当i插在1-(i-1)的排列倒数第j+1个位置时,增加了j个逆序对数,dp[i][j] += dp[i-1][0]
    注意:i最多能在1-(i-1)的序列上贡献i-1个逆序对
    当j>=i-1时,dp[i][j]不能加上 dp[i-1][0-(j-i+1]这一段
    时间复杂度:不经任何优化的时间复杂度是O(n^3),会超时

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1010;
    int dp[N][N];
    
    int main()
    {
        int n,k;
        cin >> n >> k;
        for (int i = 1; i <= n; i ++ ) dp[i][0] = 1;
        for (int i = 2; i <= n; i ++ ){ //枚举n个数
            for(int j = 1;j <= k;j ++){  //枚举逆序对的个数
                for(int m = max(0,j-i+1);m <=j;m++){
                    dp[i][j] = (dp[i][j] + dp[i-1][m])%10000;
                }
            }
        }
        cout << dp[n][k];
    }
    

    用前缀和优化

    上述算法时间复杂度:不经任何优化的时间复杂度是O(n^3),会超时
    用sum记录dp[i-1][j],前j个之和,并当j>=i-1时,sum减去dp[i-1][j-i+1],相当于上面那种算法,没有把dp[i-1][0-(j-i+1]这一段算进去。

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1010;
    int dp[N][N];
    
    int main()
    {
        int n,k;
        cin >> n >> k;
        for (int i = 1; i <= n; i ++ ) dp[i][0] = 1;
        for (int i = 2; i <= n; i ++ ){ //枚举n个数
            int sum = 0;
            for(int j = 0;j <= k;j ++){  //枚举逆序对的个数
                sum = (sum + dp[i-1][j]) % 10000;
                dp[i][j] = sum;
                if(j >= i - 1)
                    sum = (sum - dp[i-1][j-i+1] + 10000) % 10000;
            }
        }
        cout << dp[n][k];
    }
    
    作者:inss!w!
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 相关阅读:
    2020.11.9
    2020.11.6
    2020.11.5
    2020.11.2
    建站纪念
    退役记——CCC2020&CCO2020
    BZOJ2809&&LG1552 APIO2012派遣(线段树合并)
    BZOJ4668 冷战(LCT维护最小生成树)
    BZOJ3926&&lg3346 ZJOI诸神眷顾的幻想乡(广义后缀自动机)
    BZOJ4566&&lg3181 HAOI找相同字符(广义后缀自动机)
  • 原文地址:https://www.cnblogs.com/Hfolsvh/p/15034198.html
Copyright © 2011-2022 走看看