ケーブルの総延長を最小化するコンピューターとソケットの接続方法を数えるC++プログラム
要素数Nの2つの配列AとBがあるとします。ここで、N台のコンピューターとN個のソケットが存在すると考えます。i番目のコンピューターの座標はA[i]、i番目のソケットの座標はB[i]であり、これら2N個の座標はすべて互いに異なるものとします。
目的は、ケーブルを使って各コンピューターを1つのソケットに接続することです。ただし、1つのソケットに接続できるコンピューターは最大1台までという制約があります。このとき、ケーブルの総延長が最小となる接続方法が何通り存在するかを数える必要があります。答えが非常に大きくなる場合は、10^9 + 7で割った余りを返してください。
例として、入力がA = [0, 10]、B = [20, 30]である場合を考えてみましょう。この場合の出力は2となります。最適な接続方法は「0を20へ、10を30へ接続する」パターンと「0を30へ、10を20へ接続する」パターンの2通りがあり、どちらの場合もケーブルの総延長が40になるためです。
解法のアプローチ
この問題を解くために、以下の手順に従います。
maxn := 200005
p := 10^9 + 7
整数型ペアの配列を1つ定義
n := Aのサイズ
i := 0 から初期化し、i < n の間、i を1ずつ増やしながら繰り返す:
a[i].first := A[i]
a[i].second := 1
i := n から初期化し、i < 2 * n の間、i を1ずつ増やしながら繰り返す:
a[i].first := B[i - n]
a[i].second := -1
配列aをソート
ways := 1, val := 0
i := 0 から初期化し、i < 2 * n の間、i を1ずつ増やしながら繰り返す:
val * a[i].second < 0 の場合:
ways := ways * |val|
val := val + a[i].second
waysを返すアルゴリズムのポイント
このアルゴリズムの鍵となるのは、コンピューターの座標に+1、ソケットの座標に-1を対応させ、すべての座標をまとめて昇順にソートする点です。その後、座標軸上を左から右へ走査しながら累積値valを更新していきます。走査中にvalの符号が反転するタイミング、つまり未接続のコンピューターとソケットが対面する瞬間には|val|通りのマッチングの選択肢が生まれるため、その都度答えwaysに掛け合わせていきます。これにより、全ての組み合わせを列挙することなく、効率的に最適接続の総数を求められます。
実装例
理解をより深めるために、以下のC++による実装を見てみましょう。
#include <bits/stdc++.h>
using namespace std;
int solve(vector<int> A, vector<int> B){
long maxn = 200005;
long p = 1000000007;
pair<int, int> a[maxn];
int n = A.size();
for (int i = 0; i < n; i++){
a[i].first = A[i];
a[i].second = 1;
}
for (int i = n; i < 2 * n; i++){
a[i].first = B[i - n];
a[i].second = -1;
}
sort(a, a + 2 * n);
long long ways = 1, val = 0;
for (int i = 0; i < 2 * n; i++){
if (val * a[i].second < 0){
ways = ways * abs(val) % p;
}
val += a[i].second;
}
return ways;
}
int main(){
vector<int> A = { 0, 10 };
vector<int> B = { 20, 30 };
cout << solve(A, B) << endl;
}入力
{ 0, 10 }, { 20, 30 }出力
2
-
【Python】N×N行列の空セル選択パターン数を数えるプログラムの書き方
問題概要 N × N の2値行列を考えます。ここで 0 は空のセル、1 はブロックされたセルを表します。このとき、「すべての行とすべての列に、選ばれたセルが少なくとも1つ含まれる」ように N 個の空のセルを選ぶ方法の数を求めます。答えが非常に大きくなる可能性があるため、結果は 10^9 + 7 で割った余りとして返します。 例えば、入力が次のような行列だったとします。 000000010 この場合、出力は 4 になります。以下の4通りの配置(x が選択されたセルを表す)が存在するためです。 アプローチ:ビットマスクを使った再帰探索 この問題は、行ごとに順番に処理を進めていく再帰的な探索で解
-
Pythonで二分木を2つの木に分割できるパターン数を数えるプログラム
問題の概要値「0」「1」「2」を含む二分木があるとします。根(ルート)には、少なくとも1つの「0」ノードと1つの「1」ノードが存在しています。ここで、「木の辺(エッジ)を1本削除すると、木が2つの異なる木に分割される」という操作を考えます。このとき、削除後に生成される2つの木のどちらにも「0」と「1」のノードが同時に含まれないように、辺を1本削除する方法が何通りあるかを求めるのがこの問題です。入力例例えば、次のような二分木が与えられたとします。この場合、出力は 1 となります。「0」から「2」へ向かう辺だけが、条件を満たす唯一の削除対象だからです。解法のアプローチこの問題は、DFS(深さ優先探