È possibile scoprire se esiste una sequenza in tempo polinomiale nel seguente problema?


27

Sto pensando al seguente problema da un po 'di tempo e non ho trovato una soluzione polinomiale per questo. Solo fonte bruta. Ho cercato di ridurre anche un problema NP-Complete senza successo.

Ecco il problema :


Hai un set ordinato {(A1,B1),(A2,B2),,(An,Bn)} di coppie di numeri interi positivi.

(Ai,Bi)<(Aj,Bj)Ai<Aj(Ai=AjBi<Bj) (Ai,Bi)=(Aj,Bj)Ai=AjBi=Bj

La seguente operazione può essere applicata ad una coppia: Swap(pair). Scambia gli elementi della coppia, quindi (10,50) diventerà (50,10)

Quando una coppia nel set viene scambiata, il set viene automaticamente riordinato (la coppia scambiata è fuori posto e verrà spostata al suo posto nel set).

Il problema consiste nel vedere se esiste una sequenza che, a partire da una coppia, scambia l'intero set, con la seguente condizione:

Dopo che una coppia viene scambiata, la coppia successiva da scambiare deve essere la coppia successiva o quella precedente nel set.


Sarebbe bello trovare una soluzione temporale polinomiale a questo problema, o una riduzione di un problema NP-Complete in esso.

Nota:
è già un problema decisionale. Non voglio sapere quale sia la sequenza: solo se esiste una sequenza.

Esempio di come il set viene ordinato dopo aver scambiato una coppia

(6, 5)
(1,2)
(3,4)
(7,8)

Se cambio la prima coppia, diventa: (5,6) e dopo aver ordinato il set (posizionando la coppia ordinata nella sua nuova posizione), abbiamo:

(1,2)
(3,4)
(5,6)
(7,8)

Quindi devo scambiare la coppia (predecessore) o ( 7 , 8 ) (successore) e ripetere il processo fino a quando tutte le coppie non vengono scambiate (se possibile).(3,4)(7,8)

Importante:
non è possibile scambiare una coppia già scambiata.
Se esiste una sequenza di operazioni di "scambio", tutte le coppie devono essere rinominate una volta e una sola volta.

Esempio in cui non è possibile scambiare tutte le coppie

( 1 , 4 ) ( 3 , 2 ) ( 5 , 5 )(0,0)
(1,4)
(3,2)
(5,5)


1
L'elenco è ordinato dopo aver rinominato il file e prima di scegliere il file successivo da rinominare? Riesci a riscrivere la condizione di ordinamento come: iff ( A < A ) o ( A = A e B < B ) o ( A = A E B = B e C < C (A,B,C)<(A,B,C)A<AA=AB<BA=AB=BC<C)?
mjqxxxx,

3
I problemi di assegnazione non sono ben accetti su cstheory.stackexchange.com in generale.
Tsuyoshi Ito,

3
Hmm, non ne sono sicuro. Di solito la logica qui è che non è una buona pratica rispondere alle domande tipiche dei compiti a casa perché farlo rovinerà lo scopo dei compiti a casa in futuro. Ma in questo caso, il problema non sembra un problema tipico .
Tsuyoshi Ito,

2
forse se dai una motivazione diversa da "era un compito a casa", la gente potrebbe interessarsi e non sarà chiusa. Quale potrebbe essere una possibile applicazione di questo?
Marcos Villagra,

2
sulla riformulazione del problema, puoi dimenticare i file e vederlo in questo modo. Hai un insieme di coppie di numeri interi positivi e le regole sono le stesse che hai messo. Inizialmente viene ordinato nella prima colonna, quindi si inizia a rinominare i punti. A={(x1,y1),,(xn,yn)}
Marcos Villagra,

Risposte:


16

... Ho cercato alcuni schemi per creare una riduzione da un problema NPC, ma non ho trovato il modo di rappresentare un "flusso" con un "fork" ...

Quindi (dopo qualche lavoro) questo è un algoritmo polinomiale ...

ALGORITMO

