题意整理
- 二维海面上有n只船,每只船有一个坐标,以及一个权值。
- 求最小的带权曼哈顿距离。
方法一(求极值)
1.解题思路
简要分析:
首先考虑一个简化版的情况,一维横轴上有n个点,要从这个n个点中,找到一个点,使得其他点移动到这个点的距离之和最小。假设当前处于第x个点的位置(位置p),当前的最小移动距离是dist,如果把这个点向右移动一位(移到p'),则最小移动距离dist变为(位于p左边的所有点到p的距离加一,总共加x,位于p右边的所有点到p的距离减一,总共减)。如果dist是一个关于x的函数,那么dist关于x的一阶导等于,于是x小于时,dist递减,x大于时,dist递增,dist在x等于时取得极小值,由于整个区间只有一个极小值,所以在处dist最小。
当n为奇数时,向下取整,此时要比较处与处dist的大小,显然在处,dist最小;当n为偶数时,dist在与处的值相等。所以区间内最小值一定在处取得。
扩展分析:
明确了上述场景如何求最小距离后,再扩展到本题的情况。第一个扩展条件,本题是带有权值的,只需要将对应的坐标点的个数由1个变为权值数目。第二个扩展条件,本题是二维的,由于是曼哈顿距离,只需要分别计算x轴、y轴的情况,然后相加即可。
基本步骤:
- 初始化点对象集合、权值累加和。
- 将点对象集合按x轴坐标进行排序,寻找x轴上的极小值点对应坐标,根据这个坐标计算其他点到这个点的x轴上的曼哈顿距离。同样的方式,计算y轴上的曼哈顿距离,然后将x轴、y轴的曼哈顿距离相加即可得到所求最小距离。
动图展示:
2.代码实现
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param n int整型 n * @param x int整型一维数组 x * @param y int整型一维数组 y * @param w int整型一维数组 w * @return long长整型 */ public long MinimumDistance (int n, int[] x, int[] y, int[] w) { //初始化点对象集合 int[][] point=new int[n][3]; //初始化权值累加和 long wSum=0; for(int i=0;i<n;i++){ point[i][0]=x[i]; point[i][1]=y[i]; point[i][2]=w[i]; wSum+=w[i]; } //初始化曼哈顿距离 long sum_x=0,sum_y=0; //按横坐标进行排序 Arrays.sort(point,(o1,o2)->o1[0]-o2[0]); //寻找x轴的极小值坐标 long p_x=0; int x_median=0; for(int i=0;i<n;i++){ p_x+=point[i][2]; //如果恰好大于权值累加和一半,即找到对应坐标 if(p_x>wSum/2){ x_median=i; break; } } //计算x轴曼哈顿距离 for(int i=0;i<n;i++){ sum_x+=point[i][2]*Math.abs(point[i][0]-point[x_median][0]); } //按纵坐标进行排序 Arrays.sort(point,(o1,o2)->o1[1]-o2[1]); //寻找y轴的极小值坐标 long p_y=0; int y_median=0; for(int i=0;i<n;i++){ p_y+=point[i][2]; //如果恰好大于权值累加和一半,即找到对应坐标 if(p_y>wSum/2){ y_median=i; break; } } //计算y轴曼哈顿距离 for(int i=0;i<n;i++){ sum_y+=point[i][2]*Math.abs(point[i][1]-point[y_median][1]); } return sum_x+sum_y; } }
3.复杂度分析
- 时间复杂度:排序的时间复杂度是,计算极值点坐标、求曼哈顿距离都是线性的时间复杂度,所以最终的时间复杂度是。
- 空间复杂度:需要额外大小为的point数组,所以空间复杂度是。
方法二(枚举)
1.解题思路
假定已经按照x轴排好了三个点(从左到右分别是A,B,C),那么位于C左边的点到C的距离之和是 ,合并这个项可得,可以看出除了由固定某个点决定,和都是逐项累加的,所以可以用两个变量记录这两个累加项。一轮循环完成之后,只是计算了所有左边的点到当前点的曼哈顿距离,还需考虑右边的点到当前点的曼哈顿距离。还是以C为例,(假设从左到右依次是C,D,E),位于C右边的点到C的距离之和是,合并可得,同样可以通过两个变量记录和,不过要倒序进行循环。
- 初始化点对象集合、x轴和y轴曼哈顿距离。
- 将点对象集合按x轴坐标进行排序,寻找x轴下每一个点的曼哈顿距离,记录在dist数组,然后同样的方式按y轴排序,计算y轴下每一个点的曼哈顿距离。
- 最后遍历所有的点,寻找x轴下最小的曼哈顿距离和y轴下最小的曼哈顿距离,将两者相加。
2.代码实现
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param n int整型 n * @param x int整型一维数组 x * @param y int整型一维数组 y * @param w int整型一维数组 w * @return long长整型 */ public long MinimumDistance (int n, int[] x, int[] y, int[] w) { //初始化点对象集合 int[][] point=new int[n][3]; for(int i=0;i<n;i++){ point[i][0]=x[i]; point[i][1]=y[i]; point[i][2]=w[i]; } //初始化曼哈顿距离 long[][] dist=new long[n][2]; //按横坐标进行排序 Arrays.sort(point,(o1,o2)->o1[0]-o2[0]); //求当前点前面的所有点到它的曼哈顿距离 long pre_x_wSum=0,pre_x_mSum=0; for(int i=0;i<n;i++){ dist[i][0]+=point[i][0]*pre_x_wSum-pre_x_mSum; pre_x_wSum+=point[i][2]; pre_x_mSum+=point[i][0]*point[i][2]; } //求当前点后面的所有点到它的曼哈顿距离 long post_x_wSum=0,post_x_mSum=0; for(int i=n-1;i>=0;i--){ dist[i][0]+=post_x_mSum-point[i][0]*post_x_wSum; post_x_wSum+=point[i][2]; post_x_mSum+=point[i][0]*point[i][2]; } //按纵坐标进行排序 Arrays.sort(point,(o1,o2)->o1[1]-o2[1]); //求当前点前面的所有点到它的曼哈顿距离 long pre_y_wSum=0,pre_y_mSum=0; for(int i=0;i<n;i++){ dist[i][1]+=point[i][1]*pre_y_wSum-pre_y_mSum; pre_y_wSum+=point[i][2]; pre_y_mSum+=point[i][1]*point[i][2]; } //求当前点后面的所有点到它的曼哈顿距离 long post_y_wSum=0,post_y_mSum=0; for(int i=n-1;i>=0;i--){ dist[i][1]+=post_y_mSum-point[i][1]*post_y_wSum; post_y_wSum+=point[i][2]; post_y_mSum+=point[i][1]*point[i][2]; } //计算x轴曼哈顿距离和y轴曼哈顿距离,取两者之和 long sum_x=Long.MAX_VALUE,sum_y=Long.MAX_VALUE; for(int i=0;i<n;i++){ sum_x=Math.min(sum_x,dist[i][0]); sum_y=Math.min(sum_y,dist[i][1]); } return sum_x+sum_y; } }
3.复杂度分析
- 时间复杂度:排序的时间复杂度是,计算x轴和y轴的曼哈顿距离都是线性的时间复杂度,所以最终的时间复杂度是。
- 空间复杂度:需要额外大小为的point数组,与方法一相比,多了dist数组的空间开销,空间复杂度是。