题意:给定n , m,求解由 1-n 组成的 字典序最小的 逆序数对为m的 数列
方法一: 暴力递归求解
#include<iostream> #include<algorithm> using namespace std; int n,m; int a[(int)5e4+5]; void dfs(int l,int r,int ans) { if(l==r){ cout<<l<<' '; return ; } if(l>=r)return ; if(a[r-l+1]>=ans) { cout<<l<<' '; dfs(l+1,r,ans); } else { for(int j=l+1;j<=r;j++) if(a[j-l+1]+a[r-j+1]+j-l>=ans) { ans-=(j-l); dfs(l+1,j,ans-=min(ans,a[r-j+1])); cout<<l<<' '; dfs(j+1,r,min(ans,a[r-j+1])); break; } } } int main () { a[1]=0,a[2]=0; for(int i=3;i<=5e4;i++) a[i]=a[i-1]+i-2; cin>>n>>m; dfs(1,n,m); return 0; }
时间复杂度较高;如果n-1位数字构成的最大逆序数目大于m就将1直接放入 第一位 ,依次进行递归
如果将此为是数字按顺序放入后剩余的数字构成的逆序数对不能大于m则将此数往后边的位置进行枚举,找到最近的满足此条件的位置然后分为两段进行递归
第二种方法:
#include<iostream> using namespace std; typedef long long ll; int a[(int )1e5+5]; int main () { ll n,m; cin>>n>>m; ll l=1,r=n; for(int i=1;i<=n;i++) { int t=(n-i)*(n-i-1)/2; if(t>=m) a[l++]=i; else a[r--]=i,m-=(r-l+1); } for(int i=1;i<=n;i++) cout<<a[i]<<' '; return 0; }
通过手动模拟第一种解法:从最小值进行排序的时候,要不就按照顺序放要不就倒着放。
另一种证明方法: 所能构成的逆序数对越少,字典序越小,所以放在最后一位,会使前边的数字 字典序最小。