jzoj5938. 【NOIP2018模拟10.30】分离计划(二分)

本文解析了NOIP2018模拟赛中的“分离计划”题目,涉及通过二分查找和矩阵操作来寻找最小不稳定值的算法。文章详细介绍了问题背景、输入输出格式、样例说明及数据约束。

5938. 【NOIP2018模拟10.30】分离计划

Description
众所周知,小Z拥有者足以毁灭世界的力量,可惜他不能控制这份力量,小J和小Z的关系十分亲密,一天小J预感到了小Z体内的力量将要爆发。
这次爆发的力量比以往都要强大,以至于将小Z分为了两个整体,彼此之间靠着万有引力互相靠近,一旦融合,世界将不复存在。
为了拯救世界,小J决定打造一个容器G,将小Z的两个部分分别装在容器G的一个部分,用以控制小Z
容器由n*m个魔法水晶组成,他们组成了一个n行m列的矩阵,每个魔法水晶都有自己的能量值,容器需要
被分为两个部分,使得每个魔法水晶都属于且仅属于一个部分,并且任何一个魔法水晶都可以在矩阵中只经过和自己属于同一部分的魔法水晶由一条最多改变一次方向的路径抵达另一个和他处于同一部分的魔法水晶
例如:
AAAAA. .AAAAA. .AAAAA
AABAA. .BAAAA. .AAABB
ABBBA. .BBAAA. .AAABB
AABAA. .BAAAA. .ABBBB
AAAAA. .AAAAA. .BBBBB

…(1)…(2)…(3)…
使用.隔开(辣鸡的题面格式化)
其中12是不合法的容器,3是合法的容器
对于每一个部分,他的不稳定性是属于这个部分的所有魔法水晶能量值的极差(最大-最小)
对于整个容器,不稳定性是两部分不稳定性中的最大值
为了知道自己能不能拯救世界,不白白浪费时间,小J想知道整个容器的最小的不稳定值

Input
第一行两个数n,m代表魔法水晶组成的矩阵大小
随后n行,每行m个整数表示魔法水晶的能量值

Output
一行一个整数,表示最小的不稳定值

Sample Input
4 4
1 12 6 11
11 4 2 14
10 1 9 20
4 17 13 10

Sample Output
11

样例说明
BBBA
BBBA
BBBA
BAAA
B极差12-1=11 A极差20-10=10,不稳定值为11,分法不唯一

Data Constraint
对于15%的数据 n,m≤10
对于另15%的数据,n,m中有一个为1
对于55%的数据 n,m≤200(包括最初的15%)
对于所有数据,n,m≤2000,1≤能量值≤1e9

分析:二分答案,假设最大值在蓝色区域,最小值在红色
区域,考虑如何检验,对于每一行找到红色区域的最大范
围,再在蓝色区域中检验极差,旋转 4 遍

代码

#include <cstdio>
#include <algorithm>
#define N 3000
using namespace std;

int a[N][N],b[N][N],c[N][N],d[N][N];
int n,m,mx,mn;

void change()
{
	for (int i = 1; i <= n; i++)
    	for (int j = 1; j <= m / 2; j++)
      		swap(a[i][j], a[i][m - j + 1]);
}

bool check(int x)
{
	int las = m + 1;
	for (int i = 1; i <= n; i++)
	{
		int t = 0;
		for (int j = 1; j <= min(las, m); j++)
			if (mx - a[i][j] <= x) t = max(t, j);
				else break;
		las = t;
		for (int j = t + 1; j <= m; j++)
			if (a[i][j] - mn > x) return false;
	}
	return true;
}

int find()
{
	int l = 0, r = mx - mn + 1, ans = 0;
	while (l <= r)
	{
		int mid = (l + r) / 2;
		if (check(mid)) ans = mid, r = mid - 1;
			else l = mid + 1;
	}
	change();
	l = 0, r = mx - mn + 1;
	while (l <= r)
	{
		int mid = (l + r) / 2;
		if (check(mid)) ans = min(ans, mid), r = mid - 1;
			else l = mid + 1;
	}
	for (int i = 1; i <= n / 2; i++)
      for (int j = 1; j <= m; j++)
        swap(a[i][j], a[n - i + 1][j]);
	l = 0, r = mx - mn + 1;
	while (l <= r)
	{
		int mid = (l + r) / 2;
		if (check(mid)) ans = min(ans, mid), r = mid - 1;
			else l = mid + 1;
	}
	change();
	l = 0, r = mx - mn + 1;
	while (l <= r)
	{
		int mid = (l + r) / 2;
		if (check(mid)) ans = min(ans, mid), r = mid - 1;
			else l = mid + 1;
	}
	return ans;
}

int main()
{
	freopen("data1.in","r",stdin);
//	freopen("separate.out","w",stdout);
	scanf("%d%d", &n, &m);
	mx = 0, mn = 1e9;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			scanf("%d", &a[i][j]);
			mx = max(mx, a[i][j]);
			mn = min(mn, a[i][j]);
		}
	change();
 	printf("%d", find());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值