USACO 2019 February Contest, Gold Problem 3. Painting the Barn dp 两个矩阵圈出最大权值

62 篇文章 0 订阅

题目链接:http://usaco.org/index.php?page=viewproblem2&cpid=923

 

题意:

       在200*200的二维坐标轴上,给你n个矩阵,每个位置都有一个被覆盖的层数,问你在可以最多再画两个矩阵的情况下,被覆盖正好k层的位置最多有多少。

做法:

        因为n只有200,所以n三次方的做法也是可以的。预处理要做的就是处理出当前有多少格子正好是k层和k-1层,现在是k层的情况下,在新的数组中这个要更新为-1,意思是如果这个格子被覆盖,答案就会-1,如果这个位置正好是k-1层,那么这个位置更新为1 。再做一次二维前缀和,(原来找格子上是不是k层也是要先用二维前缀和做一下的,还是很明显的)。

         预处理做完了之后就是要进行最大字段和的处理,假设我们固定要算列i和列j(i<j)之间矩阵的最大值,那么我们只要再for一遍,不断的做k行之上和k行之下的最大值,我们就能得到一个列i和列j之间的最大值,这个最大值可以用来更新列i右边的最大值和列j左边的最大值,然后我们用同样的方法处理行上边和下边的最大值,之后我们就可以枚举每一列左边+右边的最大值和每一行上边+下边的最大值来更新我们的最大值了。


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int mod=(int)1e9+7;
const int maxn=205;
int mp[maxn][maxn],mp2[maxn][maxn],dpbot[maxn];
int n,k,xl,yl,xh,yh,ans,dptop[maxn];
int dplef[maxn],dprig[maxn];
int Cal(int xll,int yll,int addx,int addy){
    return mp2[xll+addx][yll+addy]-mp2[xll+addx][yll]-mp2[xll][yll+addy]+mp2[xll][yll];
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d",&xl,&yl,&xh,&yh);
        mp[xl+1][yl+1]++,mp[xl+1][yh+1]--;
        mp[xh+1][yl+1]--,mp[xh+1][yh+1]++;
    }
    for(int i=1;i<=200;i++){
        for(int j=1;j<=200;j++){
            mp[i][j]=mp[i][j]+mp[i-1][j]+mp[i][j-1]-mp[i-1][j-1];
            if(mp[i][j]==k-1) mp2[i][j]=1;
            if(mp[i][j]==k) mp2[i][j]=-1,ans++;
        }
    }
    for(int i=1;i<=200;i++)
        for(int j=1;j<=200;j++)
            mp2[i][j]=mp2[i][j]+mp2[i-1][j]+mp2[i][j-1]-mp2[i-1][j-1];
    int ret=0;
    for(int i=0;i<=200;i++){
        for(int len=1;len+i<=200;len++){
            int topsum=0,botsum=0,lefsum=0,rigsum=0;
            for(int k=1;k<=200;k++){
                topsum=max(0,topsum+Cal(k-1,i,1,len));
                botsum=max(0,botsum+Cal(200-k,i,1,len));
                lefsum=max(0,lefsum+Cal(i,k-1,len,1));
                rigsum=max(0,rigsum+Cal(i,200-k,len,1));
                ret=max(ret,dptop[k]=max(dptop[k],topsum));
                ret=max(ret,dpbot[k]=max(dpbot[k],botsum));
                ret=max(ret,dplef[k]=max(dplef[k],lefsum));
                ret=max(ret,dprig[k]=max(dprig[k],rigsum));
            }
        }
    }
    for(int i=1;i<=200;i++){
        dptop[i]=max(dptop[i],dptop[i-1]);
        dpbot[i]=max(dpbot[i],dpbot[i-1]);
        dplef[i]=max(dplef[i],dplef[i-1]);
        dprig[i]=max(dprig[i],dprig[i-1]);
    }
    for(int i=1;i<=199;i++){
        ret=max(ret,dptop[i]+dpbot[200-i]);
        ret=max(ret,dplef[i]+dprig[200-i]);
    }
    printf("%d\n",ret+ans);
    return 0;
}

/*
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值