BZOJ 1169: [Baltic2008]Grid
一个矩阵,可以横着切S刀,竖着切R刀,求小矩阵和最大的最小是多少。
二分答案,dfs出竖着切的,横着贪心check。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 25;
ll a[maxn][maxn];
int n,m,R,S;
template<typename T> inline void read(T &x){
x=0;T f=1;char ch;do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');do x=x*10+ch-'0',ch=getchar();while(ch<='9'&&ch>='0');x*=f;
}
template<typename A,typename B> inline void read(A&x,B&y){read(x);read(y);}
template<typename A,typename B,typename C> inline void read(A&x,B&y,C&z){read(x);read(y);read(z);}
template<typename A,typename B,typename C,typename D> inline void read(A&x,B&y,C&z,D&w){read(x);read(y);read(z);read(w);}
bool ok;
int dl[maxn];
ll mid;
ll bs[maxn];
bool isok(){
//if(dl[0]==2&&dl[1]==4) cout<<"mid"<<mid<<endl;
dl[R]=n;
memset(bs,0,sizeof bs);
//int bef=0;
int cnt=0;
for(int i=1;i<=m;i++){
bool can=1;
for(int j=0;j<=R;j++){
if(j==0&&bs[j]+a[dl[j]][i]-a[dl[j]][i-1]>mid) can=0;
else if(bs[j]+a[dl[j]][i]-a[dl[j]][i-1]-(a[dl[j-1]][i]-a[dl[j-1]][i-1])>mid)
can=0;
}
if(!can){
cnt++;
for(int j=0;j<=R;j++){
bs[j]=0;
}
}
for(int j=0;j<=R;j++){
if(j==0) bs[j]+=a[dl[j]][i]-a[dl[j]][i-1];
else bs[j]+=a[dl[j]][i]-a[dl[j]][i-1]-(a[dl[j-1]][i]-a[dl[j-1]][i-1]);
}
for(int j=0;j<=R;j++)
if(bs[j]>mid) return 0;
}
return cnt<=S;
}
void dfs(int now,int dep){
if(ok) return;
if(dep==R){
if(isok()) ok=1;
return;
}
for(int i=now;i<n;i++){
dl[dep]=i;
dfs(i+1,dep+1);
}
}
bool check(ll mid){
ok=0;
dfs(1,0);
if(ok) return 1;
return 0;
}
int main(){
//freopen("in.txt","r",stdin);
read(n,m,R,S);
R=min(R,n-1);S=min(S,m-1);
ll l=0,r=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
read(a[i][j]),r+=a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=a[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
//for(int i=1;i<=n;i++)
// for(int j=1;j<=m;j++)
// printf("%d%c",(int)a[i][j]," \n"[j==m]);
ll res=-1;
//cout<<check(mid=31)<<endl;
//l=r=130;
while(l<=r){
mid=(l+r)/2;
if(check(mid))
r=(res=mid)-1;
else l=mid+1;
}
cout<<res;
return 0;
}