腾讯2020校园招聘----逆序对

腾讯2020校园招聘----逆序对

一、题目描述

在这里插入图片描述
在这里插入图片描述

二、题目分析

首先,我们看到要求逆序对,我们做出这道题的前提是会求逆序对,所以先分析怎么求逆序对;

答案:《剑指offer》36题

代码:

class Solution {
public:
    
    //简单的归并排序
    //最好在过程当中%一下,否则不过
    long long Merge(vector<int>& data,int begin,int end,vector<int>& temp)
    {
        if(begin == end)
        {
            return 0;
        }
        
        long long count = 0;
        
        int mid = (begin + end) >> 1;
        //把数组分为更小的左右两部分
        int left = Merge(data,begin,mid,temp)% 1000000007;
        int right = Merge(data,mid + 1,end,temp)% 1000000007;
        
        //进行标记
        int begin1 = begin;
        int end1 = mid;
        int begin2 = mid + 1;
        int end2 = end;
        
        int index = end;
        //每一块比较大小
        while(begin1 <= end1 && begin2 <= end2)
        {
            //如果前面区间的数字比后面区间的数字大,就构成逆序对
            //逆序对的个数为后面区间的begin到end(end是在动态改变的)位置处的元素的个数
            //原因是归并的特性,是先把小区间有序,在合并大区间有序,所以两个区间内部肯定是有序的
            //如果前面区间的某个数字m比后面区间的某个数字n大,那么m就比后面区间的某个
            //数字m之前的数都大
            if(data[end1] > data[end2])
            {
                count = (count + end2 - begin2 + 1)% 1000000007;
                temp[index] = data[end1];
                end1--;
                index--;
            }
            else 
            {
                temp[index] = data[end2];
                end2--;
                index--;
            }
        }
        while(end1 >= begin1)
        {
            temp[index--] = data[end1--];
        }
        while(end2 >= begin2)
        {
            temp[index--] = data[end2--];
        }
        //拷贝
        for(int i =  begin;i <= end ;i++)
        {
            data[i] = temp[i]; 
        }
        
        //左边区间的逆序对的个数 + 由边区间的逆序对的个数 + 合并两个区间的逆序对的个数
        return (left + right + count)% 1000000007;
        
    }
    int InversePairs(vector<int> data) {
       if(data.empty())
       {
           return 0;
       }
        vector<int> temp(data.size());
       
        return Merge(data,0,data.size() - 1,temp) ;
    }
};

方法一:暴力求解(超时)

你会求逆序对,剩下就是反转了,直接看代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
using namespace std;
 
long long  Merge(vector<int> info,int begin,int end,vector<int> temp)
{
    if(begin == end)
    {
        return 0;
    }
    long long count = 0;
     
    int mid = (begin + end) >> 1;
    long long left = Merge(info,begin,mid,temp);
    long long right = Merge(info,mid + 1,end,temp);
     
    int begin1 = begin;
    int end1 = mid;
    int begin2 = mid + 1;
    int end2 = end;
    int index = end;
     
    while(begin1 <= end1 && begin2 <= end2)
    {
        if(info[end1] > info[end2])
        {
            count = count + end2 - begin2 + 1;
            temp[index--] = info[end1--];
        }
        else
        {
            temp[index--] = info[end2--];
        }
    }
    while(end1 >= begin1)
    {
        temp[index--] = info[end1--];
    }
    while(end2 >= begin2)
    {
        temp[index--] = info[end2--];
    }
    //拷贝
    for(int i =  begin;i <= end ;i++)
    {
        info[i] = temp[i];
    }
         
        //左边区间的逆序对的个数 + 由边区间的逆序对的个数 + 合并两个区间的逆序对的个数
    return (left + right + count);
}
 
void InversePairs(vector<int> info)
{
    vector<int> temp(info.size());
     
    cout<<Merge(info,0,info.size() - 1,temp)<<endl;
}
 
void Reverse(vector<int>& info,int num)
{
    int count = info.size() / num;
    for(int i = 0;i < count;i++)
    {
        reverse((info.begin() + i * num),info.begin() + ((i + 1) * num));
         
    }
    InversePairs(info);
}
 
