SOLDIERS(货舱选址)

该问题探讨了如何在平面上移动n个点,使得它们的横坐标相邻且纵坐标相等,从而达到最小化移动代价的目标。通过对点的横纵坐标分别进行排序和处理,应用货仓选址的策略,找到纵坐标和横坐标使得总移动距离最小的策略,即分别将货仓建在纵坐标中位数和横坐标转换后的中位数位置。

大意:

平面上有 n 个点 , 每个点都可以上下左右移动 , 要使这些点 横坐标相邻且纵坐标相等 , 问花费的最小代价是多少。

前置知识

已知一条数轴上有 N 家商店,它们的坐标分别为 A1−AnA_1-A_nA1An,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小?
结论是把货舱建在 An2+1A_{\frac{n}{2} +1}A2n+1的位置上

思路

横纵坐标分别考虑
对于纵坐标
要使得所有的坐标纵坐标相等,即找一个位置使得所有的 yiy_iyi移动到这个位置所花费的总代价最小,这就是货舱选址的模型,由此纵坐标解决;
对于横坐标
我们假设连续的横坐标的第一个位置为 xtx_txt
那么 n 个位置依次是xt,xt+1…xt+nx_t , x_t+1\dots x_t+nxt,xt+1xt+n
那么由开始位置移动到 n 个位置的花费就是
∣x1−xt∣+∣x2−xt−1∣+⋯+∣xn−xt−n+1∣|x_1 - x_t| + |x_2 - x_t - 1| + \dots +|x_n-x_t - n + 1|x1xt+x2xt1∣++xnxtn+1∣
为了让这个值尽可能小 , 我们可以对x1−xnx_1 - x_nx1xn升序排序,这样让大的跟大的做差,小的跟小的做差,才可以使绝对值的和尽量小
那么怎么找xtx_txt呢?
转化式子
原式 = ∣x1−xt∣+∣(x2−1)−xt∣+⋯+∣(xn−n+1)−xt∣|x_1 - x_t| + |(x_2-1)- x_t | + \dots +|(x_n-n+1)-x_t|x1xt+(x21)xt++(xnn+1)xt
这就又变成了货舱选址的问题
对于转化后的 x 进行排序再次进行货舱选址操作即可

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef long long ll;
const int N = 2e5+10;
const int p = 1e9 + 7;
typedef pair<int,int>PII;
const int inf = 1 << 31 - 1;
const double eps = 1e-9;

int n;
int x[N] , y[N];

int main(){

	IOS
	
	cin >> n;
	for(int i=1;i<=n;i++) cin >> x[i] >> y[i];
	sort(y+1,y+1+n);
	sort(x+1,x+1+n);
	ll ans = 0;
	for(int i=1;i<=n;i++) x[i] -= (i-1);
	sort(x+1,x+1+n);//x 坐标完成转换后要再进行一次排序 , 因为这时候转化后的大小已经不满足升序了
	for(int i=1;i<=n;i++){
		ans += abs(y[i] - y[n/2+1]);
		ans += abs(x[i] - x[n/2+1]);
	}
	
	cout << ans ;
	



	
	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QiWen.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值