Czy można sprawdzić, czy sekwencja istnieje w czasie wielomianowym w następującym problemie?

27

Przez jakiś czas myślałem o następującym problemie i nie znalazłem rozwiązania wielomianowego. Tylko brutalne źródło. Próbowałem też zredukować problem NP-Complete bez powodzenia.

Oto problem :


Masz posortowany zestaw {(A1,B1),(A2,B2),,(An,Bn)} par dodatnich liczb całkowitych.

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

Poniższy operacja może być zastosowana do pary: Swap(pair). Zamienia elementy pary, więc (10,50) stanie się (50,10)

Kiedy para w zestawie zostanie zamieniona, zestaw zostanie automatycznie ponownie posortowany (zamieniona para jest nie na miejscu i zostanie przeniesiona na swoje miejsce w zestawie).

Problem polega na sprawdzeniu, czy istnieje sekwencja, która, zaczynając od jakiejś pary, zamienia cały zestaw, z następującym warunkiem:

Po zamianie pary następna para do zamiany musi być albo następcą, albo poprzednią parą w zestawie.


Byłoby wspaniale znaleźć rozwiązanie problemu wielomianowego w czasie lub zredukować do niego problem NP-Complete.

Uwaga:
to już problem decyzyjny. Nie chcę wiedzieć, która sekwencja jest: tylko jeśli sekwencja istnieje.

Przykład sortowania zestawu po zamianie pary

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

Jeśli zamienię pierwszą parę, staje się to: (5,6) , a po posortowaniu zestawu (umieszczeniu posortowanej pary w nowej pozycji) mamy:

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

Następnie muszę zamienić parę (poprzednik) lub ( 7 , 8 ) (sukcesor) i powtarzać proces, aż wszystkie pary zostaną zamienione (jeśli to możliwe).(3,4)(7,8)

Ważne:
Nie możesz zamienić już zamienionej pary.
Jeśli istnieje sekwencja operacji „zamiany”, wówczas wszystkie pary należy zmienić na raz i tylko raz.

Przykład, w którym nie można zamienić wszystkich par

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

Oscar Mederos
źródło
1
(A,B,C)<(A,B,C)A<AA=AB<BA=AB=BC<C)?
mjqxxxx 31.01.11
3
Problemy z przypisaniem nie są ogólnie mile widziane na cstheory.stackexchange.com.
Tsuyoshi Ito
3
Hmm, nie jestem pewien. Zazwyczaj logika jest taka, że ​​nie jest dobrą praktyką odpowiadanie na typowe pytania dotyczące pracy domowej, ponieważ zrujnuje to cel pracy domowej w przyszłości. Ale w tym przypadku problem nie wygląda jak typowy problem.
Tsuyoshi Ito
2
może jeśli dasz motywację inną niż „to była praca domowa”, ludzie mogą się zainteresować i nie będzie zamknięta. Jakie może być tego zastosowanie?
Marcos Villagra,
2
A={(x1,y1),,(xn,yn)}

Odpowiedzi:

16

... Przeszukałem pewne wzorce, aby zbudować redukcję problemu NPC, ale nie znalazłem sposobu na przedstawienie „przepływu” za pomocą „rozwidlenia” ...

Tak więc (po pracy) jest to algorytm wielomianowy ...

ALGORYTM

N2(aj,bj)bjajajbjbjajbjbkbjbjbkN

  • (aj,bj)bjajstart

    • (ak,bk),akajakendakbk

      • startend

bjLRLRRL

  • edgesLR
  • edgesRL
  • flowedgesLRedgesRL

Skrzynie:

|flow|>1

end>bjendR

flow=1Lend

flow=1end

flow=0Rend

end<bjendL

endRend(start,end)

Zastosuj ten sam rezonans przy każdym ruchu.

ZŁOŻONOŚĆ

Przepływy nad każdym otworem można wstępnie obliczyć w O (N) i użyć ponownie przy każdym skanie.

Pętle to:

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)

KOD

Oto działająca implementacja algorytmu 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);
    }    
}
Marzio De Biasi
źródło
(a,b)bO(logn)
@mjqxxxx ... Przepisałem całą odpowiedź, aby pasowała do algorytmu Java ...
Marzio De Biasi
@mjqxxxx ... ok, w końcu to dostałem ... :-)
Marzio De Biasi
2
(a,b)bb(an,bn)ban. Istnieje wtedy tylko jeden możliwy kierunek przejścia po każdej krawędzi, ponieważ nieparzysta (parzysta) liczba skoków pozostawi cię po przeciwnej (tej samej) stronie, po której początkowo chodziłeś. Dlatego testowanie każdego wyboru krawędzi początkowej i końcowej można wykonać w czasie wielomianowym.
mjqxxxx
1
To jest piękny algorytm. Nigdy nie przyszło mi do głowy, aby najpierw naprawić ostatni ruch. Drobne punkty: (1) Jak napisał mjqxxxx, koniec musi być a_k. W przeciwnym razie warunek „end> b_j” jest niepoprawny. (2) Albo definicja „przepływu” musi zostać zanegowana, albo przypadki B i C muszą zostać zamienione.
Tsuyoshi Ito
10

To nie jest rozwiązanie, ale przeformułowanie, które pozwala uniknąć wyraźnego wzmianki o operacjach wymiany i sortowania. Zacznij od posortowania całej połączonej listy nazw plików i ich zamienionych wersji, a następnie zidentyfikuj każdą nazwę pliku za pomocą indeksu na tej liście. Następnie dwa pliki są sąsiadami, jeśli wszystkie stare nazwy plików między nimi zostały już zniszczone i jeśli żadna z nowych nazw plików między nimi nie została jeszcze utworzona. Przeformułowany problem jest następujący:

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

  • ajbiai+1ji
  • bjbiai+1ji+1
mjqxxxx
źródło
2
+1. Jest to o wiele prostszy sposób określenia równoważnego problemu. Tylko jedno wyjaśnienie: krawędzie (a, b) są skierowane (w tym sensie, że krawędź (a, b) i krawędź (b, a) mają różne znaczenia).
Tsuyoshi Ito
@Tsuyoshi: dzięki; Zredagowałem, żeby powiedzieć „wyreżyserowany”.
mjqxxxx
bacabc
@Oleksandr: Tutaj „b jest między ai c” oznacza „albo a <b <c lub c <b <a.”
Tsuyoshi Ito