【题意】给定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; }