問題ステートメント
N人の面接を希望する雇用主が1人いるため、N面接枠を作成します。すべての人がこれらのスロットの空き時間スケジュールを持っています。可能であればN人をNスロットにスケジュールし、それが不可能な場合はフラグ/エラー/などを返すアルゴリズムを提供します。ランタイムの最速の複雑さはどれくらいですか?
これまでの私のアプローチ
ナイーブ:Nがあります! N人をスケジュールする方法。それらすべてを調べ、順列ごとに、それが可能かどうかを確認します。オン! )
バックトラック:
これはO(n!)の最悪のケースだと思います。
D.Pがあるかもしれません。解決策も-私はまだそれを見ていません。
その他の考え
この問題は、行が「スロット」、列が「人」であり、値が空きの場合は「1」、ビジーの場合は「0」であるNxNマトリックスで表すことができます。次に、この行列内で行と列が入れ替わった単位行列を探します。手順1と2は、「1」が1つだけの行または列を探しています。 (行列のランクが= Nの場合、つまり、解があることを意味します。ただし、その逆は成り立ちません)これを調べる別の方法は、重み付けされていない有向グラフエッジ行列として行列を扱うことです。次に、ノードはそれぞれ1つの候補と1つのスロットを表します。次に、グラフのすべてのノードに1つの出力エッジと1つの入力エッジがあるように、エッジのセットを探しています。または、2xノードの場合、2部グラフになります。
マトリックスの例:
1 1 1 1
0 1 1 0
1 0 0 1
1 0 0 1
あなたが指摘したように、問題は二部グラフで最大の一致を見つける問題と同等です(頂点のセットは人のセットであり、他のセットはスロットのセットです、人とスロットの間にエッジがあります)人がこのスロットで利用できる場合)。
この問題は Hopcroft-Karpアルゴリズム で解決できます。
最悪の場合の複雑度O(n ^(5/2))。グラフがスパースの場合に適しています。
制約プログラミングのアプローチについては、たとえばマトリックスアプローチやセットベースのアプローチなど、さまざまな方法でモデル化できます。
セットベースのアプローチは、高水準CP言語 MiniZinc で以下に示されています。 sは、(セット表記を使用して)時間枠ごとに利用可能な人です。決定変数は配列xです(その人は毎回割り当てられるべきです)。
include "globals.mzn"; int:n = 4; %の時間スロットあたりの空き人数 array [1..n] intのセット:s = [{1,2,3,4}、 {2,3}、 {1,4}、 {1,4}]; %決定変数 %スロットへの人物の割り当て(アポイントメント番号1..n) array [1..n] var 1 。 。] forall(i in 1..n)( x [i] in s [i] ) /\%各ユーザーが個別のタイムスロットを取得できるようにします alldifferent(x) ; output [show(x)];
モデルはこれらの4つのソリューションを(0.5msで)出力します。時間1は人3に割り当てられます。
x:[3、2、4、1] ---------- x:[2、3、4、1] ---------- x:[3、2、1、4] ---------- x: [2、3、1、4]
MiniZincモデルは次のとおりです: appointment_scheduling_set.mzn
マトリックスアプローチモデルはここにあり(出力セクションなし)、標準の整数プログラミングアプローチを使用します: appointment_scheduling.mzn 。
int:n = 4; %行はタイムスロットです %列は人です array [1..n、1 .. intのn]:m = array2d(1..n、1..n、 1、1、1、1、 0、1、1、0 、 1、0、0、1、 1、0、0、1、 ); %決定変数 %スロットへの人物の割り当て(アポイントメント番号1..n) var [1..n、1..n] of var 0..1:x; ソルブ満足; 制約 forall(i in 1..n)( %空きスロットを確保 合計([m [i、j] * x [i、j] | j in 1..n])= 1 /\%スロットごとと人ごとに1つの割り当てを保証します sum([x [i、j] | j in 1..n])= 1 /\ sum([x [j、i] | j in 1..n ])= 1 ) ;
このモデルからのソリューションは同じですが、別の(そしてより冗長な)方法で提示されます-そして、それが発生した場合-セットベースのアプローチと同じ順序で
スロット1:3 スロット2:2 スロット3:4 スロット4:1 --------- - スロット1:2 スロット2:3 スロット3:4 スロット4:1 -------- - スロット1:3 スロット2:2 スロット3:1 スロット4:4 ------- --- スロット1:2 スロット2:3 スロット3:1 スロット4:4
これが 最大2部マッチング問題 です。
グラフの密度に応じて、最速の解決策はおそらく Hopcroft-Karpアルゴリズム であり、最大でO(N ^(5/2))時間で実行されますが、おそらくはるかに高速です。
ネットワークフロー を使用できると思います。
i
には頂点_u_i
_を、スロットj
には頂点_v_j
_を作成します。s
を作成し、s
から(指示された)エッジを容量1の各_u_i
_に配置します。t
を作成し、各_v_j
_から容量1のt
にエッジを配置します。i
がスロットj
で面接できる場合は、_u_i
_から_v_j
_にエッジを配置します。O(N)
頂点とO(N^2)
エッジがあります。最大可能なフローはN
です。O(E*f)
を使用します。ここで、E
はエッジの数、f
はは最大フローの値なので、O(N^3)
を取ります。N
の場合、スケジュールがあります。エッジ_(u_i,v_j)
_にフロー1がある場合、候補者i
をスロットj
に面接します。それ以外の場合は、それは不可能だ。積分フローの定理により、最大フローはエッジに整数フロー(0または1)を持つため、有効なスケジュールがあります。