... Saya mencari beberapa pola untuk membuat pengurangan dari masalah NPC, tetapi tidak menemukan cara untuk mewakili "aliran" dengan "garpu" ...
Jadi (setelah beberapa pekerjaan) ini adalah algoritma polinomial ...
ALGORITMA
Daftar awal dapat dilihat sebagai larik " lubang " berurutan . Untuk setiap pasangan awal ( a j , b j ) , letakkan " elemen " b j pada nomor lubang a j . Setiap pasangan dapat dilihat sebagai tepi diarahkan dari posisi suatu j ke posisi b j . Sebuah langkah terdiri dalam memilih sebuah elemen b j pada posisi yang j dan pindah ke nya posisi tujuan b jN∗2(aj,bj)bjajajbjbjajbj(lubang tujuan menjadi pasak tidak bergerak ). Kami menghapus tepi, dan lanjutkan untuk memilih langkah selanjutnya yang akan dimulai dari salah satu dari dua unsur dicapai terdekat dari posisi b j (hanya lubang antara b j dan b k diperbolehkan). Kami harus menemukan urutan gerakan N berturut-turut.bkbjbjbkN
Ketika Anda bergerak, Anda mematok pasak pada posisi dan array dibagi menjadi dua partisi L (kiri) dan R (kanan) dan satu-satunya cara untuk beralih dari L ke R (atau dari R ke L ) menggunakan tepi yang melompat di pasak. SetbjLRLRRL
- = jumlah tepi dari kiri ke kanan (jangan hitung tepi akhir)edgesLR
- = jumlah tepi dari kanan ke kiri (jangan hitung tepi akhir)edgesRL
- = e d g e s L R - e d g e s R LflowedgesLR−edgesRL
Kasus:
A) jika maka salah satu dari dua partisi akan menjadi tidak terjangkau, berhenti|flow|>1
Sekarang anggaplah bahwa , yaitu e n d ∈ Rend>bjend∈R
B) jika maka ada keunggulan tambahan dari kiri ke kanan, Anda harus pergi kiri (memilih elemen terdekat dari L ), jika tidak, anda tidak akan pernah mencapai e n dflow=1Lend
C) jika maka ada tepi ekstra dari kanan ke kiri dan simpul apa pun yang Anda pilih, Anda tidak akan pernah mencapai e n dflow=−1end , berhenti
D) jika Anda harus pergi ke kanan (memilih elemen terdekat dari R ), jika tidak Anda akan neve jangkauan e n dflow=0Rend
Jika ( e n d ∈ L ), B, C, D terbalik.end<bjend∈L
CATATAN: saat bergerak ke kiri atau ke kanan, Anda harus mempertimbangkan sebagai pasak. Misalnya, jika Anda harus berbelok ke kanan, tetapi elemen terdekat pada R adalah e n d maka langkah tersebut tidak mungkin (dan Anda harus melanjutkan dengan pasangan lain ( s t a r t , e n d ) )endRend(start,end)
Terapkan pengubahan ulang yang sama di setiap gerakan.
KOMPLEKSITAS
Aliran pada setiap lubang dapat dihitung ulang dalam O (N) dan digunakan kembali pada setiap pemindaian.
Loop adalah:
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
Tidak ada pilihan yang dibuat selama perhitungan, sehingga kompleksitas algoritma adalah O(N3)
KODE
Ini adalah implementasi algoritma Java yang berfungsi:
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);
}
}