P3017 [USACO11MAR]Brownie Slicing G

题意

有一块可以被看作一个 R×CR \times C 大小的方格的蛋糕,第 ii 行第 jj 列的方格上有 Ni,jN_{i,j} 块巧克力碎屑。

首先要水平方向切 A1A-1 刀,把蛋糕分成 AA 条;接着,对于每一条,竖直切 B1B-1 刀,分成 BB 块(注意,每一条蛋糕分割成 BB 小块的方式不必相同,每一刀都只能切沿整数坐标切)。

求问,巧克力碎屑最少的那块蛋糕最多有多少块巧克力碎屑。

分析

设答案为 ansans,显然如果 ans<ansans'<ans,因为 ansans 最大,所以 ansans' 可以满足要求;如果 ans>ansans'>ans,就无法切出来。也就是说答案满足单调性,所以我们可以二分

来一个不一样的二分

这里给出一个与其他题解不一样的二分。

提前做一下处理:因为计算中需要多次用到许多块的巧克力碎屑数,所以我们用二维前缀和存储每一块的和。

处理
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]

先二分巧克力碎屑最少的那块蛋糕有 xx 块巧克力碎屑,边界最少为 00,最多为 S(1,1,R,C)\text{S}(1,1,R,C)

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;

接下来我们写 check(x)\text{check}(x) 函数。

先确定每一条的第一行 r1r1 和最后一行 r2r2,循环 AA 次,每次令 r2=r1r2=r1,令 r2r2 不断增加,直到 S(r1,1,r2,C)B×x\text{S}(r1,1,r2,C)≥B\times x,也就是这一条的和有可能分成每份不小于 xxBB 份。

接着再令 r2r2 不断增加,用 check2(x,r1,r2)\text{check2}(x,r1,r2) 函数表示当巧克力碎屑最少的那块蛋糕有 xx 块巧克力碎屑时,要把第一行为 r1r1,最后一行为 r2r2 的一条分成符合要求的 BB 块是否可行,直到 check2(x,r1,r2)=1\text{check2}(x,r1,r2)=1 时,r2r2 停止自增。这时我们找出了这一条满足要求时最小需要的行数。

在整个过程中,若 r2>Rr2>R,也就是无法满足要求,则返回假,否则最后返回真。

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;
}

最后我们来写 check2(x,r1,r2)\text{check2}(x,r1,r2) 函数。

先确定这一条的第一列 c1c1 和最后一列 c2c2,循环 BB 次,每次令 c2=c1c2=c1,令 c2c2 不断增加,直到 S(r1,c1,r2,c2)x\text{S}(r1,c1,r2,c2)≥x,这时就算切了一刀,这一块已符合要求。

在整个过程中,若 c2>Rc2>R,也就是无法满足要求,则返回假,否则最后返回真。

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;
}

总体复杂度最坏情况下为 Θ(RClogS(1,1,R,C))Θ(RC\log \text{S}(1,1,R,C))

代码

#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;
}
posted @ 2021-07-18 18:36  luckydrawbox  阅读(15)  评论(0)    收藏  举报  来源