void GetReverseCount(vector<int>& info,vector<int>& ans)
{
    for(auto e : ans)
    {
        int num = pow(2,e);
        Reverse(info,num);
    }
}
 
int main()
{
    int n;
    cin>>n;
    vector<int> v(pow(2,n));
    for(int i = 0;i < pow(2,n);i++)
    {
        cin>>v[i];
    }
    int num;
    cin>>num;
    vector<int> ans(num);
    for(int i = 0;i < num;i++)
    {
        cin>>ans[i];
    }
    GetReverseCount(v,ans);
     
    return 0;
}

方法二:优化

每次翻转,然后利用归并排序求解逆序对,复杂度太高,只能通过50%的数据。显然每次都要归并排序成本太高,那么可不可只使用一次归并排序哪?

  • 可以发现,逆序对翻转后,变成顺序对,如1 2,翻转变成2 1
  • 因此我们需要 记录不同长度的逆序对和顺序对的数量,当翻转时,仅需交换小于等于该翻转长度的逆序对和顺序对(因为大于翻转长度的逆序对不会收到影响),将所有的逆序对加起来即可得到结果。
  • 举个例子,如 2 1 4 3,长度为2 ^ 1的逆序对数量为2(2 1一对,4 3一对),长度为2 ^ 2的逆序对数量为0(2 1 4 3中2 1 和4 3不构成逆序对,不要重复计算)。之后便可以只交换逆序对和顺序对数量,求逆序对的和即可
  • 顺序对怎么计算,可以考虑用 总数-逆序对-相等的对
#include <vector>
#include <iostream>
#include <math.h>
using namespace std;
void merge(vector<long long int>&nums, long long int lbegin, long long int lend,long long int rbegin,long long int rend,long long int index,vector<long long int>&cnt) {
    long long int p1 = lbegin, p2 = rbegin;
    vector<long long int> tmp(rend - lbegin + 1);
    long long int i = 0;
    long long int pairsCnt = 0;
    while (p1 <= lend && p2 <= rend) {
        if (nums[p1] <= nums[p2]) {
            tmp[i++] = nums[p1++];
        }
        else if(nums[p1]>nums[p2]){
            pairsCnt += (lend - p1 + 1);
            tmp[i++] = nums[p2++];
        }
    }
    while (p1 <= lend) {
        tmp[i++] = nums[p1++];
    }
    while (p2 <= rend) {
        tmp[i++] = nums[p2++];
    }
    for (int i = 0; i < tmp.size(); i++) {
        nums[lbegin++] = tmp[i];
    }
    cnt[index] += pairsCnt;
}
void mergeSort(vector<long long int>& nums, long long int begin, long long int end, long long int index,vector<long long int>& cnt) {
    if (begin == end)
        return;
    long long int mid = (end - begin) / 2 + begin;
    mergeSort(nums, begin, mid,index-1,cnt);
    mergeSort(nums, mid + 1, end,index-1,cnt);
    merge(nums, begin, mid, mid + 1, end,index,cnt);
}
int main() {
    ios::sync_with_stdio(false);
    long long int n;
    cin >> n;
    long long int cnt = pow(2, n);
    vector<long long int> nums;
    while (cnt--) {
        long long int tmp;
        cin >> tmp;
        nums.push_back(tmp);
    }
    vector<long long int> order(n+1,0);//保存不同长度的顺序对个数
    vector<long long int> reOrder(n+1,0);//保存不同长度的逆序对个数
    vector<long long int> reverse_nums(nums.rbegin(), nums.rend());
    mergeSort(nums, 0, nums.size() - 1,n,reOrder);
    mergeSort(reverse_nums, 0, reverse_nums.size() - 1, n,order);
    long long int m;
    cin >> m;
    while (m--) {
        long long int q;
        cin >> q;
        long long int cnt = 0;
        for (long long int i = 1; i <= q; i++) {
            swap(order[i], reOrder[i]);//每次反转后,需要交换q范围内顺序对和逆序对的数量
        }
        for (long long int i = 1; i <= n; i++) {
            cnt += reOrder[i];
        }
        cout << cnt << endl;
    }
    system("pause");
    return 0;
}

原创文章 209 获赞 172 访问量 10万+

猜你喜欢

转载自blog.csdn.net/wolfGuiDao/article/details/105717683