5

私は次の問題を抱えています (私のバージョンでは、おそらく問題を解決しやすくするいくつかの制約がありますが、一般的な解決策も良いでしょう):

10 エントリの 4 つのリストがあります。最初のリストには 0 ~ 6 の整数エントリが含まれ、他の 3 つのリストには 0 ~ 3 のエントリが含まれます。これら 4 つのリストの要素の最適な組み合わせを 100 個見つける必要があります。1 つの組み合わせは、各リストから 1 つずつ、4 つの値の合計で構成されます。結果の要素の値を知る必要があるだけでなく、それらがどのように構成されたかについても知る必要があることに注意してください。値に関連する情報は他にもあるからです。

例 1:

list A: 6, 3, 2, 2, 1, 0, 0, 0, 0
list B: 3, 2, 0, 0, 0, 0, 0, 0, 0
list C: 2, 2, 0, 0, 0, 0, 0, 0, 0
list D: 3, 1, 1, 1, 1, 1, 0, 0, 0

この場合、5 つの最適な組み合わせは次のようになります。

  • 14 (A[0] + B[0] + C[0] + D[0])
  • 14 (A[0] + B[0] + C[1] + D[0])
  • 13 (A[0] + B[1] + C[0] + D[0])
  • 13 (A[0] + B[1] + C[1] + D[0])
  • 12 (A[0] + B[0] + C[0] + D[1])

これはおそらく、この問題を解決するほとんどのアルゴリズムの最初のステップになるため、リストのエントリを並べ替えたことに注意してください。

些細な解決策

最も簡単な解決策は、10,000 通りの可能な組み合わせをすべて形成し、その中から 100 のベストを選択することです。10,000 の可能な組み合わせを並べ替える必要さえありません。最初に配列をスキャンして、どの値がどのくらいの頻度で表示されるかを分析できます。次に、次の値のスキャンで 100 の最良の値 (およびそれらのさらなる属性) を選択できます。

うまくいかない解決策

私の頭に浮かんだ別のアイデアは次のとおりです。まず、リストをソートする必要があります。各リストで、ソリューションに貢献できるエントリとそうでないエントリを区切るインデックスを見つけたいと思います。例 1 で 4 つの最適な組み合わせを見つけなければならない場合、たとえば、リストBとの最初の 2 つの要素を選択し、リストとの最初の要素を選択するCと、次の結果 が得られます。AD

A: 6
B: 3, 2
C: 3, 2
D: 3

これらのサブリストのすべての組み合わせにより、最適なソリューションが得られます。ただし、次の例に示すように、これが常に可能であるとは限りません。

例 2:

(今回は2つのリストのみ)

list A: 6, 5, 5, 0, 0, ...
list B: 3, 1, 1, 0, 0, ...

ここで、最良の 4 つのソリューションは次のとおりです。

  • 6 + 3
  • 5 + 3
  • 5 + 3
  • 6 + 1

ただし、この解決策は、他のすべての組み合わせから 4 つの組み合わせを分離するインデックスでは見つかりません。

4

5 に答える 5

3

(Evgeny Kluev、solendilによる)既存の回答をより簡潔に言い換えてみましょう。

解決策は、構成(0,0,0,0)から開始する最良優先探索です。検索ツリーの分岐係数は4(リストの数)です。

(0,0,0,0)で、次善の構成は(1,0,0,0)、(0,1,0,0)、(0,0,1,0)のいずれかであることがわかります。または(0,0,0,1)。したがって、検索ツリーのこれらの「リーフ」を、各構成がどれだけ優れているかでソートされた優先キューに入れます。リーフのキューは、次善の構成候補のキューです。

次に、キューから最適な構成を取り出して回答リストに追加し、キューを更新します。たとえば、(0,0,1,0)が次に最適なものである場合は、キューから取り出して、その子を追加します-(1,0,1,0)、(0,1,1,0)、 (0,0,2,0)、(0,0,1,1)-キューへ。

于 2012-11-12T16:16:37.540 に答える
2

このソリューションでは、次の 2 つのデータ構造を使用します。

  1. リスト要素の合計がキーで、リスト インデックスのタプルが値である優先キュー。
  2. リスト インデックスのタプルを含むハッシュ セット。

アルゴリズム:

  1. すべてのリストを並べ替えます。
  2. すべてのリストの最初の要素を含むタプルをキューに入れます。
  3. ハッシュ セットに含まれるタプルが 100 未満の場合は、手順 4 と 5 を実行します。
  4. キューから最大のアイテムをポップし、ハッシュ セットに対応するインデックス タプル (T) が含まれているかどうかを確認します。そのようなタプルが見つかった場合は、ステップ 4 を繰り返します。そうでない場合は、ステップ 5 に進みます。
  5. タプル T をハッシュ セットに挿入します。N 個のタプル (N はリストの数) を作成します。それぞれのタプルは、T の対応するインデックスに等しい N-1 個のインデックスと、1 つ大きいインデックスを 1 つ持ち、これらのタプルをキューに挿入します。

