逆序對及其變種

2021-10-03 08:54:37 字數 4005 閱讀 9991

首先看一下逆序對(leetcode 劍指offer 51):

在陣列中的兩個數字,如果前面乙個數字大於後面的數字,則這兩個數字組成乙個

逆序對。輸入乙個陣列,求出這個陣列中的逆序對的總數。

示例 1:

輸入: [7,5,6,4]

輸出: 5

限制:0 <= 陣列長度 <= 50000

這也是乙個經典問題,而且如果方向沒對的話,是想破頭也想不出來的。肯定不能暴力搜尋,會tle。

怎麼解決這個問題呢?我們先看一下歸併排序。

給定乙個整數陣列 nums,將該陣列公升序排列。

示例 1:

輸入:[5,2,3,1]

輸出:[1,2,3,5]

示例 2:

輸入:[5,1,1,2,0,0]

輸出:[0,0,1,1,2,5]

1 <= a.length <= 10000

-50000 <= a[i] <= 50000

排序演算法千千萬,這次我們用歸併排序來解決。

class

solution

void

mergesort

(vector<

int>

& nums,

int p,

int r)

}void

merge

(vector<

int>

& nums,

int p,

int q,

int r)

else}}

};

為什麼要引入歸併排序呢?回憶一下歸併排序的過程。對於某個陣列,我們是把它分成兩半,每一半都進行各自的歸併排序,當然要是這個陣列大小為1的話就直接返回了。而這兩半陣列各自排好序以後,接下來做什麼?我們需要把這兩半陣列,通過雙指標遍歷的方法寫成排好序的乙個陣列。而這個合併的過程,就可以順便計算出逆序對的個數。

怎麼做到這一點呢?首先要搞清楚一點,對於兩半陣列來說,無論某一半的陣列元素順序發生什麼變化,橫跨這兩半陣列的逆序對是始終存在的。比如說,前一半陣列有個元素5,後一半陣列有個元素3,那麼無論這兩半陣列內部發生什麼順序上的變化,這個5和3組成的逆序對是始終存在的。

明白了這一點,我們在對這兩半陣列分別進行排序的時候,計算出這兩半陣列內部各自的逆序對個數之和,然後再在合併這兩個陣列的過程中,計算橫跨這兩半陣列的逆序對個數。

怎麼計算橫跨這兩半陣列的逆序對個數呢?現在前後兩半陣列nums1, nums2各有指標i, j,想象一下,當nums1[i] <= nums2[j] 的時候,也就是說nums1[i] 可以和nums[j]前面的元素組成逆序對,這個時候逆序對的個數應該加上j(假設下標從0開始),然後 i 向後移動1位;而nums1[i] > nums2[j] 的時候,j 向後移動1位,這個時候我們不改變逆序對的計數。

通過這種過程,就可以計算出逆序對的個數了。

class

solution

void

mergesort

(vector<

int>

& nums,

int p,

int r)

}void

merge

(vector<

int>

& nums,

int p,

int q,

int r)

else}}

private

:int ans;

};

這份**是有點兒問題的,就是在leetcode的測試樣例上,有乙個樣例的元素有int_max,這會使得哨兵失效。不過這個演算法是沒有任何問題的,這種細節就沒必要糾結了。

時間限制:c/c++ 2秒,其他語言4秒

空間限制:c/c++ 256m,其他語言512m

作為程式設計師的小q,他的數列和其他人的不太一樣,他有2^n個數。

老闆問了小q一共 m次,每次給出乙個整數qi(1 <= i <= m), 要求小q把這些數每

2^qi分為一組,然後把每組進行翻轉,小q想知道每次操作後整個序列中的逆序對個數是

多少呢?

例如:對於序列1 3 4 2,逆序對有(4, 2),(3, 2),總數量為2。

翻轉之後為2 4 3 1,逆序對有(2, 1),(4, 3), (4, 1), (3, 1),總數量為4。

輸入描述:

第一行乙個數n(0 <= n <= 20)

第二行2^n個數,表示初始的序列(1 <= 初始序列 <= 10^9)

第三行乙個數m(1 <= m <= 10^6)

第四行m個數表示qi(0 <= qi <= n)

輸出描述:

m行每行乙個數表示答案。

輸入例子1:

22 1 4 3

41 2 0 2

輸出例子1:06

60例子說明1:

初始序列2 1 4 3

2^q1 = 2 ->

第一次:1 2 3 4 -> 逆序對數為0

2^q2 = 4 ->

第二次:4 3 2 1 -> 逆序對數為6

2^q3 = 1 ->

第三次:4 3 2 1 -> 逆序對數為6

2^q4 = 4 ->

第四次:1 2 3 4 -> 逆序對數為0

這個題很簡單嘛,每次翻轉一下,然後用剛才計算逆序對的方法計算一下就好了,繁瑣了點兒,不過思路還是一樣的,實現**如下:

#include

#include

#include

#include

using

namespace std;

int ans =0;

intreversepairs

(vector<

int>

& nums)

;void

mergesort

(vector<

int>

& nums,

int p,

int r)

;void

merge

(vector<

int>

& nums,

int p,

int q,

int r)

;int

main()

cin >> m;

vector<

int>

ms(m)

,ans

(m,0);

for(

int i =

0; i < m;

++i)

int two_ms_i;

for(

int i =

0; i < m;

++i)

/*for (int j = 0; j < n; ++j)

}}*/

ans[i]

=reversepairs

(ms);}

for(

int i =

0; i < m;

++i)

}return0;

}int

reversepairs

(vector<

int>

& nums)

void

mergesort

(vector<

int>

& nums,

int p,

int r)

}void

merge

(vector<

int>

& nums,

int p,

int q,

int r)

else

}}

一提交,tle。

m最大為106,也就是說最多要做106次逆序對的計算,不tle才怪了,因此肯定有什麼捷徑。

這道題我想不出來,也沒查到很明確的解答。希望有人能做出來,然後讓我觀摩一下。

RANSAC及其經典變種

近年來學者們不斷在對經典演算法ransac進行各種改進,本文想總結一下近年來ransac演算法的各種改進優化。看到乙個寫得很好的部落格系列,可惜博主沒有繼續寫下去了,很希望博主哪天想起來繼續寫下去,我暫時在這裡做一些簡單補充吧 此處暫時只蒐集到的一些相關文獻,後續隨著學習的增加會再繼續完善 p ra...

nim遊戲及其變種

nim遊戲 n堆石子,操作為可以從任意一堆拿走任意正整數個石子,不能操作者輸。問先手勝還是後手勝。結論 當n堆石子的個數的亦或為0時,先手必敗 否則先手必勝。證明 1,若亦或值不為0,則一定可以通過一步操作讓它變成0.考慮亦或值最高位的那個1,一定有一堆石子數在那一位是1 否則總的亦或裡的那個1是怎...

Relu啟用函式及其變種

神經網路中使用啟用函式來加入非線性因素,提高模型的抽象表達能力。relu rectified linear unit,修正線性單元 relu公式近似推導 下面解釋上述公式中的softplus,noisy relu.softplus函式與relu函式接近,但比較平滑,同relu一樣是單邊抑制,有寬廣的...