Description
今天是Bessie的生日,他买了一个蛋糕和朋友们一起分享,蛋糕可以看成是一个R行C列的表格,共有R*C个格子,每个格子都有一个0至9的数字,表示该格子蛋糕拥有的巧克力。现在Bessie要把蛋糕横的切3刀再竖的切3刀,由于Bessie刀法厉害,所以每个格子蛋糕都是完整的,显然蛋糕会被切成16份,然后Bessie和他的15个朋友们每人拿一份,Bessie比较客气,总是等其他朋友拿完了,Bessie拿最后剩下的那一份。Bessie的朋友们都很不客气,都是挑最多巧克力的那份去拿,于是Bessie最后拿到手的那份蛋糕总是巧克力总和最少的。Bessie心想:既然自己总是最后拿蛋糕,那应该怎么切蛋糕,才能使得自己拿的那部分蛋糕的有尽量多的巧克力呢?这个问题自然是你的任务了。
100%的数据, 4 <= R,C <= 75。
Analysis
看到双最优先考虑二分啦
二分出一个答案,而后枚举哪三列切我们可以O(n)扫一遍很快地知道行最多切几刀也可以二分,但因为r,c很小所以其实两个跑出来差不多
O(n4logn)
或
O(n3log2n)
Code
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=80;
int lim,n,m,a[N][N];
int s(int x1,int y1,int x2,int y2)
{
return a[x2][y2]-a[x2][y1-1]-a[x1-1][y2]+a[x1-1][y1-1];
}
bool pd(int x,int y,int z)
{
int i,j,k;
for(i=1;i<=n+1;i++)
if(s(1,1,i,x)>=lim && s(1,x+1,i,y)>=lim
&& s(1,y+1,i,z)>=lim && s(1,z+1,i,m)>=lim) break;
if(i>n) return 0;
for(j=i+1;j<=n+1;j++)
if(s(i+1,1,j,x)>=lim && s(i+1,x+1,j,y)>=lim
&& s(i+1,y+1,j,z)>=lim && s(i+1,z+1,j,m)>=lim) break;
if(j>n) return 0;
for(k=j+1;k<=n+1;k++)
if(s(j+1,1,k,x)>=lim && s(j+1,x+1,k,y)>=lim
&& s(j+1,y+1,k,z)>=lim && s(j+1,z+1,k,m)>=lim) break;
if(k>n) return 0;
if(s(k+1,1,n,x)>=lim && s(k+1,x+1,n,y)>=lim
&& s(k+1,y+1,n,z)>=lim && s(k+1,z+1,n,m)>=lim) return 1;
return 0;
}
bool check()
{
fo(i,1,m-3)
fo(j,i+1,m-2)
fo(k,j+1,m-1)
if(pd(i,j,k)) return 1;
return 0;
}
int main()
{
scanf("%d %d\n",&n,&m);
int l=0,r=0;
fo(i,1,n)
{
fo(j,1,m)
{
int x=getchar()-'0';
r+=x;
a[i][j]=x+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
}
scanf("\n");
}
r=r/16+1;
while(l<r-1)
{
lim=(l+r)>>1;
if(check()) l=lim;
else r=lim;
}
printf("%d",l);
return 0;
}