다음 문제에서 시퀀스가 ​​다항식 시간으로 존재하는지 확인할 수 있습니까?


27

나는 한동안 다음과 같은 문제에 대해 생각하고 있었고, 다항식 해결책을 찾지 못했습니다. 무차별 적입니다. NP-Complete 문제를 성공적으로 줄이기 위해 노력하고 있습니다.

문제는 다음과 같습니다 .


당신은이 정렬 된 세트 {(A1,B1),(A2,B2),,(An,Bn)} 양수 쌍.

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

다음 작업을 한 쌍에 적용 할 수 있습니다 Swap(pair).. 쌍의 요소를 교환하므로 (10,50)(50,10)

세트의 한 쌍이 교체되면 세트는 자동으로 다시 정렬됩니다 (스왑 된 쌍이 제자리를 벗어나 세트의 해당 위치로 이동 됨).

문제는 일부 쌍에서 시작하여 다음과 같은 조건으로 전체 세트를 교체하는 시퀀스가 ​​있는지 확인합니다.

한 쌍이 교환 된 후 교환 될 다음 쌍은 세트의 후속 또는 선행 쌍이어야합니다.


이 문제에 대한 다항식 시간 솔루션을 찾거나 NP-Complete 문제를 줄이는 것이 좋습니다.

참고 :
이미 결정적인 문제입니다. 시퀀스가 존재하는 경우에만 시퀀스가 ​​어떤 것인지 알고 싶지 않습니다.

쌍을 교체 한 후 집합이 정렬되는 방법의 예

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

첫 번째 쌍을 바꾸면 (5,6) 이되고 집합을 정렬 한 후 (정렬 된 쌍을 새로운 위치에 놓음) 다음과 같이됩니다.

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

(3,4)(7,8)

중요 :
이미 교환 된 쌍을 교환 할 수 없습니다.
일련의 '스왑'작업이있는 경우 모든 쌍의 이름을 한 번만 바꾸어야합니다.

모든 쌍을 교환 할 수없는 예

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


1
(A,B,C)<(A,B,C)A<AA=AB<BA=AB=BC<C)?
mjqxxxx

3
할당 문제는 일반적으로 cstheory.stackexchange.com에서 환영받지 못합니다.
Ito Tsuyoshi

3
흠, 확실하지 않다. 일반적으로 여기서의 논리는 전형적인 숙제 질문에 대답하는 것이 좋은 습관이 아니라는 것입니다. 그렇게하는 것은 미래에 누군가를위한 숙제의 목적을 망칠 것이기 때문입니다. 그러나이 경우 문제는 일반적인 문제 처럼 보이지 않습니다 .
Ito Tsuyoshi

2
"숙제였다"와 다른 동기를 부여하면 사람들이 관심을 가질 수 있고 닫히지 않을 것입니다. 이것의 가능한 응용은 무엇입니까?
Marcos Villagra

2
A={(x1,y1),,(xn,yn)}

답변:


16

... NPC 문제를 줄이기 위해 몇 가지 패턴을 검색했지만 "포크"로 "흐름"을 나타내는 방법을 찾지 못했습니다 ...

그래서 (일부 작업 후) 이것은 다항식 알고리즘입니다 ...

연산

N2(aj,bj)bjajajbjbjajbjbkbjbjbkN

  • (aj,bj)bjajstart

    • (ak,bk),akajakendakbk

      • 에서 일련의 이동을 생성합니다.startend

bjLRLRRL

  • edgesLR
  • edgesRL
  • flowedgesLRedgesRL

사례 :

|flow|>1

이제 이라고 가정하자end>bjendR

flow=1Lend

flow=1end

flow=0Rend

end<bjendL

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);
    }    
}

(a,b)bO(logn)

@mjqxxxx ... 나는 자바 알고리즘 ...에 맞게 전체 답변을 다시 썼다
MARZIO 드 BIASI

@mjqxxxx ... 좋아, 마침내 나는 그것을 얻었다 ... :-)
Marzio De Biasi

2
(a,b)bb(an,bn)ban. 홀수 (짝수)의 점프는 처음에 걸어온 반대쪽 (같은) 쪽을 떠나기 때문에 각 모서리 다음에 걸을 수있는 방향은 하나뿐입니다. 따라서 다항식 시간으로 시작 및 끝 에지의 각 선택을 테스트 할 수 있습니다.
mjqxxxx

1
이것은 아름다운 알고리즘입니다. 마지막 움직임을 먼저 고치는 것은 결코 일어나지 않았습니다. 사소한 점 : (1) mjqxxxx가 기록한대로 end는 a_k 여야합니다. 그렇지 않으면 "end> b_j"조건이 잘못되었습니다. (2)“흐름”의 정의를 무시하거나 B와 C의 경우를 바꿔야합니다.
Tsuyoshi Ito

10

이것은 해결책이 아니라 교환 및 정렬 작업을 명시 적으로 언급하지 않는 재구성입니다. 전체 결합 된 파일 이름 목록과 스왑 된 버전을 정렬하여 시작하고 해당 파일의 색인으로 각 파일 이름을 식별하십시오. 그런 다음 두 파일 사이의 모든 이전 파일 이름이 이미 파기되고 새 파일 이름이 아직 생성되지 않은 경우 두 파일이 인접합니다. 재구성 된 문제는 다음과 같습니다.

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

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

2
+1. 이것은 동등한 문제를 설명하는 훨씬 간단한 방법입니다. 한 가지 설명 만하면됩니다 : 가장자리 (a, b)와 가장자리 (b, a)는 다른 의미를 갖습니다.
이토 쓰요시

@ 츠요시 : 감사합니다; '지시 됨'이라고 편집했습니다.
mjqxxxx

bacabc

@Oleksandr : 여기서 "b는 a와 c 사이입니다"는 "a <b <c 또는 c <b <a"를 의미합니다.
Ito Tsuyoshi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.