Pythonで配列内のすべての1をグループ化するための最小スワップ回数を求める方法
問題の概要
0と1のみから構成されるバイナリ配列 data が与えられたとき、配列内のすべての 1 をどこか一箇所に連続して並べる(グループ化する)ために必要な最小スワップ回数を求めます。
例えば、配列が [1,0,1,0,1,0,0,1,1,0,1] の場合、出力は 3 になります。これは [0,0,0,0,0,1,1,1,1,1,1] のように、すべての 1 を隣接させることが可能だからです。
解法のアプローチ
この問題は「累積和(プレフィックスサム)」と「スライディングウィンドウ」を組み合わせることで効率的に解くことができます。
基本的な考え方は以下の通りです。まず配列全体に含まれる 1 の総数を one とします。最終的な状態では、長さ one の区間にすべての 1 が集まっていることになります。したがって、長さ one のウィンドウを配列上でスライドさせながら、ウィンドウ内に含まれる 1 の数が最大となる位置を探します。そのとき「ウィンドウ外に残る 1 の個数」が、必要なスワップ回数の最小値となります。
アルゴリズムの手順
one := 0、n := 配列 data の長さと初期化します。- サイズ n の累積和配列
summを作成し、すべて 0 で埋めた後、summ[0] := data[0]とします。 one := one + data[0]とします。- i を 1 から n − 1 まで繰り返します。
summ[i] := summ[i - 1] + data[i]one := one + data[i]
ans := oneとします。left := 0、right := one - 1とします。right < nの間、以下を繰り返します。leftが 0 の場合はtemp := summ[right]、それ以外の場合はtemp := summ[right] - summ[left - 1]とします。ans := min(ans, one - temp)とします。rightとleftをそれぞれ 1 ずつ増やします。
ansを返します。
実装例(Python)
以下の実装例を見て、より深く理解しましょう。
class Solution(object):
def minSwaps(self, data):
one = 0
n = len(data)
summ=[0 for i in range(n)]
summ[0] = data[0]
one += data[0]
for i in range(1,n):
summ[i] += summ[i-1]+data[i]
one += data[i]
ans = one
left = 0
right = one-1
while right <n:
if left == 0:
temp = summ[right]
else:
temp = summ[right] - summ[left-1]
ans = min(ans,one-temp)
right+=1
left+=1
return ans
ob = Solution()
print(ob.minSwaps([1,0,1,0,1,0,0,1,1,0,1]))
入力
[1,0,1,0,1,0,0,1,1,0,1]
出力
3
計算量について
このアルゴリズムは、累積和の構築に O(n)、スライディングウィンドウの走査にも O(n) しかかからないため、時間計算量は O(n) です。また、累積和配列を保持するため空間計算量も O(n) となります。全探索的に各位置でのスワップ数を数える非効率な方法と比べ、大規模な配列でも高速に動作する点が大きなメリットです。
-
Pythonで全ノードに到達可能な最小の頂点集合を見つけるプログラム
問題概要有向非巡回グラフ(DAG)を考えます。グラフにはn個の頂点があり、各ノードには0からn-1までの番号が付けられています。グラフはエッジリストとして表現され、edges[i] = (u, v)はノードuからノードvへ向かう有向エッジを意味します。このとき、そこから出発すればグラフ内のすべてのノードに到達できるような、最小の頂点集合を見つける必要があります(頂点は任意の順序で返して構いません)。例えば、入力が次のような場合を考えてみましょう。この場合、出力は [0, 2, 3] となります。これらの頂点は他のどの頂点からも到達できないため、ここから探索を開始すれば全ノードをカバーできるから
-
Pythonで二値グリッドを整列させるための最小スワップ回数を求めるプログラム
問題の概要n × n の二値(0と1のみ)行列を考えます。この行列に対して、「隣接する2つの行を選んで入れ替える」という操作を1ステップとして実行できます。ここで求めたいのは、行列の主対角線より上側にあるすべての要素が 0 になるようにするために必要な最小スワップ回数です。どのように行を入れ替えても条件を満たせない場合は、-1 を返します。たとえば、次のような入力が与えられたとします。010011100この場合、出力は 2 になります。2回の隣接スワップで行を並べ替えれば、主対角線より上の要素をすべて 0 にできるからです。解き方のポイントこの問題を効率よく解く鍵は、各行を「右端にいくつ 0