题目
复制过来会乱码,题目是截图截过来的,可能会不清楚,可以通过下面的连接去看题目
链接:https://www.nowcoder.com/questionTerminal/8fe007e54fc04b5e82089aaa71ba3553
来源:牛客网
解题思路
这道题我一开始只会用暴力法写,也就是按照题目意思翻转一次计算一次逆序对,时间复杂度O(MNN),M是翻转次数,N是数据规模,只能通过50%的测试用例。
然后看了一下解答区的答案,需要用到归并排序的思想,我不懂,然后去学了一下,可以看Leetcode 912. 排序数组
这是只用归并排序就能解决的题目,也是我学习归并排序的过程。
这道题的思路是参照的这一篇:腾讯笔试题——逆序对
我学习了三天,其实也不是特别懂这个思想。
- 首先是利用归并计算出来的是将数组切割成不同数据长度所产生的逆序对。
- 然后将数组反转,求逆序对,也就是与第1步相反的求的不同数据长度的顺序对。
- 最后是根据划分的翻转长度,将小于等于翻转长度的逆序对和顺序对交换,交换完成后的逆序对数组的数据累积起来就是结果。
我目前还没搞懂的就是为什么2^n的数据量,为什么会产生n+1种切片方式?
难道是因为只考虑2的次方的切片方式?好像也是,后面翻转的q也是2的q次方的翻转。
后面有机会再自己测试一下好了。
代码(C++)
暴力法
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
int main(){
//输入n和n序列
int n=0;
cin>>n;
int len=pow(2,n);
vector<int> vec(len);
for(int i=0;i<len;++i){
cin>>vec[i];
}
//输入m和m序列
int m=0;
cin>>m;
vector<int> mvec(m);
for(int i=0;i<m;++i){
cin>>mvec[i];
}
//翻转数据
for(int i=0;i<m;++i){
auto ite=vec.begin();
int gap=pow(2,mvec[i]);
for(int j=0;j<len;j+=gap){
reverse(ite+j,ite+j+gap);
}
//计算逆序数对的数目
int ans=0;
for(int i=1;i<len;++i){
for(int j=0;j<i;++j){
if(vec[i]<vec[j]) ans++;
}
}
cout<<ans<<endl;
}
return 0;
}
归并排序
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
void merge(vector<ll>& vec,ll L,ll R,vector<ll>& cnt,ll index){
ll mid=L+(R-L)/2;
ll p1=L;
ll p2=mid+1;
vector<ll> temp(R-L+1);
ll i=0;
ll pairCnt=0;
while(p1<=mid&&p2<=R){
if(vec[p1]<=vec[p2]){
temp[i++]=vec[p1++];
}
else{
pairCnt += mid-p1+1; //左右都是有序的,有一个vec[p2]<vec[p1],
// 就说明p1右边的都比该p2位置的值大;
temp[i++]=vec[p2++];
}
}
while(p1<=mid){
temp[i++]=vec[p1++];
}
while(p2<=R){
temp[i++]=vec[p2++];
}
for(ll i=0;i<temp.size();++i){
vec[L+i]=temp[i];
}
cnt[index]+=pairCnt;
}
void mergeSort(vector<ll>& vec,ll L,ll R,vector<ll>& cnt,ll index){
if(L==R) return;
ll mid=L+(R-L)/2;
mergeSort(vec,L,mid,cnt,index-1);
mergeSort(vec,mid+1,R,cnt,index-1);
merge(vec,L,R,cnt,index);
}
int main(){
//输入n和n序列
int n=0;
cin>>n;
ll len=pow(2,n);
vector<ll> vec(len);
for(ll i=0;i<len;++i){
cin>>vec[i];
}
//输入m和m序列
//记录不同长度逆序对和顺序对
vector<ll> cnt(n+1,0);
vector<ll> reCnt(n+1,0);
vector<ll> reVec(vec.rbegin(),vec.rend());
mergeSort(vec,0,vec.size()-1,cnt,n);
mergeSort(reVec,0,reVec.size()-1,reCnt,n);
ll m=0;
cin>>m;
for(ll i=0;i<m;++i){
int q=0;
cin>>q;
ll ans=0;
for(int i=1;i<=q;++i){
swap(cnt[i],reCnt[i]);
}
for(int i=1;i<=n;++i){
ans+=cnt[i];
}
cout<<ans<<endl;
}
return 0;
}