第一次写单调队列优化的dp。。
解题思路:dp很明显了,和上次多校的一道题差不多。(dragon ball)
可以看成是分组的背包问题(背包九讲06)每一层是一组,不过不能不取。
但是这样,dp[i][p]--第i层第p个位置可以取到的最大值。直接递推复杂度O(n*m*T*2)
肯定不行。于是用单调队列:假设dp[i][p]只和第i+1层p左边的最优值有关。这样的话左-〉右推过去每层可以基本O(M)的推出dp值,可是题目是和两边都有关啊。。。再从右面推到左面。和原来存在dp[i][p]内的值比较去最优即可。
我自己写的单调队列类,实际证明还是起了作用的。
我的代码差。。怎么也优化不到200ms。。唉
#include"stdio.h"
#include"stdlib.h"
#include"time.h"
#include"math.h"
#include"iostream"
#include"string.h"
#include"algorithm"
#define nmax 110
#define mmax 10010
#define inf 6000000
inline int maxy(int a,int b){return a>b?a:b;}
inline int miny(int a,int b){return a<b?a:b;}
inline long long maxy(long long a,long long b){return a>b?a:b;}
inline long long miny(long long a,long long b){return a<b?a:b;}
using namespace std;
int N,M,X,T;
int mat[nmax][mmax];
int sum[nmax][mmax];
int dp[nmax][mmax];
//bool vis[nmax][mmax];
///-------------
class Que{
//private:
//int head,tail;
public:
int A,B;
int q[mmax];
int ind[mmax];
public:
void init();
void add2right(int v,int i);
void add2right_min(int v,int i);
void pop();
int leftmosti();
int front();
int size();
}q1;
///func
int Que::size(){
return B-A;
}
void Que::init(){
A=B=0;
}
void Que::add2right(int v,int i){
//int &A=head;
//int &B=tail;
if(A==B){
ind[B]=i;
q[B++]=v;
}
else {
//!empty
while(B!=A){
if(q[B-1]<v){
B--;
}
else {break;}
}
ind[B]=i;
q[B++]=v;
}
}
void Que::add2right_min(int v,int i){
if(A==B){
ind[B]=i;
q[B++]=v;
}
else {
//!empty
while(B!=A){
if(q[B-1]>v){
B--;
}
else {break;}
}
ind[B]=i;
q[B++]=v;
}
}
void Que::pop(){
A++;
}
int Que::leftmosti(){
if(A==B)return -1;
return ind[A];
}
int Que::front(){
if(A==B)return -1;
return q[A];
}
///----------------
int get_sum(int fl,int st,int ed){
if(st==ed)return mat[fl][st];
if(st>ed){
return sum[fl][st]-sum[fl][ed-1];
}
else{
return sum[fl][ed]-sum[fl][st-1];
}
}
int main(){
while(scanf("%d%d%d%d",&N,&M,&X,&T)==4){
int i;
for(i=1;i<=N;i++){
mat[i][0]=mat[i][N+1]=0;
sum[i][0]=0;
for(int j=1;j<=M;j++){
scanf("%d",&mat[i][j]);
sum[i][j]=mat[i][j]+sum[i][j-1];
}
}//io
for(i=0;i<=M;i++)dp[N+1][i]=0;
for(i=N;i>=1;i--){
int j;
//left to right;
int cursum=get_sum(i,1,M);
q1.init();
for(j=1;j<=M;j++){
while(q1.size()>0&&abs(j-q1.leftmosti())>T)q1.pop();
cursum-=mat[i][j-1];
q1.add2right(cursum+dp[i+1][j],j);
int con=0;
if(j<M)con=get_sum(i,j+1,M);
dp[i][j]=q1.front()-con;
}
///2nd-------------
cursum=get_sum(i,1,M);
q1.init();
for(j=M;j>0;j--){
while(q1.size()>0&&abs(j-q1.leftmosti())>T)q1.pop();
cursum-=mat[i][j+1];
q1.add2right(cursum+dp[i+1][j],j);
int con=0;
if(j>1)con=get_sum(i,j-1,1);
dp[i][j]=maxy(dp[i][j],q1.front()-con);
}
}
printf("%d\n",dp[1][X]);
}
return 0;}