HDU 4311&4312 Meeting point-1&2 (曼哈顿距离&&切比雪夫距离)

HDU 4311

题意:平面上有n个点,一个点(x,y)只能到达(x-1,y), (x+1,y), (x, y-1), (x, y+1)4个点。从n个点中找到一点,使其他点到此点的距离之和最小。

思路:

可以发现,两个点间距离为 |x1-x2| + |y1-y2| ,这便是两点间的曼哈顿距离。

朴素的做法是遍历所有点,枚举该点与其他点间的曼哈顿距离之和,但是会TLE;

取巧的做法是将所有点与中心点的曼哈顿距离排序,枚举中间大概250个点左右的情况比较即可(不要欺负人家数据水!

正确姿势:

用结构体数组p[i]保持输入数据x[i],y[i]

将点的x值升序排序求出前i项x和sum1[i],将点的y值升序排序求出前i项y和sum2[i].

求出p[i].x在排序后的x[]中的位置ox,p[i].y在排序后的y[]中的位置oy,则以p[i]为所选中心点的曼哈顿距离之和为

(x_[ox]*(ox-1)-sum1[ox-1]) + (sum1[n]-sum1[ox]-x_[ox]*(n-ox)) + (y_[oy]*(oy-1)-sum2[oy-1]) + (sum2[n]-sum2[oy]-y_[oy]*(n-oy));
这个公式的意思就是有ox-1个点的x值小于p[i],n-ox个大于p[i],故可以消去绝对值符号,将曼哈顿距离的前半部分之和求出来。后半部分同理。

code:

/*
* @author Novicer
* language : C++/C
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
#define INF 2147483647
#define cls(x) memset(x,0,sizeof(x))
#define rise(i,a,b) for(int i = a ; i <= b ; i++)
using namespace std;
const double eps(1e-8);
typedef long long lint;

const int maxn = 100000 + 5;
int n;
lint x_[maxn],y_[maxn];
lint sum1[maxn],sum2[maxn];
struct pt{
	int x,y;
};
pt p[maxn];
void input(){
	cin >> n;
	for(int i = 1 ; i <= n ; i++){
		scanf("%I64d%I64d",&x_[i], &y_[i]);
		p[i].x = x_[i];
		p[i].y = y_[i];
	}
	sort(x_+1,x_+n+1);
	cls(sum1);
	for(int i = 1 ; i <= n ; i++) sum1[i] = sum1[i-1] + x_[i];
	sort(y_+1,y_+n+1);
	cls(sum2);
	for(int i = 1 ; i <= n ; i++) sum2[i] = sum2[i-1] + y_[i];
}
void solve(){
	lint ans = 1e18;
	for(int i = 1 ; i <= n ; i++){
		lint ox  = lower_bound(x_+1 , x_+n+1 , p[i].x) - (x_);
		lint oy  = lower_bound(y_+1 , y_+n+1 , p[i].y) - (y_);
        lint tmp = 0;
        tmp += (x_[ox]*(ox-1)-sum1[ox-1]) + (sum1[n]-sum1[ox]-x_[ox]*(n-ox));
        tmp += (y_[oy]*(oy-1)-sum2[oy-1]) + (sum2[n]-sum2[oy]-y_[oy]*(n-oy));
		ans = min(ans , tmp);
	}
	cout << ans << endl;
}
int main(){
	int t;cin >> t;
	while(t--)	{
		input();
		solve();
	}
	return 0;
}



HDU 4312

题意:平面上有n个点,一个点(x,y)只能到达(x-1,y), (x+1,y), (x, y-1), (x, y+1), (x-1,y+1), (x-1,y-1), (x+1,y+1), (x+1,y-1)8个点。从n个点中找到一点,使其他点到此点的距离之和最小。

思路:可以发现,两个点间距离为 max(|x1-x2|, |y1-y2|),这便是是切比雪夫距离。

我们可以将坐标轴顺时针旋转45度并将所有点的坐标值放大sqrt(2)倍,

切比雪夫距离便是所得到的新坐标系中的曼哈顿距离的二分之一。

然后按照上述题做法直接来即可。

证明如下://证明fromhttp://blog.csdn.net/dgq8211/article/details/7796711

假设有两点(x1,y1), (x2,y2),不妨设 x1>x2。

则Chebyshev距离 D1 = max(|x1-x2|, |y1-y2|)

这两个点对应到新坐标系中的坐标为 (x1-y1, x1+y1), (x2-y2, x2+y2)

某点绕原点逆时针旋转α°(或坐标轴顺时针旋转)后,点(x,y)的坐标会变为(cosα x - sinα y , sinα x + cosα y)。

则Manhattan 距离D2 = |x1-y1-x2+y2| + |x1+y1-x2-y2|

分四种情况讨论:

1.1 y1>y2 && x1-x2>y1-y2

D1 = max(x1-x2, y1-y2) = x1 - x2

D2 = x1-y1-x2+y2 + x1+y1-x2-y2 = 2(x1-x2)

1.2   y1>y2 && x1-x2<=y1-y2

D1 = max(x1-x2,y1-y2) = y1-y2

D2 = -(x1-y1-x2+y2) + x1+y1-x2-y2 = 2(y1-y2)

2.1   y1<=y2 && x1-x2>y2-y1

D1 = max(x1-x2, y2-y1) = x1-x2

D2 = x1-y1-x2+y2 + x1+y1-x2-y2 = 2(x1-x2)

2.2   y1<=y2 && x1-x2<=y2-y1

D1 = max(x1-x2, y2-y1) = y2-y1

D2 = x1-y1-x2+y2 - (x1+y1-x2-y2) = 2(y2-y1)

于是,将Chebyshev距离形式转化成Manhattan距离的形式再求解即可。


code:

/*
* @author Novicer
* language : C++/C
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
#define INF 2147483647
#define cls(x) memset(x,0,sizeof(x))
#define rise(i,a,b) for(int i = a ; i <= b ; i++)
using namespace std;
const double eps(1e-8);
typedef long long lint;

const int maxn = 100000 + 5;
int n;
int x[maxn],y[maxn];
lint sumx[maxn],sumy[maxn];
lint s1,s2;

struct pt{
	int x,y;
}p[maxn];

void input(){
	cin >> n;
	for(int i = 0 ; i < n ; i++){
		scanf("%d%d",&x[i],&y[i]);
		int tmp = x[i];
		x[i] += y[i];
		y[i] = tmp - y[i];
		p[i].x = x[i];p[i].y = y[i];
	}
}
void solve(){
	sort(x,x+n);
	sort(y,y+n);
	cls(sumx);cls(sumy);
	for(int i = 1 ; i <= n ; i++){
		sumx[i] = sumx[i-1] + x[i-1];
		sumy[i] = sumy[i-1] + y[i-1];
	}
	lint ans = 1e18;
	for(int i = 0 ; i < n ; i++){
		lint px = lower_bound(x , x+n , p[i].x) - x;
		lint py = lower_bound(y , y+n , p[i].y) - y;
		lint tmp = 0;
		lint tmpx = (2*px - n)*p[i].x + sumx[n] - 2*sumx[px];
		lint tmpy = (2*py - n)*p[i].y + sumy[n] - 2*sumy[py];
		tmp = tmpx + tmpy;
		ans = min(tmp , ans);
	}
	cout << ans / 2 << endl;
}
int main(){
//	freopen("input.txt","r",stdin);
	int t;cin >> t;
	while(t--)	{
		input();
		solve();
	
	return 0;
}



ps.还有一种距离叫做欧拉距离,就是D = sqrt((x1- x2)^2 + (y1 - y2)^2)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值