【动态规划】P1521 求逆序对

288 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情

求逆序对

题目描述

我们说(i,j)是a1,a2,…,aN的一个逆序对当且仅当i<j且ai>a j。例如2,4,1,3,5的逆序对有3个,分别为(1,3),(2,3),(2,4)。现在已知N和K,求1…N的所有特定排列,这些排列的逆序对的数量恰好为K。输出这些特定排列的数量。

例如N=5,K=3的时候,满足条件的排列有15个,它们是:

1,2,5,4,3    
1,3,4,5,2   
1,3,5,2,4   
1,4,2,5,3   
1,4,3,2,5   
1,5,2,3,4   
2,1,4,5,3   
2,1,5,3,4   
2,3,1,5,4   
2,3,4,1,5
2,4,1,3,5    
3,1,2,5,4   
3,1,4,2,5   
3,2,1,4,5   
4,1,2,3,5

输入格式

输入第一行有两个整数N和K。(N≤100,K≤N*(N-1)/2)

输出格式

将1…N的逆序对数量为K的特定排列的数量输出。为了避免高精度计算,请将结果mod 10000以后再输出!

样例 #1

样例输入 #1

5 3

样例输出 #1

15

解题思路

dp 方程很容易得出:f[i][j]=f[i-1][j-i+1]+f[i-1][j-i+2]+……+f[i-1][j]

时间复杂度:O(n^3)

但可以优化为O(n^2)

因为f[i][j]=f[i-1][j-i+1]+f[i-1][j-i+2]+……+f[i-1][j]

f[i][j-1]=f[i-1][j-i]+f[i-1][j-i+1]+f[i-][j-i+2]+……+f[i-1][j-1]

这两个式子有许多重复项

所以可以合并为f[i][j]=f[i][j-1]+f[i-1][j]-f[i-1][j-i]

#include<bits/stdc++.h>
using namespace std;
int n,k,dp[105],f[105][5000];
int main(){
    cin>>n>>k;
	f[0][0]=f[1][0]=1;
    for(int i=2;i<=n;i++){
        dp[i]=dp[i-1]+i-1;
        for(int j=0;j<=dp[i];j++){
            f[i-1][j]%=10000;
            if(j<=dp[i]/2){
                f[i][j]=f[i-1][j]+f[i][j-1];
                if(j>=i)f[i][j]-=f[i-1][j-i];
            }
            else f[i][j]=f[i][dp[i]-j];
        }
    }
    cout<<f[n][k]%10000;
    return 0;
}