[LOJ2334] 「JOI 2017 Final」JOIOI 王国(二分答案)

博客介绍了如何解决JOIOI王国的行政划分问题,要求划分后的两个省JOI和IOI满足特定条件。通过分析得出合法划分必须为阶梯型,采用二分法寻找最小的海拔极差。在每一步中,检查四种阶梯填充策略,并利用贪心算法确定最优解。解决方案的时间复杂度为O(nmlogL),其中L为最大海拔与最小海拔之差。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

JOIOI 王国是一个 HHHWWW 列的长方形网格,每个 1×11\times 11×1 的子网格都是一个正方形的小区块。为了提高管理效率,我们决定把整个国家划分成两个省 JOI 和 IOI 。

我们定义,两个同省的区块互相连接,意为从一个区块出发,不用穿过任何一个不同省的区块,就可以移动到另一个区块。有公共边的区块间可以任意移动。
我们不希望划分得过于复杂,因此划分方案需满足以下条件:

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

现给出所有区域的海拔,你需要求出 m a x max max(JOI省海拔极差,IOI省海拔极差)的最小值。

首先观察题目,发现合法的划分一定是阶梯型,考虑二分答案,我们发现最大值和最小值不在同一块。

那么我们考虑两种阶梯形的情况,每次分别填入最大值和最小值一共四种情况,判定的时候我们可以利用阶梯长度递减的性质,贪心找到一个最优的阶梯,每次四种情况一起判定 如果一种可行那么当前答案就可行。

复杂度 O ( n m log ⁡ L ) , L = m a x − m i n O(nm\log L),L = max - min O(nmlogL),L=maxmin

#include<bits/stdc++.h>

using namespace std;

int read()
{
	int _ = 0, __ = getchar(), ___ = 1; 
	for(; !isdigit(__); __ = getchar()) if(__ == '-') ___ = -1;
	for(; isdigit(__); __ = getchar()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
	return _ * ___;
}

const int N = 2000 + 7;

int premax[N][N], sufmax[N][N], premin[N][N], sufmin[N][N];
int tmp[N], n, m, mn = tmp[0] = 2e9, mx = -2e9;

bool check1(int lim)
{
	int mnlim = mn + lim, mxlim = mx - lim, lst = m;
	for(int i = 1; i <= n; ++ i)
	{
		tmp[i] = 0;
		for(int j = 1; j <= m; ++ j)
			if(premax[i][j] <= mnlim) 
				tmp[i] = j; 
	}
	for(int i = n; i >= 1; -- i)
	{
		lst = min(tmp[i], lst);
		if(sufmin[i][lst + 1] < mxlim)
			return false;
	}
	return true;
}

bool check2(int lim)
{
	int mnlim = mn + lim, mxlim = mx - lim, lst = m;
	for(int i = 1; i <= n; ++ i)
	{
		tmp[i] = 0;
		for(int j = 1; j <= m; ++ j)
			if(premin[i][j] >= mxlim) 
				tmp[i] = j; 
	}
	for(int i = n; i >= 1; -- i)
	{
		lst = min(tmp[i], lst);
		if(sufmax[i][lst + 1] > mnlim)
			return false;
	}
	return true;
}

bool check3(int lim)
{
	int mnlim = mn + lim, mxlim = mx - lim, lst = m;
	for(int i = 1; i <= n; ++ i)
	{
		tmp[i] = 0;
		for(int j = 1; j <= m; ++ j)
			if(premax[i][j] <= mnlim) 
				tmp[i] = j; 
	}
	for(int i = 1; i <= n; ++ i)
	{
		lst = min(tmp[i], lst);
		if(sufmin[i][lst + 1] < mxlim)
			return false;
	}
	return true;
}

bool check4(int lim)
{
	int mnlim = mn + lim, mxlim = mx - lim, lst = m;
	for(int i = 1; i <= n; ++ i)
	{
		tmp[i] = 0;
		for(int j = 1; j <= m; ++ j)
			if(premin[i][j] >= mxlim) 
				tmp[i] = j; 
	}
	for(int i = 1; i <= n; ++ i)
	{
		lst = min(tmp[i], lst);
		if(sufmax[i][lst + 1] > mnlim)
			return false;
	}
	return true;
}

bool check(int lim)
{
	if(check1(lim) || check2(lim) || check3(lim) || check4(lim))
		return true;
	return false;
}

int main() 
{
	//freopen("separate.in", "r", stdin);
	//freopen("separate.out", "w", stdout);

	n = read(), m = read();
	for(int i = 1; i <= n; ++ i)
	{
		for(int j = 1; j <= m; ++ j)
		{
			premax[i][j] = premin[i][j] = sufmax[i][j] = sufmin[i][j] = read();
			if(premax[i][j] > mx) 
				mx = premax[i][j];
			if(premin[i][j] < mn)
				mn = premin[i][j];
		}
		sufmin[i][m + 1] = 2e9;
	}
	for(int i = 1; i <= n; ++ i)
	{
		for(int j = 2; j <= m; ++ j)
		{
			premax[i][j] = max(premax[i][j - 1], premax[i][j]);
			premin[i][j] = min(premin[i][j - 1], premin[i][j]);
		}
		for(int j = m; j >= 2; -- j)
		{
			sufmax[i][j - 1] = max(sufmax[i][j], sufmax[i][j - 1]);
			sufmin[i][j - 1] = min(sufmin[i][j], sufmin[i][j - 1]);
		}
	}

	int l = 0, r = mx - mn, ans;
	while(l <= r) 
	{
		int mid = (l + r) >> 1;
		if(check(mid)) 
			ans = mid, r = mid - 1;
		else 
			l = mid + 1;
	}

	printf("%d\n", ans);

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值