洛谷AT2444 JOIOI 王国题解

题目链接



\color{orange}{亲测可以通过}

题意

J O I O I JOIOI 王国是一个 H H W W 列的长方形网格,每个 1 × 1 1\times1 的子网格都是一个正方形的小区块

为了提高管理效率,我们决定把整个国家划分成两个省 J O I JOI I O I IOI

我们定义,两个同省的区块互相连接,意为从一个区块出发,不用穿过任何一个不同省的区块,就可以移动到另一个区块。有公共边的区块间可以任意移动。

我们不希望划分得过于复杂,因此划分方案需满足以下条件:

  • 区块不能被分割为两半,一半属 J O I JOI 省,一半属 I O I IOI 省。
  • 每个省必须包含至少一个区块,每个区块也必须属于且只属于其中一个省。
  • 同省的任意两个小区块互相连接。
  • 对于每一行/列,如果我们将这一行/列单独取出,这一行/列里同省的任意两个区块互相连接。这一行/列内的所有区块可以全部属于一个省。



    现给出所有区块的海拔,第 i i 行第 j j 列的区块的海拔为 A i , j A_{i,j} 。设 J O I JOI 省内各区块海拔的极差(最大值减去最小值)为 R J O I R_{JOI} I O I IOI 省内各区块海拔的极差为 R I O I R_{IOI} 。在划分后,省内的交流有望更加活跃。但如果两个区块的海拔差太大,两地间的交通会很不方便。 因此,理想的划分方案是 max ( R J O I , R I O I ) \max(R_{JOI}, R_{IOI}) 尽可能小。

    你的任务是求出 max ( R J O I , R I O I ) \max(R_{JOI}, R_{IOI}) 的最小值

思路

首先,根据题意,假设所选取的某一个省图像必须为一个阶梯状,这样才能符合横竖中没有被岔开的。

因为要求是最大的值最小,很显然,可以进行二分答案。

对图中的方差,得到最大值和最小值,答案的方差一定会是在 0 0 ~ m a x m i n max-min 的范围内。

那么分别就设定了 l l r r m i d mid 就是二分的答案。

那么,怎么验证这个 m i d mid 是否合法呢?

这里,我们划分的图像为不上升的阶梯。

假设前一个阶梯划分的位置为 x x ,并且最大值会在被划分的这个阶梯里

那么如果当前阶梯的 m a x m i d max - 任意元素 \le mid 并且 m i n m i d 另一阶梯的任意元素 - min \le mid .

那么当前阶梯的划分位置是在 0 0 ~ x x

当前阶梯的划分位置应该在哪里呢?

因为我们不但要满足当前阶梯满足 m a x m i d max - 任意元素 \le mid

并且另一个阶梯应该满足 m i n m i d 任意元素 - min \le mid

发现当满足当前阶梯时,尽量多的选取一定会让另一个阶梯更容易被满足。

所以当前阶梯的划分位置要尽可能大。
这样得到一个答案后,并不一定是最小的。为什么呢?

因为答案的划分可能是左右或上下的不上升或不下降的。

一开始我们只得到了一种情况的最优解,这时候我们还需要进行 3 3 次旋转

即对于90°, 180°, 和270°的情况进行依次求解取最优的。

代码

#include <cstdio>
#include <cctype>
#include <queue>
#define pk putchar(' ')
#define ph puts("")
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
template <class T>
void rd(T &x)
{
    x = 0;
    ll f = 1;
    char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    x *= f;
}
template <class T>
void pt(T x)
{
    if (x < 0)
        putchar('-'), x = (~x) + 1;
    if (x > 9)
        pt(x / 10);
    putchar(x % 10 ^ 48);
}
template <class T>
T Max(T a, T b)
{
    return a > b ? a : b;
}
template <class T>
T Min(T a, T b)
{
    return a < b ? a : b;
}
template <class T>
T Fabs(T x)
{
    return x < 0 ? -x : x;
}
const int N = 2005, INF = 0x3f3f3f3f;
int a[N][N], h, w;
void Swap(int &x,int &y)
{
	x ^= y;
    y ^= x;
    x ^= y;
}
void Turnh()
{
	for (int i = 1; i <= h; i++)
		for (int j = 1; j <= w / 2; j++)
			Swap(a[i][j], a[i][w - j + 1]);
}
void Turnw()
{
	for (int i = 1; i <= h / 2; i++)
		for (int j = 1; j <= w; j++)
			Swap(a[i][j], a[h - i + 1][j]);
}
int ans = INF, maxx, minn = INF;
bool Check(int x)
{
	int Pre = w + 1;
	for (int i = 1; i <= h; i++)
	{
		int mid = 0;
		for (int j = 1; j <= Min(Pre, w); j++)
			if (maxx - a[i][j] <= x)
				mid = Max(mid, j);
			else 
                break;
		Pre = mid;
		for (int j = mid + 1; j <= w; j++)
			if (a[i][j] - minn > x)
				return 0;
	}
	return 1;
}
int mid_cut()
{
    int l = 0, r = maxx - minn;
	while (l <= r)
	{
		int mid = l + r >> 1;
		if (Check(mid))
			r = mid - 1;
		else 
            l = mid + 1;
	}
    return l;
}
int main()
{
	rd(h), rd(w);
	for (int i = 1; i <= h; i++)
		for (int j = 1; j <= w; j++)
			rd(a[i][j]), maxx = Max(maxx, a[i][j]), minn = Min(minn, a[i][j]);
	ans = Min(ans, mid_cut());
	Turnh();
	ans = Min(ans, mid_cut());
	Turnw();
	ans = Min(ans, mid_cut());
	Turnh();
	ans = Min(ans, mid_cut());
	pt(ans), ph;
    return 0;
}

Thanks!

原创文章 23 获赞 41 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43537070/article/details/103326782