次の問題で多項式時間にシーケンスが存在するかどうかを見つけることは可能ですか?


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完全問題をそれに還元することは素晴らしいことです。

注:
すでに決定の問題です。シーケンスが何であるかを知りたくない:シーケンスが存在する場合のみ。

ペアを交換した後のセットのソート方法の例

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

最初のペアを交換すると、になり、セットをソートした後(ソートされたペアを新しい位置に配置)、次のようになります。(5,6)

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

次に、(先行)ペアまたは(後続)のいずれかを交換し、すべてのペアが交換されるまで(可能な場合)プロセスを繰り返します。7 8 (3,4)(7,8)

重要:
すでに交換されたペアを交換することはできません。
「スワップ」操作のシーケンスがある場合、すべてのペアの名前を一度だけに変更する必要があります。

すべてのペアを交換できない例

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


1
ファイルの名前を変更した後、名前を変更する次のファイルを選択する前に、リストはソートされていますか?あなたは、ソート条件をとして書き換えることができる: IFF()または(と)または( and and)?A < A A = A B < B A = A B = B C < C (A,B,C)<(A,B,C)A<AA=AB<BA=AB=BC<C
mjqxxxx

3
cstheory.stackexchange.comでの割り当ての問題は一般的に歓迎されません。
伊藤剛

3
うーん、わかりません。通常、ここでのロジックは、典型的な宿題の質問に答えることは良い習慣ではないということです。そうすると、将来誰かの宿題の目的が台無しになるからです。しかし、この場合、問題は典型的な問題のようには見えません。
伊藤剛

2
「宿題でした」とは異なる動機を与えると、人々は興味を持ち、閉じられません。これの可能なアプリケーションは何でしょうか?
マルコスヴィラグラ

2
問題の再定式化については、ファイルを忘れてこの方法で確認できます。正の整数のペアのセットがあり、ルールはそれを置いたときと同じです。最初は最初の列でソートされ、次にポイントの名前を変更し始めます。A={(x1,y1),,(xn,yn)}
マルコスビジャグラ

回答:


16

... NPCの問題を軽減するためにいくつかのパターンを検索しましたが、「フォーク」で「フロー」を表す方法が見つかりませんでした...

(ある程度の作業の後)これは多項式アルゴリズムです...

アルゴリズム

開始リストは、連続した「」の配列として表示できます。初期ペアごとに、「要素」を穴番号ます。各ペアは、位置から有向エッジと見なすことができる位置に。この動きは、素子ピッキングからなる位置におよびその先の位置に移動(先孔は不動となるPEG)。エッジを削除し、2つの最も近い到達可能な要素のいずれかから開始する次の移動の選択に進みますa jb jb j a j a j b j b j a j b j b k b j b j b k NN2(aj,bj)bjajajbjbjajbjbk位置からの(と間の穴のみが許可されます)。連続した動きのシーケンスを見つけなければなりません。bjbjbkN

  • 各について、開始要素として(配列位置)を考慮します。b j a j s t a r t(aj,bj)bjajstart

    • 各はを最終的な要素のと見なします(位置から位置までのエッジが最終エッジになります)。a k e n d a k b k(ak,bk),akajakendakbk

      • 次の基準を使用して、要素の到達する(および解決策が見つかるまで)、条件から一連の移動を生成するか、停止条件を生成します。e n dstartend

移動するとき、位置ペグを修正し、配列は2つのパーティション(左)と(右)に分割され、から(またはから)に進む唯一の方法はエッジを使用することですペグを飛び越えます。セット L R L R R LbjLRLRRL

  • edgesLR =左から右へのエッジの数(最終エッジをカウントしない)
  • edgesRL =右から左へのエッジの数(最終エッジはカウントしません)
  • e d g e s L R e d g e s R Lflow =edgesLRedgesRL

事例:

A)if場合、2つのパーティションのいずれかが到達不能になり、停止します|flow|>1

ここで、、つまりと仮定します。 E N D Rend>bjendR

B)の場合その後、左から右に余分なエッジは、あなたが最も近い要素選ぶ(左に行く必要がありますありそれ以外の場合は、あなたが到達することはありません、)L e n dflow=1Lend

C)場合、右から左に余分なエッジがあり、選択したノードはに到達しません。stope n dflow=1end

D)場合、右に移動する(の最も近い要素を選択する)必要があります。そうでない場合は、到達する必要がありますR e n dflow=0Rend

場合()、B、C、Dが反転されます。 E N D Lend<bjendL

注:左または右に移動するときは、をペグと見なす必要があります。たとえば、右に行かなければならないが、の最も近い要素が場合、移動は不可能です(そして、別のペア進む必要があります)R e n d s t a r t e n d 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 ...私は... Javaのアルゴリズムに合わせて、全体の答えを書き直し
マルツィオ・デ・BIASI

@mjqxxxx ... OK、ついに私はそれを手に入れました... :
マルツィオ・デ・

2
(a,b)bb(an,bn)ban。奇数(偶数)のジャンプが最初に歩いた反対側(同じ)側にあなたを残すため、各エッジの後に歩くことができる方向は1つだけです。したがって、開始エッジと終了エッジの各選択のテストは、多項式時間で実行できます。
mjqxxxx

1
これは美しいアルゴリズムです。最初に最後の動きを修正することは私には決して起こりませんでした。軽微な点:(1)mjqxxxxが書いたように、endはa_kでなければなりません。そうでない場合、条件「end> b_j」は間違っています。(2)「フロー」の定義を否定するか、ケースBとCを交換する必要があります。
伊藤剛

10

これは解決策ではなく、スワッピングおよびソート操作の明示的な言及を回避する再定式化です。ファイル名とそれらの交換されたバージョンの結合されたリスト全体をソートすることから始め、そのリスト内のインデックスで各ファイル名を識別します。その後、2つのファイルは、それらの間のすべての古いファイル名が既に破棄されており、それらの間の新しいファイル名がまだ作成されていない場合、隣接しています。再定式化された問題は次のとおりです。

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

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

2
+1。これは、同等の問題を述べるためのはるかに簡単な方法です。ただ1つの説明:エッジ(a、b)は方向付けられています(エッジ(a、b)とエッジ(b、a)が異なる意味を持つという意味で)。
伊藤剛

@剛:ありがとう。「指示」と言うように編集しました。
mjqxxxx

bacabc

@Oleksandr:ここで、「bはaとcの間にある」とは、「a <b <cまたはc <b <aのいずれか」を意味する
伊藤剛
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.