單調性-DP優化
zerojudge a146
題目敘述:
給定一個長度為N的序列A以及一個整數K,請找出所有長度為K的窗口極值(min&max)。
N, K <=10^6
Sliding window
1.對於每一個長度為K的窗口全部都掃一次
需(N-K+1)*K次
複雜度為O(NK)
作法
2.考慮單調性
以下先以Min的情況舉例
假設現在要計算以i為起始,那麼Ai有可能成為答案,對於所有j<i,Aj>Ai,Aj絕對不可能是i之後的答案
e.x.
現在有陣列A 5 3 6 7, 若現在要算A[1]的答案,由於5>3,那之後5都不可能會是答案了。
所以考慮維護一個遞增的資料結構,當首元素過期時把它刪掉就可以了。
作法
//單調性,只會越來越小(除非過期
void find_min(int n, int k)
{
deque<int> dq; //dq裡面紀錄的是位置
for (int i=1; i<=n; i++)
{
while (!dq.empty() && a[dq.back()] > a[i]) //把大於a[i]且位置在i前面的都刪掉
dq.pop_back();
dq.push_back(i);
if (dq.front() == i-k) dq.pop_front(); //把過期的刪掉
ans_mn[i] = a[dq.front()];
}
}
而這樣的複雜度為O(N)
給你一個數列
求最長的嚴格遞增數列的長度 (不用連續
1 3 5 4 6
那麼這個數列可以是
1 3 5 6
1 3 4 6
LIS - (n logn)版
如何轉移
* 對於當前數字,看前面誰比她小而且長度最長
dp[i] = {length, from}
LIS - 普通版
int a[M];
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i; j++)
{
if (a[i] >= a[j] && dp[j].ff + 1 > dp[i].ff)
dp[i] = {dp[j].ff + 1, j};
}
}先看題目
給你三個整數 n、l 和 r。
一個 長度為 n 的 ZigZag 陣列 定義如下:
-
每個元素的值都在區間
[l, r]內。 -
相鄰的兩個元素 不能相等。
-
不能出現 三個連續元素 形成嚴格遞增或嚴格遞減的序列。
請你返回 所有合法 ZigZag 陣列的總數,答案對 1e9+7 取模。
常用技巧-結合前綴和
直覺狀態定義
dp[i][k][0] //對於當前的第i項,當前數字為k的可能數
// 0 / 1 代表上或者下
轉移式:
常用技巧-結合前綴和
Code
int dp[2005][2005][2] = {0}, sum = 0; // 第i個元素,結尾為j, k代表往上或下
int zigZagArrays(int n, int l, int r) {
int P = 1e9 + 7;
for (int i = l; i <= r; i++)
dp[1][i][1] = 1, dp[1][i][0] = 1;
for (int i = 2; i <= n; i++)
{
for (int j = l; j <= r; j++)
{
for (int k = l; k < j; k++) // 往下
dp[i][j][0] =(dp[i][j][0] + dp[i - 1][k][1]) % P;
for (int k = j + 1; k <= r; k++)
dp[i][j][1] =(dp[i][j][1] + dp[i - 1][k][0]) % P;
}
//cout << endl;
}
for (int i = l; i <= r; i++)
sum = (sum + dp[n][i][0] + dp[n][i][1]) % P;
return sum;
}複雜度O(n * m * m), where m = r-l
題目敘述:有M個煙火將在一維數列上綻放。
第i個煙火在時間Ti、於位置Ai綻放,其中1<=Ai<=N
類題:watching fireworks is fun
先按照綻放時間排序
定義$$dp_{i,j}$$為看完第i個煙火位於j的開心度
則我們只需關注他會從哪裡走到j,而這個哪裡我們先定為k
也就是 $$|k-j| \le d* (T_i-T_{i-1})$$
解題
可以得到轉移式:
$$dp_{i, j} = max dp_{i-1, k} + b_i - |a_i-j|$$
發現到右邊的$$b_i - |a_i-j|$$與轉移來源k並不相關
會發現與sliding windows很像
只需要找尋k的最大值(對k使用單調隊列)
也可以想成求長度為2*d*(Ti-Ti-1)+1 的sliding windows
解題
題目敘述:有 N 種物品,第 i 種重量為 wi,價值為 vi,且總共有 si 個。
求選出若干個物品使得總重量不超過 W 的情況下,最大總價值為何。
背包問題(有限背包)
以前的做法:定義dp[i]為重量為i時的價格,取
$$ max dp_i =( dp_{i-w[x]} +v[x], dp_i) $$
這在以前每項物品只有一個時複雜度為O(NW)
而現在題目每一項物品最多會有K(Si的最大值)個
這樣的做法複雜度會變成O(NWK)
背包問題(有限背包)
改良作法一:將同個種類的K個物品改成
$$ 1, 2, 4, 8....2^a, K-2^a$$個物品
可以套用回去0/1的背包問題,複雜度為O(NWlogK)
背包問題(有限背包)
改良作法二:定義dp(i, j)為前i個物品中重量為j的價格
$$ dp_{i, j} = max_{0\le x\le s[i]} ( dp_{i-1, j-x*w[i]} + x*v[i] ) $$
我們將j-x*w[i] 訂為k,則上面的式子可改寫為
$$ dp_{i, j} = max_{0\le \frac{j-k}{w[i]}\le s[i], j\equiv k (mod w[i])} ( dp_{i-1, k} + \frac{j-k}{w[i]}*v[i] ) $$
由於我們的轉移來源變成k,可以在寫成下面的樣子來讓j/w[i]從轉移狀態移到前面
$$ dp_{i, j} = \lfloor \frac{j}{w[i]} \rfloor * v[i] + max_{0\le \frac{j-k}{w[i]}\le s[i], j\equiv k (mod w[i])} ( dp_{i-1, k} + \lfloor \frac{k}{w[i]} \rfloor*v[i] ) $$
背包問題(有限背包)
可以發現,對於每個 j
只會取到與他差 w[i]∗x 的重量
也就是我們只要管同餘的重量!(即k)
接著就可以用剛剛教的單調隊列了
背包問題(有限背包)
bitset
一個stl的資料結構
* bool 變數
* 但是儲存空間、位元運算比bool變數的表現好
位元運算:
<< 左移運算子
>> 右移運算子
例題

例題
一般情況下的複雜度是 O(\(n^2\))
但是透過bitset可以優化為O(\(\frac{n^2}{w}\))
ex 3 12
3 4 5
一開始dp = 1
1 | 1000 = 1001
1001 | 10010000 = 10011001
10011001 | 1001100100000 = 1001110111001
參考資料
題目連結
Desk
By yujuan
Desk
- 289