L'elenco di partenza può essere visualizzato come una matrice di " fori " consecutivi . Per ogni coppia iniziale ( a j , b j ) , metti l '" elemento " b j nel foro numero a j . Ogni coppia può essere vista come un bordo diretto dalla posizione a j alla posizione b j . Una mossa consiste nel selezionare un elemento b j nella posizione a j e spostarlo nella posizione di destinazione b jN2(aj,bj)bjajajbjbjajbj(il foro di destinazione diventa un piolo immobile ). Eliminiamo il bordo e procediamo a scegliere la mossa successiva che inizierà da uno dei due elementi raggiungibili più vicini dalla posizione b j ( sono ammessi solo fori tra b j e b k ). Dobbiamo trovare una sequenza di N mosse consecutive.bkbjbjbkN

  • Per ciascuno considera b j (nella posizione dell'array a j ) come l'elemento iniziale s t a r t .(aj,bj)bjajstart

    • Per ciascuno considera a k come elemento finale e n d (il bordo da posizione a k a posizione b k sarà il bordo finale).(ak,bk),akajakendakbk

      • generare una sequenza di movimenti da utilizzando i seguenti criteri fino elemento e n d (e una soluzione è stata trovata), oppure una condizione di arrestostartend

Quando fai una mossa, fissi un piolo nella posizione e l'array viene diviso in due partizioni L (sinistra) e R (destra) e l'unico modo per passare da L a R (o da R a L ) è usare un bordo che salta attraverso il piolo. ImpostatobjLRLRRL

  • = numero di bordi da sinistra a destra (non contare il bordo finale)edgesLR
  • = numero di bordi da destra a sinistra (non contare il bordo finale)edgesRL
  • flowedgesLRedgesRL

casi:

|flow|>1

end>bjendR

flow=1Lend

flow=1end

flow=0Rend

end<bjendL

endRend(start,end)

Applica la stessa risonanza ad ogni mossa.

COMPLESSITÀ

I flussi su ciascun foro possono essere precalcolati in O (N) e riutilizzati ad ogni scansione.

I loop sono:

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)

CODICE

Questa è un'implementazione Java funzionante dell'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);
    }    
}

(a,b)bO(logn)

@mjqxxxx ... Ho riscritto l'intera risposta per abbinare l'algoritmo Java ...
Marzio De Biasi

@mjqxxxx ... ok, finalmente l'ho capito ... :-)
Marzio De Biasi,

2
(a,b)bb(an,bn)ban. C'è quindi una sola direzione possibile da percorrere dopo ogni fronte, poiché un numero dispari (pari) di salti ti lascerà sul lato opposto (stesso) a cui inizialmente hai camminato. Pertanto, ogni test dei bordi iniziale e finale può essere eseguito in tempi polinomiali.
mjqxxxx,

1
Questo è un bellissimo algoritmo. Non mi è mai venuto in mente di fissare prima l'ultima mossa. Punti minori: (1) Come ha scritto mjqxxxx, end deve essere a_k. Altrimenti la condizione "end> b_j" è errata. (2) O la definizione di "flusso" deve essere negata, oppure i casi B e C devono essere scambiati.
Tsuyoshi Ito,

10

Questa non è una soluzione, ma una riformulazione che evita la menzione esplicita delle operazioni di scambio e smistamento. Inizia ordinando l'intero elenco combinato di nomi di file e le loro versioni scambiate e identifica ogni nome di file con il suo indice in quell'elenco. Quindi due file sono vicini se tutti i vecchi nomi di file tra loro sono già stati distrutti e se nessuno dei nuovi nomi di file tra loro è stato ancora creato. Il problema riformulato è il seguente:

n(a,b)a,b{1,2,,2n}(a1,b1),(a2,b2),...,(an,bn)

  • ajbiai+1ji
  • bjbiai+1ji+1

2
+1. Questo è un modo molto più semplice per affermare il problema equivalente. Solo un chiarimento: i bordi (a, b) sono diretti (nel senso che il bordo (a, b) e il bordo (b, a) hanno significati diversi).
Tsuyoshi Ito,

@Tsuyoshi: grazie; Ho modificato per dire "diretto".
mjqxxxx,

bacabc

@Oleksandr: Qui "b è tra a e c" significa "o a <b <c oppure c <b <a."
Tsuyoshi Ito
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.