单调栈:(修筑绿化带)

题目:修筑绿化带,这中间的j-b+d+1,和i-a+c+1,始终没弄明白是怎么回事。

[HAOI2007] 修筑绿化带

【问题描述】
为了增添公园的景致,现在需要在公园中修筑一个花坛,同时在画坛四周修建一片绿化带,让花坛被绿化带围起来。
如果把公园看成一个M*N的矩形,那么花坛可以看成一个C*D的矩形,绿化带和花坛一起可以看成一个A*B的矩形。
如果将花园中的每一块土地的“肥沃度”定义为该块土地上每一个小块肥沃度之和,那么,
绿化带的肥沃度=A*B块的肥沃度-C*D块的肥沃度
为了使得绿化带的生长得旺盛,我们希望绿化带的肥沃度最大。
【输入】:
第一行有6个正整数M,N,A,B,C,D
接下来一个M*N的数字矩阵,其中矩阵的第i行j列元素为一个整数Xij,表示该花园的第i行第j列的土地“肥沃度”。
【输出】:
一个正整数,表示绿化带的最大肥沃程度。
【输入输出样例】
parterre.in
4 5 4 4 2 2
20 19 18 17 16
15 14 13 12 11
10 9 8 7 6
5 4 3 2 1

parterre.out
132
【数据范围】
30%的数据,1<=M,N<=50
100%的数据,1<=M,N<=1000,1<=A<=M,1<=B<=N,1<=C<=A-2,1<=D<=B-2,1<=“肥沃度”<=100
代码都是借鉴的,两个思路都是一样的。

#include<cstdio>
//#include"debug.h"
const int N=1010;
int n,m,a,b,c,d,i,j,ans,s[N][N],v[N][N],dp[N][N],DP[N][N];
int q[N],head,tail;
int main()
{
    freopen("parterre.in","r",stdin);
    freopen("parterre.out","w",stdout);
    scanf("%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d);
    //计算总和
    for (i=1;i<=n;i++)
    for (j=1;j<=m;j++){
        scanf("%d",&s[i][j]);
        s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    }
    //在m*n中c*d对应的总和值
    for (i=2;i<=n-c;i++)
    for (j=2;j<=m-d;j++)
        v[i][j]=s[i+c-1][j+d-1]-s[i+c-1][j-1]-s[i-1][j+d-1]+s[i-1][j-1];
    //for (i=2;i<=n-c;i++) print(v[i],2,m-d,' ');writeln;
    for (i=2;i<=n-c;i++){
        head=1,tail=0;
        for (j=2;j<=m-d;j++){
            while (head<=tail&&q[head]<=j-b+d+1) head++;
            //上面的j-b+d+1,没弄明白什么回事,同理下面的i-a+c+1.
            while (head<=tail&&v[i][q[tail]]>v[i][j]) tail--;
            q[++tail]=j;
            dp[i][j]=v[i][q[head]];
        }
    }
    //for (i=2;i<=n-c;i++) print(dp[i],2,m-d,' ');writeln;
    for (j=2;j<=m-d;j++){
        head=1;tail=0;
        for (i=2;i<=n-c;i++){
            while (head<=tail&&q[head]<=i-a+c+1) head++;
            while (head<=tail&&dp[q[tail]][j]>dp[i][j]) tail--;
            q[++tail]=i;
            DP[i][j]=dp[q[head]][j];
        }
    }
    //for (i=2;i<=n-c;i++) print(DP[i],2,m-d,' ');writeln;
    for (i=1;i<=n-a+1;i++)
    for (j=1;j<=m-b+1;j++){
        int sum=s[i+a-1][j+b-1]-s[i+a-1][j-1]-s[i-1][j+b-1]+s[i-1][j-1];
        sum-=DP[i+a-c-1][j+b-d-1];
        if (sum>ans) ans=sum; 
    }
    printf("%d\n",ans);
    return 0;
}
#include<cstdio>
#include<cstring>
using namespace std;
#define MAXN 1010
int Map[MAXN][MAXN],N,M,A,B,C,D,S2[MAXN][MAXN]={0};//S2表示C*D矩形的面积 
int S[MAXN][MAXN]={0},S1[MAXN][MAXN]={0},Ans=0;//S为前缀和,S1为A*B矩形的面积 
class sigal_queue{ //双端队列 
    private:
    int q[MAXN<<1],st,en;
    public:
    sigal_queue(){
        st=1; en=0;
        memset(q,0,sizeof(q));
    }
    inline void clear(){st=1; en=0;} 
    inline void push_back(int x){q[++en]=x;}
    inline int back(){return q[en];}
    inline void pop_back(){en--;}
    inline void pop_front(){st++;}
    inline bool empty(){return st>en;}
    inline int front(){return q[st];}
}Q;
inline int max(int x,int y){
    if(x>y) return x;
    return y;
}
void init(){
    scanf("%d%d%d%d%d%d",&M,&N,&A,&B,&C,&D);
    for(int i=1;i<=M;i++){
        int sum=0;
        for(int j=1;j<=N;j++){
            scanf("%d",&Map[i][j]);
            sum+=Map[i][j];
            S[i][j]=sum+S[i-1][j];  //处理出前缀和 
        }
    }
}
void work(){
    for(int i=A;i<=M;i++){  //计算两个矩形的面积 
        for(int j=B;j<=N;j++){
            S1[i][j]=S[i][j]-S[i-A][j]-S[i][j-B]+S[i-A][j-B];
        }
    }
    for(int i=C;i<=M;i++){
        for(int j=D;j<=N;j++){
            S2[i][j]=S[i][j]-S[i-C][j]-S[i][j-D]+S[i-C][j-D];
        }
    }
}
void solve(){
    for(int i=C;i<=M;i++){  //以每一行做单调队列 
        Q.clear();
        for(int j=D;j<=N;j++){
            while(!Q.empty()&&S2[i][Q.back()]>=S2[i][j]) Q.pop_back();
            Q.push_back(j);
            while(Q.front()<=j-B+D+1) Q.pop_front(); //如果超了限制,弹出 
            if (j>=B-1) S[i][j]=S2[i][Q.front()];   //重新构造前缀,意为最大 
        }
    }
    for(int j=B;j<=N;j++){  //以列做单调队列 
        Q.clear();
        for(int i=C;i<=M;i++){
            while(!Q.empty()&&S[Q.back()][j-1]>=S[i-1][j-1]) Q.pop_back();
            Q.push_back(i-1);
            while(Q.front()<=i-A+C) Q.pop_front();
            if(i>=A) Ans=max(Ans,S1[i][j]-S[Q.front()][j-1]);
        }
    }
    printf("%d",Ans);
}
int main(){
    freopen("parterre.in","r",stdin);
    freopen("parterre.out","w",stdout);
    init();
    work();
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值