(计算几何)Codeforces Round 798 (Div. 2) D. Lena and Matrix

解题思路

这道题是一道很不错的计算几何练习题

首先我们要仔细读题,要求的曼哈顿距离,不是直线距离,这个要小心,如果用了直线距离就不对了,这道题思考的基础就是这个曼哈顿距离

这道题需要找到一个点,这个点到所有黑点的距离中的最远距离要求最小,如果有多个点的最远距离都一样随便输出,那么只要找到一个答案就行了

那避免不了的就是遍历这个图上的每一个点,那暴力检索肯定是不行的,因为黑点的数量也可能很庞大

那如何减少遍历的次数呢?

认真思考,角度清奇的时候你就会发现所有的黑点可以用其中的总数量小于等于4个的黑点来代替,这些黑点肯定能大概围成一个图形,难么这四个点就是左上角、左下角、右上角和右上角,当然如果是个三角形那就可能是三个点,直线的话就可能是两个

这里很容易误导此时此刻的思维,注意,不是用四个点构成的矩形就能围住所有的黑点,这不是一个凸包问题

我们换个角度去想,把其中任何一个点当作原点,它一定有四个象限,也就是大概是四个方向,那么我说的这四个点代表的四个方向就是指,把这个点当作原点,分别四个象限里距离它最远的黑点。

大概说法 如下图:

在这里插入图片描述
红点就是可能遍历到的点,黑点就是所谓的黑点,这里结合我之前的说法可能更容易理解

来点准备

struct Point
{
	int x,y;
	Point(){}
	Point(int x,int y): x(x) , y(y){}
	int add(){return x+y;}
	int sub(){return x-y;}
};

你肯定很好奇为什么要加入add和sub函数

这就脱不开怎么找到这四个点了

左上角的点x + y肯定是最小的,如果不好理解就想象一下矩形
同样右下角的点x - y肯定是最大的

右上角的点x - y肯定是最小的,因为它很有可能x是最大的,y是最小的,也是不好理解联想一下矩形和右上角这个名字

同理,左下角x - y肯定是最大的

那么就可以的得到如下代码俩判断黑点是不是这四个点之一

void check(int i,int j)
{
	//右下角
	if(p[0].x == 0 || p[0].add() < i + j) p[0] = Point(i,j);
	
	//左上角
	if(p[1].x == 0 || p[1].add() > i + j) p[1] = Point(i,j);
	
	//左下角
	if(p[2].x == 0 || p[2].sub() > i - j) p[2] = Point(i,j);
	
	//右上角
	if(p[3].x == 0 || p[3].sub() < i - j) p[3] = Point(i,j);
}

那么其余的只需要按照他说的模拟出来即可

总代码

#include<bits/stdc++.h>
using namespace std;
struct Point
{
	int x,y;
	Point(){}
	Point(int x,int y): x(x) , y(y){}
	int add(){return x+y;}
	int sub(){return x-y;}
}p[4],ans;
int t;
int Dis(int x,int y,Point a)
{
	return abs(a.x - x) + abs(a.y - y);
}
void check(int i,int j)
{
	//右下角
	if(p[0].x == 0 || p[0].add() < i + j) p[0] = Point(i,j);
	
	//左上角
	if(p[1].x == 0 || p[1].add() > i + j) p[1] = Point(i,j);
	
	//左下角
	if(p[2].x == 0 || p[2].sub() > i - j) p[2] = Point(i,j);
	
	//右上角
	if(p[3].x == 0 || p[3].sub() < i - j) p[3] = Point(i,j);
}
void init()
{
	for(int i=0;i<4;i++){
		p[i] = Point(0,0);
	}
}
int main()
{
	cin >> t;
	while (t--){
		init();
		int n,m;
		cin >> n >> m;
		char c;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cin >> c;
				if(c == 'B'){
					check(i,j);
				}
			}
		}
		
		int res = 1e9;
		
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				int o = -1;
				for(int k = 0 ; k < 4; k++){
					o = max(o , Dis(i,j,p[k]));
				}
				
				
				if(res > o){
					res = o;
					ans = Point(i,j);
				}
			}
		}
		
		cout << ans.x << " " << ans.y << endl;
		// cout << endl;
	}
	return 0;
}
  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值