1. 程式人生 > >區間問題三部曲(1) : 線上區間最值演算法

區間問題三部曲(1) : 線上區間最值演算法

區間問題是常見的統計問題之一,廣泛應用於各類問題之中。主要形式有區間最值問題,區間和問題等。而區間最值問題可以分為靜態區間最值和動態區間最值。我們先看一個情景:

描述

老管家是一個聰明能幹的人。他為財主工作了整整10年,財主為了讓自已賬目更加清楚。要求管家每天記k次賬,由於管家聰明能幹,因而管家總是讓財主十分滿意。但是由於一些人的挑撥,財主還是對管家產生了懷疑。於是他決定用一種特別的方法來判斷管家的忠誠,他把每次的賬目按1,2,3…編號,然後不定時的問管家問題,問題是這樣的:在a到b號賬中最少的一筆是多少?為了讓管家沒時間作假他總是一次問多個問題。

輸入格式

輸入中第一行有兩個數m,n表示有m(m<=100000)筆賬,n表示有n個問題,n<=100000。
第二行為m個數,分別是賬目的錢數
後面n行分別是n個問題,每行有2個數字說明開始結束的賬目編號。

輸出格式

輸出檔案中為每個問題的答案。具體檢視樣例。

測試樣例1

輸入

10 3 
1 2 3 4 5 6 7 8 9 10 
2 7 
3 9 
1 10

輸出

2 3 1

最好想的方法就是暴力法,每次詢問就過一遍區間找最小值,複雜度O(n^2),對於100000資料量顯然難以勝任。而如果開一個100000*100000的陣列做優化空間又吃不消。所以我們需要ST演算法來解決這個問題。
我們用這道題來解釋ST演算法的執行。

(1) 預處理

預處理使用一個數組dp[0..n][log(n)+1]的陣列,使dp[i][j]為從i開始(包括i)之後2^j個數字中的最小值。具體方法使用動態規劃。

首先 dp[i][0] = a[i], 即從i開始後一箇中的最小值為第i個數(自己嘛)
對於樣例就是這樣的
這裡寫圖片描述

然後 對於dp[j][i],一定有dp[j][i] = min(dp[j][i-1],dp[j+2^(j-1)][i-1])

我們來證明他:dp[j][i-1]記錄著j..j+2^(i-1)-1中的最小值, dp[j+2^(j-1)][i-1]則記錄了j+2^(i-1)..j+2^(i-1)+2^(i-1)-1。合起來就是j..j+2^(i-1)+2^(i-1)-1即j..j+2^i-1為所求。

這裡寫圖片描述

//無視excel公式純屬偷懶

最後 搞dp順序: 因為計算dp[j][i] 需要 dp[j][i-1]和dp[j+2^(j-1)][i-1]。不難看出i計算dp[j][i]需要所有的dp[j][i-1],即i是單調遞增的。

dp要素都齊了,給一個程式片段吧(對於原題)

void init()
{
    for (int i = 1; i <= 18; i++)
        for (int j = 1; j <= n; j++)
            if (j+TwoPow(i) <= n) 
                dp[j][i] = min(dp[j][i-1],dp[j+TwoPow(i-1)][i-1]);
}

其中計算2^x使用了一個小技巧:

#define TwoPow(x) ((1)<<(x))

(2)詢問

算出來這些個數看起來並沒什麼用。然而一種很好的演算法可以在O(1)效率內計算出一段區間內的最小值。先看程式碼

int ask(int i, int j)
{
    int k = (int)(log(j-i+1)/log(2));
    return min(dp[i][k], dp[j-TwoPow(k)+1][k]);
}

看起來有些雲裡霧裡、、但是思想非常簡單。我們舉例說明下:

對於2..8這個區間如何利用已經求過的資料來解答呢?關鍵在於將區間分為兩個長度為2^x(x為自然數)的子區間,且這兩個區間的併為原區間,則這兩個區間的最小值為所求。即2..5, 5..8,兩個區間長度都為4。2..8的最小值即為dp[2][2], dp[5][2]

