私の質問は、CodeFuの練習問題(2012年ラウンド2問題3)についてです。基本的には、整数の配列を2つの(ほぼ)等しい半分に分割し、2つの間の可能な限り最小の差を返すことになります。以下に問題の説明を含めました。コメントに記載されているように、これは、動的計画法の分野での問題である、バランスの取れたパーティションの問題として説明できます。
現在、同様の問題が多く議論されていますが、この特定の問題に対する効率的な解決策を見つけることができませんでした。もちろん、問題は、トラバースできる組み合わせの数がすぐに大きくなりすぎて、ブルートフォース検索を実行できないことです(少なくとも再帰を使用する場合)。最大の問題セットを除くすべての問題に対して正常に機能する再帰的なソリューションがあります。再帰を早期に停止する最適化をいくつか追加しようとしましたが、パフォーマンスが遅すぎて、CodeFuで許可されている最大5秒以内の最大長(30)の配列を解決できません。コードを改善または書き直す方法についての提案は大歓迎です。また、反復バージョンを作成するのに役立つかどうかも知りたいです。
更新:このすばらしいサイトでは、バランスの取れたパーティションの問題について理論的な議論があり、動的な方法でこれを実行して解決する方法についての良いアイデアが得られます。それが私が求めていることですが、理論を正確に実践する方法がわかりません。映画では、2つのサブコレクションの要素が「バックポインターの古いトリックを使用して」見つけることができると述べていますが、その方法がわかりません。
問題
あなたとあなたの友人は、さまざまな量のコインをたくさん持っています。コインを2つのグループに分けて、それらのグループ間の違いを最小限に抑える必要があります。
たとえば、サイズが1,1,1,3,5,10,18のコインは、次のように分割できます:1,1,1,3,5および10,181,1,1,3,5,10および18または1 、1,3,5,10および1,18 3番目の組み合わせは、グループ間の差が1しかないため、有利です。制約:コインの要素は2〜30で、コインの各要素は1〜1です。 100000を含む
戻り値:コインを2つのグループに分割した場合に可能な最小の差
注:CodeFuのルールでは、CodeFuのサーバーでの実行時間は5秒以内であると規定されています。
メインコード
Arrays.sort(coins);
lower = Arrays.copyOfRange(coins, 0,coins.length-1);
//(after sorting) put the largest element in upper
upper = Arrays.copyOfRange(coins, coins.length-1,coins.length);
smallestDifference = Math.abs(arraySum(upper) - arraySum(lower));
return findSmallestDifference(lower, upper, arraySum(lower), arraySum(upper), smallestDifference);
再帰コード
private int findSmallestDifference (int[] lower, int[] upper, int lowerSum, int upperSum, int smallestDifference) {
int[] newUpper = null, newLower = null;
int currentDifference = Math.abs(upperSum-lowerSum);
if (currentDifference < smallestDifference) {
smallestDifference = currentDifference;
}
if (lowerSum < upperSum || lower.length < upper.length || lower[0] > currentDifference
|| lower[lower.length-1] > currentDifference
|| lower[lower.length-1] < upper[0]/lower.length) {
return smallestDifference;
}
for (int i = lower.length-1; i >= 0 && smallestDifference > 0; i--) {
newUpper = addElement(upper, lower[i]);
newLower = removeElementAt(lower, i);
smallestDifference = findSmallestDifference(newLower, newUpper,
lowerSum - lower[i], upperSum + lower [i], smallestDifference);
}
return smallestDifference;
}
データセット
これは、解決に時間がかかりすぎるセットの例です。
{100000,60000,60000,60000,60000,60000,60000,60000,60000、60000,60000,60000,60000,60000,60000,60000,60000,60000、60000,60000,60000,60000,60000,60000,60000 、60000,60000、60000,60000,60000}
ソースコード全体が必要な場合は、Ideoneに配置しました。