>Description
一个R行C列的表格,共有R*C个格子,每个格子都有一个0至9的数字,表示该格子的价值。现在要把格子横分4份再竖分4份,表格总共会被分成16份,求出在什么情况下,价值最小的那份价值最大。
>Input
第一行,两个整数R,C。
接下来有R行,每行有C个整数,每个整数范围是0至9。
>Output
一个整数,表示最小的价值。
>Sample Input
5 5
95998
21945
23451
99798
74083
>Sample Output
3
40%的数据, 4 <= R,C <= 10。
60%的数据, 4 <= R,C <= 20。
100%的数据, 4 <= R,C <= 75。
>解题思路
数据并不大,所以可以先枚举竖切的3刀在哪(切三刀变成四份),然后在看横切的。
这里就可以用到二分,二分表格中可以切出来的的最小价值,在已知竖切的位置的情况下,可不可以横切成16个格子,并且可以切出二分中mid的价值。
横切>>>枚举每一行,看看每一行中的被竖切成的四个格子,有没有大于等于目标价值,如果是,就横切一刀;如果小于目标价值,那么表格中的最小价值就不是目标价值了,所以就继续往下合并格子(也就是不切当前的那一行),直到切成的四个格子都大于等于目标价值。
>代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n, m, a[80][80], ans, minn, maxn, s[4], sum[80][80], p[5];
bool check (int t)
{
memset (p, 0, sizeof (p));
int c = 0; //累计可以切出来的横切个数
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= 3; j++)
p[j] += sum[i][s[j]] - sum[i][s[j - 1]];
p[4] += sum[i][m] - sum[i][s[3]]; //计算四个格子的价值
if (p[1] >= t && p[2] >= t && p[3] >= t && p[4] >= t)
{
c++;
memset (p, 0, sizeof (p)); //清0数组,继续计算下一行
}
}
if (c < 4) return 0;
return 1;
}
void work ()
{
int l = minn, r = maxn, mid;
while (l < r)
{
mid = (l + r + 1) / 2; //hkydalao说r=mid-1的情况下,mid一般要取(l+r+1)/2
if (check (mid)) l = mid; //如果横切可以切出mid,那么就继续取更大的目标
else r = mid - 1;
}
ans = max (ans, l); //最小价值中最大的那个
}
int main()
{
scanf ("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
scanf("%1d", &a[i][j]); //在jzoj上这样交才能对
sum[i][j] = a[i][j] + sum[i][j - 1]; //每一行的前缀和
minn = min (minn, a[i][j]); //二分中l的取值
maxn += a[i][j]; //二分中r的取值
}
maxn /= 16; //所有格子的和太大了,而且也没有必要,所以就按照hkydalao所说,取一个分成16个格子的平均值
for (s[1] = 1; s[1] <= m - 3; s[1]++)
for (s[2] = s[1] + 1; s[2] <= m - 2; s[2]++)
for (s[3] = s[2] + 1; s[3] <= m - 1; s[3]++) //枚举竖切的位置
work (); //进行二分
printf ("%d", ans);
return 0;
}