PepperLa's Boast(单调队列优化二维dp)
题意:一个人在(1,1)点处,由于这个n*m的二维空间着火了,他要从(1,1)点逃到(n,m)点。给出 n*m 矩阵内每一个坐标的的值代表这里的空气,限制是空气小于等于零的地方不能呼吸。这个人的每一步只可以走到三个方向:(x+1,y+1) or (x+1,y) or (x,y+1) ,每次可以走一步到有空气的地方进行换气(呼吸),也可以消耗 U 体积的空气走 K 步到有空气的地方进行换气呼吸。问当到达(n,m)点时,这个人的肺中最多存有多少空气
题解:
做到这题的时候还没有掌握单调队列QAQ
所以WA:
1 // 又是看了一晚上的dp不知道思路错在哪里 2 // 其实是不可以这么一步一步贪心去取的最大值的 3 // 因为我到当前点i,j才减上一个点要去掉的u 4 // 有可能之前的大,但是他要减u,到这儿变小了 5 // 而之前小的, 但是他中间不经过无空气地带, 到这儿不减u, 这样得到的答案反而比上面的值打 6 // 所以, 很明显这要再来一重循环(跑每一个到当前点距离不超过点)去一个个试试了 7 // 这复杂度应该是O(n*m*k),所以要优化了 8 9 #include <iostream> 10 #include <bits/stdc++.h> 11 using namespace std; 12 typedef long long ll; 13 const int maxn = 1e3+10; 14 15 ll ans; 16 int n,m; 17 ll k,u; 18 ll dp[maxn][maxn]; 19 ll a[maxn][maxn]; 20 ll z[maxn][maxn]; 21 22 struct node{ 23 int id; 24 int u,v; 25 }; 26 27 bool cmp(node x, node y){ 28 if( x.u==y.u ) return x.v>y.v; 29 return x.u>y.u; 30 } 31 32 int judge(int i,int j){ 33 node f[5]; 34 f[1].id=1; f[1].u=dp[i-1][j-1]; f[1].v=z[i-1][j-1]; 35 f[2].id=2; f[2].u=dp[i-1][j]; f[2].v=z[i-1][j]; 36 f[3].id=3; f[3].u=dp[i][j-1]; f[3].v=z[i][j-1]; 37 sort(f+1,f+1+3,cmp); 38 return f[1].id; 39 } 40 41 int main() 42 { 43 while( ~scanf("%d%d%lld%lld",&n,&m,&k,&u)){ 44 memset(dp,-1,sizeof(dp)); 45 for(int i=1;i<=n;i++){ 46 for(int j=1;j<=m;j++){ 47 scanf("%lld",&a[i][j]); 48 } 49 } 50 51 dp[1][1] = a[1][1]; 52 if( a[1][1]>=u ) z[1][1] = k; 53 else z[1][1] = 0; 54 55 for(int i=1;i<=n;i++){ 56 for(int j=1;j<=m;j++){ 57 if( i==1 && j==1 ) continue; 58 else if( i==1 ){ 59 if( dp[i][j-1]<=0 ){ 60 continue; 61 } 62 63 if( a[i][j]<=0 ){ 64 z[i][j] = z[i][j-1]-1; 65 if( z[i][j]<=0 ){ 66 dp[i][j] = -1; 67 }else{ 68 dp[i][j] = dp[i][j-1]; 69 } 70 } 71 else{ 72 if( a[i][j-1]>0 ) dp[i][j] = dp[i][j-1]+a[i][j]; 73 else dp[i][j] = dp[i][j-1]-u+a[i][j]; 74 75 if( dp[i][j]>=u ) z[i][j] = k; 76 else z[i][j] = 0; 77 } 78 } 79 else if( j==1 ){ 80 if( dp[i-1][j]<=0 ) continue; 81 82 if( a[i][j]<=0 ){ 83 z[i][j] = z[i-1][j]-1; 84 if( z[i][j]<=0 ){ 85 dp[i][j] = -1; 86 }else{ 87 dp[i][j] = dp[i-1][j]; 88 } 89 } 90 else{ 91 if( a[i-1][j]>0 ) dp[i][j] = dp[i-1][j]+a[i][j]; 92 else dp[i][j] = dp[i-1][j]-u+a[i][j]; 93 if( dp[i][j]>=u ) z[i][j] = k; 94 else z[i][j] = 0; 95 } 96 } 97 else{ 98 if( a[i][j]<=0 ){ 99 int f = judge(i,j); 100 if( f==1 ){ 101 if( dp[i-1][j-1]<=0 ) continue; 102 103 z[i][j] = z[i-1][j-1]-1; 104 if( z[i][j]<=0 ){ 105 dp[i][j] = -1; 106 } 107 else{ 108 dp[i][j] = dp[i-1][j-1]; 109 } 110 } 111 else if( f==2 ){ 112 if( dp[i-1][j]<=0 ) continue; 113 114 z[i][j] = z[i-1][j]-1; 115 if( z[i][j]<=0 ){ 116 dp[i][j] = -1; 117 } 118 else{ 119 dp[i][j] = dp[i-1][j]; 120 } 121 } 122 else if( f==3 ){ 123 if( dp[i][j-1]<=0 ) continue; 124 125 z[i][j] = z[i][j-1]-1; 126 if( z[i][j]<=0 ){ 127 dp[i][j] = -1; 128 } 129 else{ 130 dp[i][j] = dp[i][j-1]; 131 } 132 } 133 } 134 else{ 135 int f = judge(i,j); 136 if( f==1 ){ 137 if( dp[i-1][j-1]<=0 ) continue; 138 139 140 if( a[i-1][j-1]>0 ){ 141 z[i][j] = k; 142 dp[i][j] = dp[i-1][j-1]+a[i][j]; 143 } 144 else{ 145 dp[i][j] = dp[i-1][j-1]-u+a[i][j]; 146 if( dp[i][j]>=u ) z[i][j] = k; 147 else z[i][j] = 0; 148 } 149 } 150 else if( f==2 ){ 151 if( dp[i-1][j]<=0 ) continue; 152 153 if( a[i-1][j]>0 ){ 154 z[i][j] = k; 155 dp[i][j] = dp[i-1][j]+a[i][j]; 156 } 157 else{ 158 dp[i][j] = dp[i-1][j]-u+a[i][j]; 159 if( dp[i][j]>=u ) z[i][j] = k; 160 else z[i][j] = 0; 161 } 162 } 163 else if( f==3 ){ 164 if( dp[i][j-1]<=0 ) continue; 165 166 if( a[i][j-1]>0 ){ 167 z[i][j] = k; 168 dp[i][j] = dp[i][j-1]+a[i][j]; 169 } 170 else{ 171 dp[i][j] = dp[i][j-1]-u+a[i][j]; 172 if( dp[i][j]>=u ) z[i][j] = k; 173 else z[i][j] = 0; 174 } 175 } 176 } 177 } 178 } 179 } 180 printf("%lld\n",dp[n][m]); 181 } 182 return 0; 183 }
AC_Code:可以先看一看我的这篇博客-->here
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=1e3+10; 5 const int inf = 0x3f3f3f3f; 6 7 struct node{ 8 ll val; 9 int idx; 10 }; 11 int n,m,k,u; 12 int a[maxn][maxn]; 13 ll dp[maxn][maxn]; 14 deque<node>col_q[maxn], row_q; 15 16 void _max(ll &x, ll y ){ 17 if( x<y ) x=y; 18 } 19 //纵向每列建一个列单调队列,在每行从左到右转移时候再建一个行单调队列,入队的都是列单调队列的队首元素 20 //为什么呢?因为从左边或者从左上转移过来,都是用单调队列后从左边转移过来啊 21 22 int main() 23 { 24 memset(dp,-1,sizeof(dp)); 25 while( ~scanf("%d%d%d%d",&n,&m,&k,&u) ){ 26 for(int i=1;i<=n;i++){ 27 for(int j=1;j<=m;j++){ 28 scanf("%d",&a[i][j]); 29 dp[i][j] = -1; 30 } 31 } 32 dp[1][1] = a[1][1]; 33 for(int i=1;i<=m;i++) col_q[i].clear(); 34 for(int i=1;i<=n;i++){ 35 row_q.clear(); 36 for(int j=1;j<=m;j++){ 37 while( col_q[j].size() && col_q[j].front().idx<i-k ) col_q[j].pop_front();//注意这里是<i-k, 不是<=i-k,举个栗子就知道了从1走5步不是到5是到6,那么从6退5补是到6-5=1处 38 while( row_q.size() && row_q.front().idx<j-k ) row_q.pop_front(); 39 40 if( a[i][j]>0 ){ 41 if( dp[i-1][j]!=-1 ) _max(dp[i][j], dp[i-1][j]+a[i][j]); 42 if( dp[i][j-1]!=-1 ) _max(dp[i][j], dp[i][j-1]+a[i][j]); 43 if( dp[i-1][j-1]!=-1 ) _max(dp[i][j], dp[i-1][j-1]+a[i][j]); 44 45 if( col_q[j].size() ){ 46 while( row_q.size() && row_q.back().val<=col_q[j].front().val ) row_q.pop_back(); 47 row_q.push_back(node{col_q[j].front().val, j});//暂时加入上方的push here 48 } 49 if( row_q.size() ) _max(dp[i][j], row_q.front().val+a[i][j]-u); 50 if( col_q[j].size() ) row_q.pop_back();//然后删除, pop here 51 } 52 53 if( dp[i][j]>=u ){ 54 while( col_q[j].size() && col_q[j].back().val<=dp[i][j]) col_q[j].pop_back(); 55 col_q[j].push_back(node{dp[i][j], i}); 56 } 57 58 if( col_q[j].size() ){ 59 while( row_q.size() && row_q.back().val<=col_q[j].front().val ) row_q.pop_back(); 60 row_q.push_back(node{col_q[j].front().val, j}); 61 } 62 } 63 } 64 printf("%lld\n", dp[n][m]); 65 } 66 return 0; 67 }