農夫約翰決定給站在一條線上的n(1 <= n <= 200,000)頭奶牛製作一張全家福**,n頭奶牛編號1到n。於是約翰拍攝了m(1 <= m <= 100,000)張**,每張**都覆蓋了連續一段奶牛:第i張**中包含了編號a_i 到 b_i的奶牛。但是這些**不一定把每乙隻奶牛都拍了進去。在拍完**後,約翰發現了乙個有趣的事情:每張**中都有且僅有乙隻身上帶有斑點的奶牛。約翰意識到他的牛群中有一些斑點奶牛,但他從來沒有統計過它們的數量。 根據**,請你幫約翰估算在他的牛群中最多可能有多少只斑點奶牛。如果無解,輸出「-1」。
我一開始看見了區間,便老是往資料結構那裡想,花費了很長時間;後來想到了動規,希望用f(i, j)來表示前i頭牛、前j張**中最多會有多少斑點牛,卻發現這樣的決策具有後效性。
避免後效性的方法便是增加限制條件。我們讓f(i)指第i頭牛是有斑點的情況下前i頭奶牛中最多會有多少斑點牛即可。
本題的難點之一在於可以轉移的轉移的狀態範圍不固定。i的前一頭斑點牛必須在i所在任何區間的左側,而且它要麼被在i所在的左端點最靠左的區間a的相鄰左側的區間b內,要麼位於a與b間不被區間包含的部分中。因此,可以轉移到i的狀態組成了乙個區間[_cows[i].prevl, _cows[i].prevr]。prevr便是a的左端點-1,prevl便是b的左端點。因此遞迴式為f(i) = max(f[j]+1|j∈[_cows[i].prevl, _cows[i].prevr])。由於轉移**是個區間,所以我們可以用單調佇列來優化。
這是乙個極其新穎的做法:若我們知道乙個序列是單調不減的,有很多元素都重複,那麼我們可以把序列中值開始變化的分界點的值求出來,然後從前到後或從後往前掃瞄一遍即可求出這個序列。
我們知道隨著i的遞增, _cows[i].prevl,prevr都是遞增的。那麼分界點在**呢?若prevl變化,則i在乙個區間的右端點+1處;若prevr變化,則i在乙個區間的右端點處。由於取min和取max的區別,我們prevl從左到右掃瞄,prevr從右到左掃瞄。
注意我們以上都沒有提到「區間的左端點」這個位置,因為區間的左端點可能並不是prevl的分界點,因為若該區間左側有一段空白,prevl不變。另外prevr的分界點可能會漏掉,因為一段區間右端點以後不一定緊跟另乙個區間的左端點。
錯誤求法是dp完後,對所有i取最大值。結果是會漏掉-1的情況。原因是f[i]指考慮負責包含[1, i]這些點的區間,以後的區間沒有考慮。若最大值是在前面取的,一些右面的區間可就沒有對應的點嘍!
正確的做法是後部增加乙個奶牛,求它的f值就對了。這樣也可以迅速得知-1的情況。
#include #include #include using namespace std;const int max_range_cnt = 100010, max_id = 200010, minf = 0xcfcfcfcf;
int totid, totrange;
int f[max_id];
struct range
}_ranges[max_range_cnt];
struct cow
_cows[max_id];
struct slidewindow
void push_back(int x)
void pop_back()
int back()
void pop_front()
int front()
bool empty()
}idq;
int tail;
public:
slidewindow(): tail(-1){}
int move(int tail, int len)
if (len <= 0)
return minf;
return f[idq.front()];
}int getmax()
};void read()
int noans;
void setcoverl()
for (int i = 2; i <= totid; i++)
_cows[i].prevl = max(_cows[i].prevl, _cows[i - 1].prevl);
for (int i = totid - 1; i >= 1; i--)
_cows[i].prevr = min(_cows[i].prevr, _cows[i + 1].prevr);
}int dp()
return f[totid] < 0 ? -1 : f[totid] - 1;
}int main()
Luogu 1419(二分答案 單調佇列)
傳送門 題意 求最大段落平均值 子段和除以長度 段落長度在s到t之間 題解 段落平均值一定在某個區間 min,v 內取值 雖然取值是離散的但是有界 v即為所求最大平均值。考慮二分答案,當前二分的答案為mid,如果將所有數都減去mid後仍存在乙個長度在s到t之間的子段和非負,那該段落的平均值一定不小於...
NOIP2017模擬賽 龍珠(dp 單調佇列優化)
你得到了乙個龍珠雷達,它會告訴你龍珠出現的時間和地點。龍珠雷達的畫面是一條水平的數軸,每乙個視窗時間,數軸的某些點上會出現同一種龍珠,每當你獲得其中一顆龍珠,其它龍珠就會消失。下乙個視窗時間,數軸上又會出現另一種龍珠。總共有n個視窗時間,也就是總共有n種龍珠。假設你會瞬間移動,你從數軸的x點移動到y...
bzoj3156防禦準備 DP之單調佇列優化
第一行為乙個整數n表示戰線的總長度。第二行n個整數,第i個整數表示在位置i放置守衛塔的花費ai。output 共乙個整數,表示最小的戰線花費值。sample input 102 3 1 5 4 5 6 3 1 2 sample output 這道題是dp,這是毋庸置疑的。我們先考慮暴力dp,我們顯然...