P3145 [USACO16OPEN] Splitting the Field G(题解)

标题

P3145 [USACO16OPEN] Splitting the Field G

题目描述

Farmer John 的 N N N 头奶牛( 3 ≤ N ≤ 50 , 000 3 \leq N \leq 50,000 3≤N≤50,000)位于他二维牧场的不同位置。FJ 想要用一个与 x 轴和 y 轴平行的矩形围栏将所有奶牛围住,并且他希望这个围栏尽可能小,以便它包含每一头奶牛(允许奶牛位于边界上)。

由于上季度牛奶产量低,FJ 的预算紧张。因此,他希望围住更小的区域以减少维护成本,而他唯一能想到的方法就是建造两个围栏而不是一个。请帮助他计算使用两个围栏而不是一个围栏总共可以减少多少面积。与原始围栏一样,这两个围栏必须共同包含所有奶牛(允许奶牛位于边界上),并且它们的边必须与 x x x 轴和 y y y 轴平行。这两个围栏不允许重叠------即使在它们的边界上也不行。注意,零面积的围栏是合法的,例如如果一个围栏的宽度和/或高度为零。

输入格式

输入的第一行包含 N N N。接下来的 N N N 行每行包含两个整数,指定一头奶牛的位置。奶牛的位置是 1 ... 1 , 000 , 000 , 000 1 \ldots 1,000,000,000 1...1,000,000,000 范围内的正整数。

输出格式

输出一个整数,表示 FJ 通过使用两个围栏而不是一个围栏总共可以减少的面积。

输入输出样例 #1

输入 #1

复制代码
6
4 2
8 10
1 1
9 12
14 7
2 3

输出 #1

复制代码
107

核心思路

本题可以简化成在网格图中有 n n n 个点,让你圈出 2 2 2 个矩形,使得这 2 2 2 个矩形面积最小。

1.建立矩阵

题目中指出所有坐标都是正数,于是我们可以 ( 0 , 0 ) (0,0) (0,0) 为锚点,从远到近将所有的点排序。再从第一个点开始枚举,将点的序列分为前后两部分。

a n s i = S 1 , i + S i + 1 , n ans_{i} = S_{1, i} + S_{i + 1, n} ansi=S1,i+Si+1,n

这样分可以将所有的点分成离锚点较近的和距离锚点较远的点,易证没有更优的分法。

2.求矩形面积

通过观察,我们可以发现矩形的长就是这些点中最大的 x x x 的值 减去最小的 x x x 的值 ,宽是这些点中最大的 y y y 的值 减去最小的 y y y 的值

那我们的问题可以转化成区间内求最值

数据范围是 1 \\le n \\le 50000

我们要枚举每一个点,找到每一个点的最大和最小的 x x x 和 y y y 的值,所以需要对查找操作进行优化。这里推荐使用 RMQ 。不懂戳这儿 RMQ 简单介绍

注意事项:

  1. 我们应该将 x x x, y y y 分别排序,取最小值(只对 x x x 排序 80 80 80 分)。
  2. 不开 long long 见祖宗。
  3. RMQ 要设置初始值。
  4. 求出 ans 后要用整个矩形的面积减去 ans。

代码如下:

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
/*
fx[i][j][0] 代表 i - j 区间内 x 的最小值
fx[i][j][1] 代表 i - j 区间内 x 的最大值
fy[i][j][0] 代表 i - j 区间内 y 的最小值
fy[i][j][1] 代表 i - j 区间内 y 的最大值
*/
int n, fx[50001][31][2], fy[50001][30][2], ans = 1e20, t, lx, ly, rx, ry;
struct cow{  //牛的位置
	int x, y;
}a[50001];
bool cmp1(cow a, cow b){  // 以 x 的远近排序
	return a.x < b.x;
}
bool cmp2(cow a, cow b){  // 以 y 的远近排序
	return a.y < b.y;
}
inline int read(){  // 读入优化
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0' || ch>'9'){ if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
void rmq(){
    // 初始化
	for (int i = 0; i <= n; i++){
		for (int j = 0; j <= 30; j++){
			fx[i][j][0] = fy[i][j][0] = 1e20;
		}
	}
	for (int i = 1; i <= n; i++) fx[i][0][0] = fx[i][0][1] = a[i].x, fy[i][0][0] = fy[i][0][1] = a[i].y;

	for (int j = 1; j <= 30; j++){
		for (int i = 1; i + (1 << j) - 1 <= n; i++){
			fx[i][j][0] = min(fx[i][j - 1][0], fx[i + (1 << j - 1)][j - 1][0]);
			fx[i][j][1] = max(fx[i][j - 1][1], fx[i + (1 << j - 1)][j - 1][1]);
			fy[i][j][0] = min(fy[i][j - 1][0], fy[i + (1 << j - 1)][j - 1][0]);
			fy[i][j][1] = max(fy[i][j - 1][1], fy[i + (1 << j - 1)][j - 1][1]);
		}
	}
}
int find(int l, int r){  // l - r 区间内的面积
	t = log2(r - l + 1);
	lx = max(fx[l][t][1], fx[r - (1 << t) + 1][t][1]);
	rx = min(fx[l][t][0], fx[r - (1 << t) + 1][t][0]);
	ly = max(fy[l][t][1], fy[r - (1 << t) + 1][t][1]);
	ry = min(fy[l][t][0], fy[r - (1 << t) + 1][t][0]);
	return (lx - rx) * (ly - ry);
}
signed main() {
	n = read();
	for (int i = 1; i <= n; i++) a[i].x = read(), a[i].y = read();
    // x 
	sort(a + 1, a + 1 + n, cmp1);
	rmq();
	for (int i = 1; i < n; i++){
		int k = find(1, i), q = find(i + 1, n);
		ans = min(ans, k + q);
	}
    // y
	sort(a + 1, a + 1 + n, cmp2);
	rmq();
	for (int i = 1; i < n; i++){
		int k = find(1, i), q = find(i + 1, n);
		ans = min(ans, k + q);
	}

	cout << find(1, n) - ans;  // 输出答案,大面积减去小面积
	return 0;
}
相关推荐
zhouwy11321 小时前
Linux文件系统与IO编程
linux·c++
深邃-1 天前
【数据结构与算法】-二叉树(2):实现顺序结构二叉树(堆的实现),向上调整算法,向下调整算法,堆排序,TOP-K问题
数据结构·算法·二叉树·排序算法·堆排序··top-k
咸鱼2.01 天前
【java入门到放弃】Dubbo
java·开发语言·dubbo
We་ct1 天前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·javascript·算法·leetcode·typescript
JAVA面经实录9171 天前
Java企业级工程化·终极完整版背诵手册(无遗漏、全覆盖、面试+落地通用)
java·开发语言·面试
王老师青少年编程1 天前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·合并果子
周杰伦fans1 天前
AutoCAD .NET 二次开发:深入理解 EntityJig 的工作原理与正确实现
开发语言·.net
叼烟扛炮1 天前
C++第二讲:类和对象(上)
数据结构·c++·算法·类和对象·struct·实例化
天疆说1 天前
【哈密顿力学】深入解读航天器交会最优控制中的Hamilton函数
人工智能·算法·机器学习
wuweijianlove1 天前
关于算法设计中的代价函数优化与约束求解的技术7
算法