... Я искал некоторые шаблоны, чтобы построить сокращение от проблемы NPC, но не нашел способа представить «поток» с помощью «вилки» ...
Итак (после некоторой работы) это полиномиальный алгоритм ...
АЛГОРИТМ
Стартовый список можно рассматривать как массив из последовательных « дырок ». Для каждой начальной пары ( a j , b j ) поместите « элемент » b j в номер отверстия a j . Каждая пара может рассматриваться как направленный край от положения a j до положения b j . Шаг состоит в выборе элемента Ь J в положении J и перемещение его в положение назначения б JN∗2(aj,bj)bjajajbjbjajbj(отверстие назначения становится неподвижным колышком ). Мы удаляем ребро и продолжаем выбирать следующий ход, который начнется с одного из двух ближайших достижимых элементов из позиции b j ( разрешены только отверстия между b j и b k ). Мы должны найти последовательность из N последовательных ходов.bkbjbjbkN
Когда вы делаете ход, вы фиксируете колышек в позиции и массив разбивается на два раздела L (слева) и R (справа), и единственный способ перейти от L к R (или от R к L ) - это использовать край, который прыгает через колышек. ПоставилbjLRLRRL
- = количество ребер слева направо (не считайте конечное ребро)edgesLR
- = количество ребер справа налево (не считайте конечное ребро)edgesRL
- = е д г Ē сек л Р - е д г Е сек R LflowedgesLR−edgesRL
случаи:
А) если то один из двух разделов станет недоступным, остановите|flow|>1
Теперь предположим, что , т.е. e n d ∈ Rend>bjend∈R
B) если то слева слева направо есть дополнительное ребро, вы должны идти налево (выбрать ближайший элемент L ), иначе вы никогда не достигнете e n dflow=1Lend
C) если то есть дополнительное ребро справа налево, и какой бы узел вы ни выбрали, вы никогда не достигнете e n d , остановитесьflow=−1end
D) если вы должны идти направо (выберите ближайший элемент R ), иначе вы никогда не достигнете e n dflow=0Rend
Если ( e n d ∈ L ), B, C, D инвертируются.end<bjend∈L
ПРИМЕЧАНИЕ: при перемещении влево или вправо, вы должны рассматривать как колышек. Например, если вы должны идти прямо, но ближайший элемент на R является е п д , то ход невозможно (и вы должны действовать с другой парой ( ы т г т , е п й ) )endRend(start,end)
Применяйте одно и то же звучание при каждом движении.
СЛОЖНОСТИ
Потоки через каждое отверстие могут быть предварительно рассчитаны в O (N) и использованы повторно при каждом сканировании.
Петли являются:
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
Во время вычислений выбор не сделан, поэтому сложность алгоритма равна O(N3)
КОД
Это рабочая реализация алгоритма на Java:
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);
}
}