おそらく、(この問題に対して) Priority キューを実装する最良の方法は、最初にリスト要素の合計でソートされ、次にリスト インデックスでソートされた、順序付けられたコンテナー (バイナリ サーチ ツリーまたはスキップ リスト) を使用することです。このコンテナーは重複がキューに追加される前に重複を検出できるため、別のハッシュ セットは必要ありません。

于 2012-11-12T13:28:55.373 に答える
2

私の回答の前提条件: 解は 4 桁の数字で表されます。0000(14) は、スコア 14 に対して、各リストの最初の要素を取得するソリューションを表します。 7。

ここで、0000(14) で始まるツリーを作成し、4 桁の数字をそれぞれ 1 ずつ増やしてすべての葉を見つけます (つまり、1000(11)、0100(13)、0010(14)、および 0001(12))。最良の合計は 14 で、0010(14) が分岐 (解の一部) になり、その葉、つまり 1010(11)、0110(13)、0020(12)、および 0011(12) を計算します。すべての葉の最適な合計は 13 です。これらの葉を枝として設定し、100 個のソリューションが成長するまで葉を計算します。

この方法では、サンプルの問題を 20 ステップで解決し、102 個の枝と 188 個の葉が計算されます。

ここにあなたの問題を解決する(粗い)Javaプログラムがあります。

package HighestSums;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class HighestSums {

static int[][] data = {
                {6, 3, 2, 2, 1, 0, 0, 0, 0, 0},
                {3, 2, 0, 0, 0, 0, 0, 0, 0, 0},
                {2, 2, 0, 0, 0, 0, 0, 0, 0, 0},
                {3, 1, 1, 1, 1, 1, 0, 0, 0, 0}
};

static Set<Solution> branches = new HashSet<Solution>();
static Set<Solution> leaves = new HashSet<Solution>();

public static void main(String[] args) {
    Solution s = new Solution();
    leaves.add(s);
    int sum = s.getSum();

    for (int i=0; i<100; i++) {
        System.out.println("======== STEP " + i);
        System.out.println("-- Nb Branches : " + branches.size());
        System.out.println("-- Nb Leaves : " + leaves.size());
        if (branches.size()>100)
            return;
        sum = max(leaves);
        step(sum);
        System.out.println("Sum : " + sum);
        System.out.println("Res\n" + toStr(branches));
        System.out.println("Analyse\n" + toStr(leaves));
    }
}

private static int max(Set<Solution> analyse2) {
    List<Solution> disp = new ArrayList<HighestSums.Solution>();
    disp.addAll(analyse2);
    Collections.sort(disp);
    return disp.get(0).getSum();
}

private static String toStr(Collection<Solution> l) {
    List<Solution> disp = new ArrayList<HighestSums.Solution>();
    disp.addAll(l);
    Collections.sort(disp);
    String res = "";
    for (Solution s : disp)
        res += s.toString()+"\n";
    return res;
}

private static void step(int sum) {
    List<Solution> created = new ArrayList<Solution>();
    for (Iterator<Solution> it = leaves.iterator(); it.hasNext(); ) {
        Solution a = it.next();
        if (a.getSum()==sum) {
            it.remove();
            branches.add(a);
            for (Solution a2 : a.next()) {
                if (branches.contains(a2) || leaves.contains(a2))
                    continue;
                created.add(a2);
            }
        }
    }
    leaves.addAll(created);
}

static class Solution implements Comparable<Solution>{
    int[] ix = new int[4];
    Solution parent;
    public Solution(Solution sol) {
        System.arraycopy(sol.ix, 0, ix, 0, sol.ix.length);
        parent = sol;
    }
    public Solution() {}
    public String toString() {
        String res = "";
        for (int i=0; i<4; i++) 
            res += ix[i]; 
        res += " " + getSum(); 
        if (parent != null) 
            res += " (" + parent.toString() + ")";
        return res;
    }
    public List<Solution> next() {
        List<Solution> res = new ArrayList<Solution>();
        for (int i=0; i<4; i++) {
            if (ix[i]<9) {
                Solution s2 = new Solution(this);
                s2.ix[i]+=1;
                res.add(s2);
            }
        }
        return res;
    }
    private int getSum() {
        int res = 0;
        for (int i=0; i<4; i++) 
            res += data[i][ix[i]]; 
        return res;
    }
    @Override
    public boolean equals(Object obj) {
        Solution s = (Solution)obj;
        for (int i=0; i<4; i++) 
            if (ix[i]!=s.ix[i])
                return false;
        return true;
    }
    @Override
    public int hashCode() {
        return ix[0]+ix[1]*10+ix[2]*100+ix[3]*1000;
    }
    @Override
    public int compareTo(Solution o) {
        return o.getSum() - getSum();
    }
}

}

今の解決策(102回の出現すべて):

