[Usaco2011 Mar]Brownie Slicing

Description
Bessie烘焙了一块巧克力蛋糕。这块蛋糕是由 RC(1<=R,C<=500) 个小的巧克力蛋糕组成的。 第 i 行,第j列的蛋糕有 Nij(1<=Nij<=4,000) 块巧克力碎屑。 Bessie想把蛋糕分成 AB 块, (1<=A<=R,1<=B<=C) : 给 AB 只奶牛。蛋糕先水平地切 A1 刀 (只能切沿整数坐标切)来把蛋糕划分成 A 块。然后再把剩下来的每一块独立地切B1刀, 也只能切沿整数坐标切。其他 AB1 只奶牛就每人选一块,留下一块给Bessie。由于贪心, 他们只会留给Bessie巧克力碎屑最少的那块。 求出Bessie最优情况下会获得多少巧克力碎屑。 例如,考虑一个 54 的蛋糕,上面的碎屑分布如下图所示:

1 2 2 1 
3 1 1 1 
2 0 1 3 
1 1 1 1 
1 1 1 1 

Bessie必须把蛋糕切成4条,每条分成2块。Bessie能像这样切蛋糕:

1 2 | 2 1 
--------- 
3 | 1 1 1 
--------- 
2 0 1 | 3 
--------- 
1 1 | 1 1 
1 1 | 1 1 

因此,其他贪得无厌的牛拿完后,Bessie仍能够拿走3个巧克力碎屑。

Input
第1行: 四个空格隔开的数: R C A B
第2- R+1 行: 第 i+1 行有 C 个整数,Ni1 , Ni2 NiC

Output
第1行:一个整数:Bessie保证能拿到的最多碎屑数。

Sample Input
5 4 4 2
1 2 2 1
3 1 1 1
2 0 1 3
1 1 1 1
1 1 1 1

Sample Output
3

HINT

Source
Gold

方法
二分答案,check函数里有两个优化:第一个就是枚举横坐标时,如果从当前点切一刀还不够(蛋糕价值下限*要纵向切的块数)时,就说明无论怎么切也满足不了条件,那么就不要枚举纵向的切法了;第二个就是如果搜索纵切方案时,前面的切法已经可以满足限制时,就可以跳出不用再枚举了。

代码

#include <cstdio>

const int maxn=500;

int r,c,a,b;
int map[maxn+10][maxn+10];
int sum[maxn+10][maxn+10];
//sum数组是这个矩阵的前缀和
int left,right,ans;

int check(int lim)
{
    int prex=1,cntx=0;
    for(int i=1; i<=r; i++)
    {
        int prey=1,cnty=0;
        if(sum[i][c]-sum[prex-1][c]<lim*b)
        //优化1,应该能优化50%
        {
            continue;
        }
        for(int j=1; j<=c; j++)
        {
            if(sum[i][j]-sum[prex-1][j]-sum[i][prey-1]+sum[prex-1][prey-1]>=lim)
            {
                cnty++;
                prey=j+1;
            }
            if(cnty>=b)
            //优化2
            {
                break;
            }
        }
        if(cnty>=b)
        {
            cntx++;
            prex=i+1;
        }
    }
    if(cntx>=a)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

int main()
{
    scanf("%d%d%d%d",&r,&c,&a,&b);
    for(int i=1; i<=r; i++)
    {
        for(int j=1; j<=c; j++)
        {
            scanf("%d",&map[i][j]);
            sum[i][j]=map[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
        }
    }
    right=sum[r][c]/(a*b);
    //这是答案的上限
    while(left<=right)
    //二分枚举答案
    {
        int mid=(left+right)>>1;
        if(check(mid))
        {
            ans=mid;
            left=mid+1;
        }
        else
        {
            right=mid-1;
        }
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值