再例如對於3..7, 可以分成3..6, 4..7; 對於3..8,可以分成3..6, 5..8。

一般的,對於區間a = i..j, 一定可以分成區間i..log2(|a|), j-log2(|a|)+1..j。|a|為區間a的長度,即j-i+1。log2用換底公式可以求出來。

證明這個方法,即區間i..2^(log2(|a|)),j-log2(|a|)+1..2^log2(|a|)的併為原區間非常簡單,這裡省略。

最後給出完整程式碼

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
using namespace std;

#define maxn 100010
int dp[maxn][20];
int n, q;
#define TwoPow(x) ((1)<<(x))

void init()
{
    for (int i = 1; i <= 18; i++)
        for (int j = 1; j <= n; j++)
            if (j+TwoPow(i) <= n) 
                dp[j][i] = min(dp[j][i-1],dp[j+TwoPow(i-1)][i-1]);
}

int ask(int i, int j)
{
    int k = (int)(log(j-i+1)/log(2));
    return min(dp[i][k], dp[j-TwoPow(k)+1][k]);
}

int main()
{
    memset (dp, 127/3, sizeof(dp));
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i++)
        scanf("%d", &dp[i][0]);
    init();
    int i, j;
    while (q --> 0) {
        scanf("%d%d", &i, &j);
        printf("%d ", ask(i,j));
    }
    return 0;
}

吐槽一下c++的IO、、過於慢,居然TLE了、、、看看結果

//暴力法評測結果
#0: Accepted (0ms, 768KiB)
#1: Accepted (0ms, 768KiB)
#2: Accepted (15ms, 800KiB)
#3: Accepted (0ms, 796KiB)
#4: Accepted (468ms, 1864KiB)
#5: Accepted (109ms, 980KiB)
#6: Accepted (265ms, 1180KiB)
#7: Accepted (795ms, 2792KiB)
#8: Time Limit Exceeded (1809ms, 3356KiB)
#9: Accepted (842ms, 2828KiB)
//ST演算法
#0: Accepted (15ms, 8360KiB)
#1: Accepted (0ms, 8360KiB)
#2: Accepted (0ms, 8364KiB)
#3: Accepted (15ms, 8360KiB)
#4: Accepted (140ms, 8364KiB)
#5: Accepted (15ms, 8364KiB)
#6: Accepted (31ms, 8364KiB)
#7: Accepted (31ms, 8360KiB)
#8: Accepted (359ms, 8364KiB)
#9: Accepted (406ms, 8368KiB)

別走開、第二場戰鬥(動態區間和)馬上開始、、、雖然沒什麼人看。。。

相關推薦

區間問題三部曲(1) : 線上區間演算法

區間問題是常見的統計問題之一,廣泛應用於各類問題之中。主要形式有區間最值問題,區間和問題等。而區間最值問題可以分為靜態區間最值和動態區間最值。我們先看一個情景: 描述 老管家是一個聰明能幹的人。他為財主工作了整整10年,財主為了讓自已賬目更加清楚。要求管

快速查詢區間——RMQ演算法(ST實現)

概述 RMQ(Range Minimum/Maximum Query),即區間最值查詢,是指這樣一個問題:對於長度為n的數列A,回答若干詢問RMQ(A,i,j)(i,j<=n),返回數列A中下標在i,j之間的最小/大值。這兩個問題是在實際應用中經常遇到

快速查詢區間——RMQ演算法(線段樹實現程式碼)

要求找出區間內的最大最小值的差。 #include<stdio.h> #include<string.h> #include<math.h> #define lso

RMQ 區間查詢演算法

RMQ(Range Minimum/Maximum Query),對於長度為n的數列A,回答若干詢問RMQ(A,i,j)(i,j<=n),返回數列A中下標在i,j裡的最小(大)值。 主要方法: 樸素(即搜尋),複雜度為O(n)線段樹能在對數時間內在陣列區間上進行

RMQ區間查詢SparseTable演算法

