2-7 士兵站队问题
问题描述
在一个划分成网格的操场上,n个士兵散乱地站在网格点上。网格点由整数坐标(x,y)表示。士兵们可以沿网格边上、下、左、右移动一步,但在同一时刻任一网格点上只能有一名士兵。按照军官的命令,士兵们要整齐地列成一个水平队列,即排列(x,y),(x+1,y),…,(x+n-1,y)。如何选择x 和y的值才能使士兵们以最少的总移动步数排成一列。 编程计算使所有士兵排成一行需要的最少移动步数。
分析:中位数原理
Y轴方向
设目标坐标为M,即n个士兵最终需要移动到的Y轴的坐标值为M
n个士兵的Y轴坐标分别为:
Y0,Y1,Y2……Yn−1
Y
0
,
Y
1
,
Y
2
…
…
Y
n
−
1
则最优步数
S=|Y0−M|+|Y1−M|+|Y2−M|+……+|Yn−1−M|
S
=
|
Y
0
−
M
|
+
|
Y
1
−
M
|
+
|
Y
2
−
M
|
+
…
…
+
|
Y
n
−
1
−
M
|
结论:M取中间点的值使得S为最少(最优)(中位数原理)
X轴方向
首先需要对所有士兵的X轴坐标值进行排序
然后,按从左至右的顺序依次移动到每个士兵所对应的“最终位置”(最优),所移动的步数总和就是X轴方向上需要移动的步数。
例如 最左的士兵移动到“最终位置”的最左那位,第二个士兵移动到“最终位置”的第二位,则总的步数为:士兵一移动步数+士兵二移动步数+ …… +士兵n移动步数。
一共n个士兵,他们相应的X轴坐标为:
X0,X1,X2…………Xn−1
X
0
,
X
1
,
X
2
…
…
…
…
X
n
−
1
假设 士兵需要移动到的“最终位置”的X轴坐标值为:k,k+1,k+2 …… …… k+(n-1)
则所求最优步数
S=|X0−k|+|X1−(k+1)|+|X2−(k+2)|+……+|Xn−1−(k+(n−1))|
S
=
|
X
0
−
k
|
+
|
X
1
−
(
k
+
1
)
|
+
|
X
2
−
(
k
+
2
)
|
+
…
…
+
|
X
n
−
1
−
(
k
+
(
n
−
1
)
)
|
经过变形
S=|X0−k|+|(X1−1)−k|+|(X2−2)−k|+……+|(Xn−1−(n−1))−k|
S
=
|
X
0
−
k
|
+
|
(
X
1
−
1
)
−
k
|
+
|
(
X
2
−
2
)
−
k
|
+
…
…
+
|
(
X
n
−
1
−
(
n
−
1
)
)
−
k
|
注意到公式的形式与Y轴方向上一样,同样是n个已知数分别减去一个待定数后取绝对值,然后求和。(中位数原理)
Java
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int soldiers = 5;
int[][] points = {{1, 2}, {2, 2}, {1, 3}, {3, -2}, {3, 3}};
int[] pointX = new int[soldiers];
int[] pointY = new int[soldiers];
for(int i=0; i<soldiers; i++){
pointX[i] = points[i][0];
pointY[i] = points[i][1];
}
//sort point x & y
Arrays.sort(pointX);
Arrays.sort(pointY);
System.out.println("Sorted point x before reform: ");
for(int i=0; i<soldiers; i++){
System.out.println(pointX[i]);
}
//reform
for(int i=0;i<soldiers;i++){
pointX[i] -= i;
}
System.out.println("point x after reform: ");
for(int i=0; i<soldiers; i++){
System.out.println(pointX[i]);
}
//sorted point x after reform
Arrays.sort(pointX);
System.out.println("Sorted point x after reform: ");
for(int i=0; i<soldiers; i++){
System.out.println(pointX[i]);
}
//sorted point y
System.out.println("Sorted point y: ");
for(int i=0; i<soldiers; i++){
System.out.println(pointY[i]);
}
int stepX=0;
int stepY=0;
int medianX, medianY;//中位数
if(soldiers%2 == 1){
medianX = pointX[soldiers/2];
medianY = pointY[soldiers/2];
for(int i=0; i<soldiers; i++){
stepX += Math.abs(pointX[i]-medianX);
stepY += Math.abs(pointY[i]-medianY);
}
}else {
medianX = pointX[soldiers/2] + pointX[soldiers/2-1];
medianX /= 2;
medianY = pointY[soldiers/2] + pointY[soldiers/2-1];
medianY /= 2;
for(int i=0; i<soldiers; i++){
stepX += Math.abs(pointX[i]-medianX);
stepY += Math.abs(pointY[i]-medianY);
}
}
int step = stepX + stepY;
System.out.println("The least steps is: "+step);
}
}
Output
Sorted point x before reform:
1
1
2
3
3
point x after reform:
1
0
0
0
-1
Sorted point x after reform:
-1
0
0
0
1
Sorted point y:
-2
2
2
3
3
The least steps is: 8
Reference
王晓东《计算机算法设计与分析》(第3版)P44

博客探讨了如何解决士兵站队问题,即在网格操场上让士兵们以最小的移动步数排列成水平队列。通过应用中位数原理,分析了在Y轴和X轴方向上的最优解策略,并提供了Java实现的相关内容。
3015

被折叠的 条评论
为什么被折叠?



