日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

RMQ算法讲解

發布時間:2023/12/4 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 RMQ算法讲解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。 本文鏈接:https://blog.csdn.net/qq_41311604/article/details/79900893 </div><!--一個博主專欄付費入口--><!--一個博主專欄付費入口結束--><link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-4a3473df85.css"><link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-4a3473df85.css"><div class="htmledit_views" id="content_views"><p>現在給你一個問題:給你一個數組 ,其中有N個數字,現在給你一次詢問,給你區間[l ,r],問你在這個區間內的最大值為多少?</p>

哇!這題簡單啊,一個for循環,遍歷數組記錄最大值輸出即可啊。

?

那好,現在我告訴你假設N為50000,給你Q次詢問((1 ≤ Q ≤ 200,000)),如果這種情況,我們還每次都進行暴力遍歷求解的話,那么無論你提交幾萬次都會得到如下結果:

?

是的,這種暴力遍歷求解雖然思維簡單,代碼簡短,但是很慢啊。

?

那該怎么做呢?以前我也不會啊,自從學了RMQ,誒,真好用,我們全家都用它!

?

RMQ(Range Minimum/Maximum Query),即區間最值查詢。RMQ算法一般用較長時間做預處理,時間復雜度為O(nlogn),然后可以在O(1)的時間內處理每次查詢。

?

下面我們從一個實際問題來解釋RMQ

我們假設數組arr為:1,2,6,8,4,3,7

我們設二維數組dp[i][j]表示從第i位開始連續 個數中的最小值。例如dp[2][1]就表示從第二位數開始連續兩個數的最小值(也就是從第二位數到第三位數的最小值),即2,6中的最小值,所以dp[2][1] = 2;

?

其實我們求 dp[i][j] 的時候可以把它分成兩部分,第一部分是從 到 ,第二部分從到 ,為什么可以這么分呢?其實我們都知道二進制數前一個數是后一個的兩倍,那么可以把 到 這個區間通過分成相等的兩部分,?那么轉移方程很容易就寫出來了。(dp[i][0]就表示第i個數字本身)

?

dp[i][j] = min(dp [i][j - 1], dp [i + (1 << j - 1)][j - 1])

由此給出下列代碼:

  • void rmq_init()
  • {
  • for(int i=1;i<=N;i++)
  • dp[i][0]=arr[i];//初始化
  • for(int j=1;(1<<j)<=N;j++)
  • for(int i=1;i+(1<<j)-1<=N;i++)
  • dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
  • }
  • 這里需要注意一個循環變量的順序,我們看到外層循環變量為j,內層循環變量為i,這是為什么呢?可以互換一下位置嗎?

    ?

    答案當然是不可以,我們要理解這個狀態轉移方程的意義,這個狀態方程的含義是:先更新每兩個元素中的最小值,然后通過每兩個元素的最小值獲得每4個元素中的最小值,依次類推更新所有長度的最小值。

    ?

    而如果是i在外,j在內的話,我們更新的順序就變成了從1開始的前1個元素,前2個元素,前4個元素,前8個元素。。。

    當j等于3的時候dp[1][3] = min(min(ans[0],ans[1],ans[2],ans[3]),min(ans[4],ans[5],ans[6],ans[7])))的值,

    但是我們根本沒有計算min(ans[0],ans[1],ans[2],ans[3])和min(ans[4],ans[5],ans[6],ans[7]),所以這樣的方法肯定是錯誤的。

    為了避免這樣的錯誤,一定要好好理解這個狀態轉移方程所代表的含義。

    ?

    接下來我們來講解RMQ的查詢部分,假設我們需要查詢區間[l ,r]中的最小值,令k = , 則區間[l, r]的最小值RMQ[l,r] = min(dp[l][k], dp[r - (1 << k) + 1][k]);

    ?

    但是為什么這樣就可以保證是區間最小值了呢?

    ?

    dp[l][k]維護的是區間 [l, l + 2^k - 1] , dp[r - (1 << k) + 1][k]維護的是區間 [r - 2^k + 1, r] 。

    那么只要我們保證? ≤ 就能保證RMQ[l,r] = min(dp[l][k], dp[r - (1 << k) + 1][k]);


    ?

    接下來我們用分析法來證明這個不等式:

    我們假設 ? ≤ 這個等式成立

    即有 r - l + 2 ≤ 也就是 r - l + 2 ≤

    又因為 k = ;

    那么 r - l + 2 ≤ 2 * (r - l +1)

    則 r - l + 2 ≤ 2*(r - l) + 2

    即 r - l ≤ 2*(r-l)

    所以 r - l ≥ 0,即假設成立

    我們舉個例子, l = 4,r = 6;

    假設數組arr為:1,2,6,8,4,3,7

    此時 k = = = 1

    則區間[4,6]的最小值 = min(dp[4][1],dp[5][1])

    dp[4][1] = 4,dp[5][1] = 3,所以區間[4,6]的最小值 = min(dp[4][1],dp[5][1]) = 3

    我們很容易看出來答案是正確的。

    由此給出查詢部分代碼:

  • int rmq(int l,int r)
  • {
  • int k=log2(r-l+1);
  • return min(dp[l][k],dp[r-(1<<k)+1][k]);
  • }
  • ?
  • 好了,至此RMQ全部介紹完畢。

    總結

    以上是生活随笔為你收集整理的RMQ算法讲解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。