【BZOJ】2196: [Usaco2011 Mar]Brownie Slicing

【题意】给定n*m的数字矩阵,要求横着切A-1刀,对每块再分别竖着切B-1刀,是最小子矩阵最大。

【算法】二分+贪心

【题解】还记得提高组2015跳石头吗?这道题做法一致,只不过拓展到二维而已。

二分最小子矩阵值,考虑行,对于每一刀贪心一行一行拓展到能切马上切。

对于行贪心中得到的若干行,通过列贪心确定是否能切(一列一列拓展)。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
int read(){
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c=='-')t=-1;
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s*t;
}
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a<b?b:a;}
int abs(int x){return x>0?x:-x;}
void mins(int &a,int b){if(a>b)a=b;}
void maxs(int &a,int b){if(a<b)a=b;}
//void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
/*------------------------------------------------------------*/
const int inf=0x3f3f3f3f,maxn=510;
 
int n,m,sum[maxn][maxn],lx,rx,ly,ry,A,B;
bool calc(int lx,int ly,int rx,int ry,int num){return (sum[rx][ry]-sum[rx][ly-1]-sum[lx-1][ry]+sum[lx-1][ly-1])>=num;}
bool pd(int num){
    bool yes=1;ly=ry=1;
    for(int j=1;j<=B;j++){
        while(ry+1<=m&&!calc(lx,ly,rx,ry,num))ry++;
        if(!calc(lx,ly,rx,ry,num)){yes=0;break;}
        ly=++ry;
    }
    return yes;
}
bool check(int num){
    bool ok=1;
    lx=1,rx=1;
    for(int i=1;i<=A;i++){
        while(rx+1<=n&&!pd(num))rx++;
        if(!pd(num)){ok=0;break;}
        lx=++rx;
    }
    return ok;
}   
int main(){
    n=read();m=read();A=read();B=read();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)sum[i][j]=sum[i][j-1]+read();
        for(int j=1;j<=m;j++)sum[i][j]+=sum[i-1][j];
    }
    int l=0,r=sum[n][m],mid;
    while(l<r){
        mid=(l+r)>>1;
        if(check(mid))l=mid+1;else r=mid;
    }
    printf("%d",l-1);
    return 0;
}
View Code

 

posted @ 2017-10-17 17:04  ONION_CYC  阅读(280)  评论(0编辑  收藏  举报