【NOIP2014模拟11.3】蛋糕

题目

今天是Bessie的生日,他买了一个蛋糕和朋友们一起分享,蛋糕可以看成是一个R行C列的表格,共有R*C个格子,每个格子都有一个0至9的数字,表示该格子蛋糕拥有的巧克力。现在Bessie要把蛋糕横的切3刀再竖的切3刀,由于Bessie刀法厉害,所以每个格子蛋糕都是完整的,显然蛋糕会被切成16份,然后Bessie和他的15个朋友们每人拿一份,Bessie比较客气,总是等其他朋友拿完了,Bessie拿最后剩下的那一份。Bessie的朋友们都很不客气,都是挑最多巧克力的那份去拿,于是Bessie最后拿到手的那份蛋糕总是巧克力总和最少的。Bessie心想:既然自己总是最后拿蛋糕,那应该怎么切蛋糕,才能使得自己拿的那部分蛋糕的有尽量多的巧克力呢?这个问题自然是你的任务了。

分析

要求最大值最小,自然考虑到二分答案,
那么暴力枚举横切的三条边,
将竖切的三条边用三次二分来找出来,如果无法找出这缩小二分出的答案。
时间复杂度\(O(log_2(\dfrac{sum}{16})n^33log_2n)\)

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const int maxlongint=2147483647;
const int mo=1000000007;
const int N=85;
using namespace std;
int a[N][N],n,m,sum[N][N],d[4];
int val(int x,int y,int x1,int y1)
{
    return sum[x1][y1]-sum[x-1][y1]-sum[x1][y-1]+sum[x-1][y-1];
}
int rf(int l,int r,int v)
{
    int t=l;
    while(l<r)
    {
        int mid=(l+r)/2;
        if(v<=min(val(1,t,d[1],mid),min(val(d[1]+1,t,d[2],mid),min(val(d[2]+1,t,d[3],mid),val(d[3]+1,t,n,mid))))) r=mid;
        else
            l=mid+1;
    }
    return l;
}
bool ok(int v)
{
    int pos=1,np=0;
    np=rf(pos,m+1,v);
    if(np==m+1) return false;
    pos=np+1;
    np=rf(pos,m+1,v);
    if(np==m+1) return false;
    pos=np+1;
    np=rf(pos,m+1,v);
    if(np==m+1) return false;
    pos=np+1;
    np=rf(pos,m+1,v);
    if(np==m+1) return false;
    return true;
}
bool dg(int x,int j,int v)
{
    if(x>3)
    {
        if(val(j,1,n,m)<v*4) return false;
        if(ok(v)) return true;
        else 
        return false;
    }
    for(int i=j;i<=n;i++)
    {
        if(val(j,1,i,m)>=v*4)
        {
            d[x]=i;
            if(dg(x+1,i+1,v)) return true;
        }
    }
    return false;
}
int main()
{
    scanf("%d%d\n",&n,&m);
    int num=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            char c=getchar();
            while(c<'0' ||  c>'9') c=getchar();
            a[i][j]=c-'0';
            num+=a[i][j];
        }
    for(int i=1;i<=n;i++) a[i][m+1]=100;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m+1;j++)
            sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
    int l=0,r=num/16;
    while(l+1<r)
    {
        int mid=(l+r)/2;
        if(dg(1,1,mid)) l=mid;
        else r=mid;
    }
    if(dg(1,1,r)) printf("%d",r);
    else printf("%d",l);
}

猜你喜欢

转载自www.cnblogs.com/chen1352/p/9071411.html