//一維區間最值查詢模板稀疏表sparse table演算法 //區間最值查詢--線段樹--RMQ //區間連續和--線段樹 //區間第k大--快排--劃分樹(線段樹?) --主席樹 //二維區間最值

hdu1754-I Hate It 線段樹RMQ演算法區間問題

I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total

洛谷P1886 滑動窗口(POJ.2823 Sliding Window)(區間

最大 ide dma include names target org void blog To 洛谷.1886 滑動窗口 To POJ.2823 Sliding Window 題目描述 現在有一堆數字共N個數字(N<=10^6),以及一個大小為k的窗口。現在這個

樹狀數組維護區間

%d 修改 stdio.h sin names ont eset soft html 在區間求和時,我們只需求出 [1, r],[1,l?1],利用前綴和的可減性,得到區間 [l,r] 的和。 但區間最值不滿足這個性質。 我們可以把區間 [l,r] 拆分成若幹個子區間,再合

hdu2795(線段樹單點更新&區間)

ref 當前 廣告 sed gif .cn lap ostream spa 題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=2795 題意:有一個 h * w 的板子,要在上面貼 n 條 1 * x 的廣告,在貼第 i 條廣

HDU 1754 I Hate It<區間 單點修改>

des div 分數 流行 esc ble lin problems php I Hate It Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tota

HDU - 1754 I Hate It (線段樹區間)

clas lan () esp tdi typedef show scanf ons 題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=1754 題意:線段樹的單點更新和區間求最值 模板題,,,???,, 1 #in

nyoj 119士兵殺敵(三)(線段樹區間查詢,RMQ算法)

信息 include out online log 每次 left 一行 [0 題目119 題目信息 執行結果 本題排行 討論區 士兵殺敵(三) 時間限制:2000 ms | 內存限制:65535 KB 難度:5

【線段樹查詢區間】poj 3264 Balanced Lineup

truct pri scan aps pre sca print logs oid 1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 5 const i

POJ 3264 Balanced Lineup(線段樹 區間

lld color href .org balanced stream ios void def 題目鏈接:http://poj.org/problem?id=3264 題意:n個數,給定m個區間,求出每個區間內最大值和最小值之差 題解:區間最值問題,挺裸的一道題

【bzoj1552/3506】[Cerc2007]robotic sort splay翻轉,區間

geo ges 空格 操作 i++ led border 輸出 print 【bzoj1552/3506】[Cerc2007]robotic sort Description Input 輸入共兩行,第一行為一個整數N,N表示物品的個數,1<=N<

【hdu5306】Gorgeous Sequence 線段樹區間操作

font 區間和 pro while turn lin int ace 退出 題目描述 給你一個序列,支持三種操作: $0\ x\ y\ t$ :將 $[x,y]$ 內大於 $t$ 的數變為 $t$ ;$1\ x\ y$ :求 $[x,y]$ 內所有數的最大值;$2\

【bzoj4355】Play with sequence 線段樹區間操作

max ans 時間 query sam define sca tps fine 題目描述 維護一個長度為N的序列a,現在有三種操作: 1)給出參數U,V,C,將a[U],a[U+1],...,a[V-1],a[V]都賦值為C。 2)給出參數U,V,C,對於區間[U,

筆記:RMQ(區間)之ST算法

運算 不變 想要 parse 計算機語言 c++ 是我 動態規劃 容易 RMQ(區間最值)之ST算法 RMQ即Range Minimum/Maximun Query 中文意思:查詢一個區間的最小值/最大值 比如有這樣一個數組:A{3 2 4 5 6 8 1 2 9 7},

ACM-ICPC國際大學生程序設計競賽北京賽區(2017)網絡賽 hihocoder #1586 : Minimum-區間查詢區間兩數小乘積+單點更新-線段樹(結構體版)

ns2 edit AD memory body bmi json ffffff inf #1586 : Minimum Time Limit:1000ms Case Time Limit:1000ms Memory Limit:256MB Descripti