P3017 [USACO11MAR]Brownie Slicing G
题意
有一块可以被看作一个 大小的方格的蛋糕,第 行第 列的方格上有 块巧克力碎屑。
首先要水平方向切 刀,把蛋糕分成 条;接着,对于每一条,竖直切 刀,分成 块(注意,每一条蛋糕分割成 小块的方式不必相同,每一刀都只能切沿整数坐标切)。
求问,巧克力碎屑最少的那块蛋糕最多有多少块巧克力碎屑。
分析
设答案为 ,显然如果 ,因为 最大,所以 可以满足要求;如果 ,就无法切出来。也就是说答案满足单调性,所以我们可以二分。
来一个不一样的二分
这里给出一个与其他题解不一样的二分。
提前做一下处理:因为计算中需要多次用到许多块的巧克力碎屑数,所以我们用二维前缀和存储每一块的和。
处理
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
{
cin>>s[i][j];
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
}
查询
#define S(a,b,c,d) s[c][d]+s[a-1][b-1]-s[c][b-1]-s[a-1][d]
先二分巧克力碎屑最少的那块蛋糕有 块巧克力碎屑,边界最少为 ,最多为 。
int L=0,R=S(1,1,r,c);
while(L<R)
{
int mid=(L+R+1)>>1;
if(check(mid))//check(mid)代表当巧克力碎屑最少的那块蛋糕有mid块巧克力碎屑时是否可以切成
L=mid;
else
R=mid-1;
}
cout<<L;
接下来我们写 函数。
先确定每一条的第一行 和最后一行 ,循环 次,每次令 ,令 不断增加,直到 ,也就是这一条的和有可能分成每份不小于 的 份。
接着再令 不断增加,用 函数表示当巧克力碎屑最少的那块蛋糕有 块巧克力碎屑时,要把第一行为 ,最后一行为 的一条分成符合要求的 块是否可行,直到 时, 停止自增。这时我们找出了这一条满足要求时最小需要的行数。
在整个过程中,若 ,也就是无法满足要求,则返回假,否则最后返回真。
bool check(int x)
{
int r1,r2=0;
for(int i=1;i<=a;i++)
{
r1=r2+1;//第一行会等于上一条最后一行+1
r2=r1;
if(r2>r)
return 0;
while(S(r1,1,r2,c)<x*b)
{
r2++;
if(r2>r)
return 0;
}
while(!check2(x,r1,r2))
{
r2++;
if(r2>r)
return 0;
}
}
return 1;
}
最后我们来写 函数。
先确定这一条的第一列 和最后一列 ,循环 次,每次令 ,令 不断增加,直到 ,这时就算切了一刀,这一块已符合要求。
在整个过程中,若 ,也就是无法满足要求,则返回假,否则最后返回真。
bool check2(int x,int r1,int r2)
{
int c1,c2=0;
for(int i=1;i<=b;i++)
{
c1=c2+1;//第一列会等于上一块最后一列+1
c2=c1;
if(c1>c)
return 0;
while(S(r1,c1,r2,c2)<x)
{
c2++;
if(c2>c)
return 0;
}
}
return 1;
}
总体复杂度最坏情况下为 。
代码
#include<bits/stdc++.h>
#define S(a,b,c,d) s[c][d]+s[a-1][b-1]-s[c][b-1]-s[a-1][d]
using namespace std;
const int N=5010;
int r,c,a,b,s[N][N];
bool check2(int x,int r1,int r2)
{
int c1,c2=0;
for(int i=1;i<=b;i++)
{
c1=c2+1;
c2=c1;
if(c1>c)
return 0;
while(S(r1,c1,r2,c2)<x)
{
c2++;
if(c2>c)
return 0;
}
}
return 1;
}
bool check(int x)
{
int r1,r2=0;
for(int i=1;i<=a;i++)
{
r1=r2+1;
r2=r1;
if(r2>r)
return 0;
while(S(r1,1,r2,c)<x*b)
{
r2++;
if(r2>r)
return 0;
}
while(!check2(x,r1,r2))
{
r2++;
if(r2>r)
return 0;
}
}
return 1;
}
int main()
{
cin>>r>>c>>a>>b;
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
{
cin>>s[i][j];
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
}
int L=0,R=S(1,1,r,c);
while(L<R)
{
int mid=(L+R+1)>>1;
if(check(mid))
L=mid;
else
R=mid-1;
}
cout<<L;
return 0;
}

浙公网安备 33010602011771号