... Busqué algunos patrones para construir una reducción de un problema de NPC, pero no encontré una forma de representar un "flujo" con un "tenedor" ...
Entonces (después de un poco de trabajo) este es un algoritmo polinómico ...
ALGORITMO
La lista de inicio puede verse como una matriz de " agujeros " consecutivos . Para cada par inicial ( a j , b j ) , coloque el " elemento " b j en el hoyo número a j . Cada par puede verse como un borde dirigido desde la posición a j hasta la posición b j . Un movimiento consiste en elegir un elemento b j en la posición a j y moverlo a su posición de destino b jnorte∗ 2( aj, bj)sijunajunajsijsijunajsij(el agujero de destino se convierte en una clavija inamovible ). Eliminamos el borde y procedemos a elegir el siguiente movimiento que comenzará desde uno de los dos elementos alcanzables más cercanos desde la posición b j (solo se permiten agujeros entre b j y b k ). Debemos encontrar una secuencia de N movimientos consecutivos.siksijsijsiknorte
Cuando realiza un movimiento, fija una clavija en la posición y la matriz se divide en dos particiones L (izquierda) y R (derecha) y la única forma de ir de L a R (o de R a L ) es usando un borde que salta a través de la clavija. ConjuntobjLRLRRL
- = número de aristas de izquierda a derecha (no cuente la arista final)edgesLR
- = número de aristas de derecha a izquierda (no cuente la arista final)edgesRL
- = e d g e s L R - e d g e s R LflowedgesLR−edgesRL
Casos:
A) si entonces una de las dos particiones se volverá inalcanzable, deténgase|flow|>1
Ahora suponga que , es decir, e n d ∈ Rend>bjend∈R
B) si entonces hay un borde adicional de izquierda a derecha, debe ir a la izquierda (elija el elemento más cercano de L ), de lo contrario nunca llegará a e n dflow=1Lend
C) si entonces hay un borde adicional de derecha a izquierda y cualquier nodo que elija nunca llegará a e n d , pareflow=−1end
D) si debe ir a la derecha (elija el elemento más cercano de R ), de lo contrario nunca llegará a e n dflow=0Rend
Si ( e n d ∈ L ), B, C, D están invertidos.end<bjend∈L
NOTA: cuando se mueve hacia la izquierda o hacia la derecha, debe considerar como una clavija. Por ejemplo, si debe ir a la derecha, pero el elemento más cercano en R es e n d, entonces el movimiento es imposible (y debe proceder con otro par ( s t a r t , e n d ) )endRend(start,end)
Aplica la misma resonancia en cada movimiento.
COMPLEJIDAD
Los flujos sobre cada orificio pueden calcularse previamente en O (N) y reutilizarse en cada exploración.
Los bucles son:
for start = 1 to N
for end = 1 to N
for move = 1 to N
make a move (fix a peg and update flows)
check if another move can be done using flow
No se realizan elecciones durante el cálculo, por lo que la complejidad del algoritmo es O(N3)
CÓDIGO
Esta es una implementación funcional de Java del algoritmo:
public class StrangeSort {
static int PEG = 0xffffff, HOLE = 0x0;
static int M = 0, N = 0, choices = 0, aux = 0, end;
static int problem[][], moves[], edgeflow[], field[];
boolean is_hole(int x) { return x == HOLE; }
boolean is_peg(int x) { return x == PEG; }
boolean is_ele(int x) { return ! is_peg(x) && ! is_hole(x); };
int []cp(int src[]) { // copy an array
int res[] = new int[src.length];
System.arraycopy(src, 0, res, 0, res.length);
return res;
}
/* find the first element on the left (dir=-1) right (dir=1) */
int find(int pos, int dir, int nm) {
pos += dir;
while (pos >= 1 && pos <= M ) {
int x = field[pos];
if ( is_peg(x) || (pos == end && nm < N-1) ) return 0;
if ( is_ele(x) ) return pos;
pos += dir;
}
return 0;
}
void build_edges() {
edgeflow = new int[M+1];
for (int i = 1; i<=M; i++) {
int start = i;
int b = field[start];
if (! is_ele(b)) continue;
if (i == end) continue;
int dir = (b > start)? 1 : -1;
start += dir;
while (start != b) { edgeflow[start] += dir; start += dir; }
}
}
boolean rec_solve(int start, int nm) {
boolean f;
int j;
int b = field[start];
moves[nm++] = b;
if (nm == N) return true;
//System.out.println("Processing: " + start + "->" + field[start]);
field[start] = HOLE;
field[b] = PEG;
int dir = (b > start)? 1 : -1;
int i = start + dir;
while (i != b) { edgeflow[i] -= dir; i += dir; } // clear edge
int flow = edgeflow[b];
if (Math.abs(flow) > 2) return false;
if (end > b) {
switch (flow) {
case 1 :
j = find(b,-1,nm);
if (j <= 0) return false;
return rec_solve(j,nm);
case -1 :
return false;
case 0 :
j = find(b,1,nm);
if (j <= 0) return false;
return rec_solve(j,nm);
}
} else {
switch (flow) {
case -1 :
j = find(b,1,nm);
if (j <= 0) return false;
return rec_solve(j,nm);
case 1 :
return false;
case 0 :
j = find(b,-1,nm);
if (j <= 0) return false;
return rec_solve(j,nm);
}
}
return false;
}
boolean solve(int demo[][]) {
N = demo.length;
for (int i = 0; i < N; i++)
M = Math.max(M, Math.max(demo[i][0], demo[i][1]));
moves = new int[N];
edgeflow = new int[M+1];
field = new int[M+1];
problem = demo;
for (int i = 0; i < problem.length; i++) {
int a = problem[i][0];
int b = problem[i][1];
if ( a < 1 || b < 1 || a > M || b > M || ! is_hole(field[a]) || ! is_hole(field[b])) {
System.out.println("Bad input pair (" + a + "," + b + ")");
return false;
}
field[a] = b;
}
for (int i = 1; i <= M; i++) {
end = i;
build_edges();
if (!is_ele(field[i])) continue;
for (int j = 1; j <= M; j++) {
if (!is_ele(field[j])) continue;
if (i==j) continue;
int tmp_edgeflow[] = cp(edgeflow);
int tmp_field[] = cp(field);
choices = 0;
//System.out.println("START: " + j + " " + " END: " + i);
if (rec_solve(j, 0)) {
return true;
}
edgeflow = tmp_edgeflow;
field = tmp_field;
}
}
return false;
}
void init(int demo[][]) {
}
public static void main(String args[]) {
/**** THE INPUT ********/
int demo[][] = {{4,2},{5,7},{6,3},{10,12},{11,1},{13,8},{14,9}};
/***********************/
String r = "";
StrangeSort sorter = new StrangeSort();
if (sorter.solve(demo)) {
for (int i = 0; i < N; i++) { // print it in clear text
int b = moves[i];
for (int j = 0; j < demo.length; j++)
if (demo[j][1] == b)
r += ((i>0)? " -> " : "") + "(" + demo[j][0] + "," + demo[j][1] + ")";
}
r = "SOLUTION: "+r;
}
else
r = "NO SOLUTIONS";
System.out.println(r);
}
}