我有一个我无法解决的算法设计难题。
这个谜题是这样形成的:有一个数字线上有N个人,每个人可能站在该线上的任何整数上。多人可能站在同一个号码上。对于任何两个人能够相互通信,它们之间的距离应小于K.目标是移动它们,以便每对两个人可以相互通信(可能通过其他人)。换句话说,我们需要移动它们,以便任何相邻的两个人之间的距离小于K.
问题:总移动的最小数量是多少?感觉这就像贪婪的算法族或动态编程。任何提示都表示赞赏!
答案 0 :(得分:1)
我们可以在O(n)
中执行以下操作:
计算将所有人移动到人i
右侧的人员i
的可接受距离的费用:
costRight(A[i]) = costRight(A[i+1]) + (A[i+1] - A[i] - k + 1) * count of people to the right
K = 3; A = { 0, 3, 11, 17, 21}
costRight = {32, 28, 10, 2, 0}
计算将人员i
左侧的所有人移动到可接受距离的人i
的费用:
costLeft(A[i]) = costLeft(A[i-1]) + (A[i] - A[i-1] - k + 1) * count of people to the left
K = 3; A = { 0, 3, 11, 17, 21}
costLeft = { 0, 1, 13, 25, 33}
costRight = {32, 28, 10, 2, 0}
既然我们已从两个方向付费,我们可以在O(n)
:
minCost = min(costRight + costLeft) for all A[i]
minCost = min(32 + 0, 28 + 1, 13 + 10, 25 + 2, 33 + 0) = 23
但有时候这还不够:
K = 3; A = { 0, 0, 1, 8, 8}
carry: -2 -4 3
costLeft = { 0, 0, 0, 11, 11}
carry: -3 5 -2
costRight = { 8, 8, 8, 0, 0}
最佳值既不是11也不是8.通过最大限度的节省来测试当前最佳:
move 1 to 2, cost = 1
K = 3; A = { 0, 0, 2, 8, 8}
carry: -2 -2 -10
costLeft = { 0, 0, 0, 10, 10}
carry: -2 -2
costRight = { 6, 6, 6, 0, 0}
minCost = 1 + min(0 + 6, 0 + 6, 0 + 6, 10 + 0, 10 + 0) = 1 + 6 = 7
不太确定如何有效地进行配方化。
答案 1 :(得分:0)
这是一个用Java编写的贪婪算法,但我不知道它是否在每种情况下都提供了最佳解决方案。此外,它更像是一个概念验证,还有一些优化空间。
这是基于两个邻居不得超过K的事实,下一个邻居不得超过2K,依此类推。在每一步中,我们都会移动“最严重违反这些限制”的人。此计算的详细信息在方法calcForce
中。
package so;
import java.util.Arrays;
public class Main {
public static void main(String args[]) {
int[] position = new int[] {0, 0, 5, 11, 17, 23};
int k = 5;
solve(position, k);
}
private static void solve(int[] position, int k) {
if (!sorted(position)) {
throw new IllegalArgumentException("positions must be sorted");
}
int[] force = new int[position.length];
int steps = 0;
while (calcForce(position, k, force)) {
int mp = -1;
int mv = -1;
for (int i = 0; i < force.length; i++) {
if (mv < Math.abs(force[i])) {
mv = Math.abs(force[i]);
mp = i;
}
}
System.out.printf("move %d to the %s%n", mp, force[mp] > 0 ? "right" : "left");
if (force[mp] > 0) {
position[mp]++;
} else {
position[mp]--;
}
steps++;
}
System.out.printf("total: %d steps%n", steps);
}
private static boolean calcForce(int[] position, int k, int[] force) {
boolean commProblem = false;
Arrays.fill(force, 0);
for (int i = 0; i < position.length - 1; i++) {
for (int j = i + 1; j < position.length; j++) {
int f = position[j] - position[i] - (j - i) * k;
if (f > 0) {
force[i] += f;
force[j] -= f;
commProblem = true;
}
}
}
return commProblem;
}
private static boolean sorted(int[] position) {
for (int i = 0; i < position.length - 1; i++) {
if (position[i] > position[i+1]) {
return false;
}
}
return true;
}
}