1. 程式人生 > 實用技巧 >PepperLa's Boast(dp+二維單調佇列維護區間極值)

PepperLa's Boast(dp+二維單調佇列維護區間極值)

題目連結:K. PepperLa's Boast

題意:給你一個n * m的矩陣,你要從(1, 1)點走到(n, m)點,由於矩陣著火,你每次只能往右,往下,往右下其中一個方向走1步,矩陣每個點有a[i][j]那麼多的空氣,如果a[i][j]<=0,則該點充滿煙霧你在該點不能呼吸,每次呼吸你可以吸入任意數量的空氣並存下來,在經過煙霧點時,你必須憋氣,每次憋氣花費U的氣體走最多K步,問你是否可以走到點(n, m),如果可以輸出最終肺內最多能剩餘多少氣體,如果不能輸出-1

Input

多組輸入,每組輸入N,M,K,U(1N,M1e3,1K,U1e9)表示n*m的矩陣,K代表走最多K步,U代表每次憋氣需要花費U那麼多氣體,然後輸入n * m的矩陣,a[i][j][1e9,1e9]

,∑n*m<=7e6,輸入保證a[1][1], a[n][m]>0
Output

問你是否可以走到點(n, m),如果可以輸出最終肺內最多能剩餘多少氣體,如果不能輸出-1

Sample Input

3 4 2 1

1 0 0 9

0 -1 1 1

-1 0 2 1

Sample Output

4

思路:考慮dp,dp[i][j]表示到點(i, j)時肺內最多剩餘多少氣體,點(i, j)要麼由不憋氣走一步,要麼由憋氣走最多K步轉移過來,所以dp[i][j]的狀態轉移方程為

問題轉化為求以點(i, j)為矩陣右下角的長寬為K的矩陣極值

我們用遞減單調佇列來維護區間極值,對於每一列建一個遞減單調佇列,從左往右掃的時候用一個行的單調佇列維護區間極值,入隊的是列單調佇列的隊首元素

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;

typedef long long ll;

const int N = 1010;

struct node {
    ll data;
    int id;
};

int n, m, k, u;
int a[N][N];
ll dp[N][N];
node row[N], col[N][N];
int head1, tail1;
int head[N], tail[N];

int main() {
    for(int i = 0; i < N; i++) dp[i][0
] = dp[0][i] = -1; while(~scanf("%d %d %d %d", &n, &m, &k, &u)) { for(int i = 1; i <= m; i++) head[i] = tail[i] = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { scanf("%d", &a[i][j]); dp[i][j] = -1; } } dp[1][1] = a[1][1]; for(int i = 1; i <= n; i++) { head1 = tail1 = 0; for(int j = 1; j <= m; j++) { while(head[j] < tail[j] && col[j][head[j]].id < i - k) head[j]++; while(head1 < tail1 && row[head1].id < j - k) head1++; if(a[i][j] > 0) { if(dp[i - 1][j] != -1) dp[i][j] = max(dp[i][j], dp[i - 1][j] + a[i][j]); if(dp[i][j - 1] != -1) dp[i][j] = max(dp[i][j], dp[i][j - 1] + a[i][j]); if(dp[i - 1][j - 1] != -1) dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + a[i][j]); if(head[j] < tail[j]) { ll now = col[j][head[j]].data; while(head1 < tail1 && row[tail1 - 1].data <= now) tail1--; row[tail1++] = (node){now, j}; } if(head1 < tail1) dp[i][j] = max(dp[i][j], row[head1].data + a[i][j] - u); if(head[j] < tail[j]) tail1--; } if(dp[i][j] >= u) { while(head[j] < tail[j] && dp[i][j] >= col[j][tail[j] - 1].data) tail[j]--; col[j][tail[j]++] = (node){dp[i][j], i}; } if(head[j] < tail[j]) { ll now = col[j][head[j]].data; while(head1 < tail1 && row[tail1 - 1].data <= now) tail1--; row[tail1++] = (node){now, j}; } // cout << i << " " << j << " " << dp[i][j] << endl; } } printf("%lld\n", dp[n][m]); } return 0; }
View Code