0000 14
0010 14 (0000 14)
0100 13 (0000 14)
0110 13 (0010 14 (0000 14))
0011 12 (0010 14 (0000 14))
0003 12 (0002 12 (0001 12 (0000 14)))
0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))
0030 12 (0020 12 (0010 14 (0000 14)))
0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))
0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))
0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))
0002 12 (0001 12 (0000 14))
0012 12 (0011 12 (0010 14 (0000 14)))
0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))
0020 12 (0010 14 (0000 14))
0001 12 (0000 14)
0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))
0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
1000 11 (0000 14)
0200 11 (0100 13 (0000 14))
0111 11 (0110 13 (0010 14 (0000 14)))
0180 11 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0300 11 (0200 11 (0100 13 (0000 14)))
0130 11 (0030 12 (0020 12 (0010 14 (0000 14))))
0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))
0400 11 (0300 11 (0200 11 (0100 13 (0000 14))))
0114 11 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))
0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14)))))
0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14))))))
0160 11 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0700 11 (0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14)))))))
0104 11 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))
0800 11 (0700 11 (0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14))))))))
0009 11 (0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))))
0900 11 (0800 11 (0700 11 (0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14)))))))))
0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))))
1010 11 (0010 14 (0000 14))
0103 11 (0003 12 (0002 12 (0001 12 (0000 14))))
0210 11 (0110 13 (0010 14 (0000 14)))
0140 11 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))
0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14)))))
0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))
0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))))
0105 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))
0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))))
0710 11 (0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))))))
0102 11 (0002 12 (0001 12 (0000 14)))
0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14)))))))
0112 11 (0012 12 (0011 12 (0010 14 (0000 14))))
0190 11 (0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))))
0910 11 (0810 11 (0710 11 (0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))))))))
0810 11 (0710 11 (0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14)))))))))
0101 11 (0100 13 (0000 14))
0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))
0120 11 (0110 13 (0010 14 (0000 14)))
0150 11 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0170 11 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0115 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0019 11 (0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))))
0113 11 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))
0022 10 (0012 12 (0011 12 (0010 14 (0000 14))))
2000 10 (1000 11 (0000 14))
3000 10 (2000 10 (1000 11 (0000 14)))
1100 10 (0100 13 (0000 14))
0091 10 (0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))))
0106 10 (0105 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))
0041 10 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))
0061 10 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0072 10 (0071 10 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0033 10 (0023 10 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
0025 10 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0109 10 (0009 11 (0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))))))
0082 10 (0081 10 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))))
0052 10 (0051 10 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0117 10 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))))
0024 10 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
0031 10 (0030 12 (0020 12 (0010 14 (0000 14))))
0023 10 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))
2010 10 (1010 11 (0010 14 (0000 14)))
3010 10 (2010 10 (1010 11 (0010 14 (0000 14))))
0032 10 (0022 10 (0012 12 (0011 12 (0010 14 (0000 14)))))
1110 10 (0110 13 (0010 14 (0000 14)))
0118 10 (0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))))
0081 10 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0108 10 (0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))))
0051 10 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0116 10 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))
0062 10 (0061 10 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0071 10 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0035 10 (0025 10 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))
0034 10 (0024 10 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0107 10 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))))
0042 10 (0041 10 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0119 10 (0019 11 (0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))))))
0021 10 (0011 12 (0010 14 (0000 14)))
0092 10 (0091 10 (0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))))
于 2012-11-12T13:50:21.173 に答える
1

最大合計が低い限り、問題なく機能するソリューションを次に示します。その複雑さは でO(N*M)Nはリストの数、Mは各リストのサイズです。

[4,16]各エントリが次の構造体のリストを保持する2 次元配列を作成します{ Reference, Index }

たとえば、あなたの例では、エントリ at[1,9]Referenceto セル[0,6]Index格納し、2 番目のリスト (値3) の最初の項目への参照を格納します。9合計は 9 で、Index+Referenceは値を取得する方法へのパスであるため、セルに格納されます9。各セルには、そのような参照の組み合わせのリストが格納されます。

for (int i = 0; i < 4; i++)
{
  foreach (var v in lists[i])
  {
    if (i == 0)
    {
      result[i, v].Add({ Reference = null, Index = v.Index });
      continue;
    }
    for (int j = 15; j >= 0; j--)
    {
      result[i, v + j].Add({ Reference = result[i-1, j].Pointer, Index = v.Index });
    }
  }
}

これで、ベスト 100 を探してトラバースする 15 のツリー構造ができました。参照を見てresults[3, 15]、再帰的にトラバースします。最後の (4 番目の) リストがソートされている場合は、最後の値を追加している間にこの走査を行うことができます。他のリストはソートする必要はありません。

于 2012-11-12T13:06:43.517 に答える
0

リスト A: 6、5、5、0、0、... リスト B: 3、1、1、0、0、...

配列の開始時: 6 (listA の項目) であり、B のこの項目インデックスを持つ未使用の要素 (開始時 - 0) 5 およびインデックス ...

この配列から最大を選択します。選択したアイテムのインデックスをインクリメントします。

リスト A セット インデックスの最後の項目まで繰り返します - (リスト B の要素の数)。

于 2012-11-12T13:29:25.280 に答える