C++で解く2都市スケジューリング問題:最小コストでN人ずつ振り分ける貪欲法
問題概要
2N人の人がいるとします。ある会社が面接を実施する予定で、i番目の人を都市Aへ飛行機で移動させる費用が costs[i][0]、都市Bへ移動させる費用が costs[i][1] として与えられています。各都市にちょうどN人ずつ到着するように全員を移動させるとき、合計費用の最小値を求めるのがこの問題の目的です。
入力例
例えば、入力が [[10, 20], [30, 200], [400, 50], [30, 20]] の場合、出力は 110 になります。具体的には、1人目をコスト10で都市Aへ、2人目をコスト30で都市Aへ送り、3人目と4人目はそれぞれコスト50と20で都市Bへ送ります。合計は 10 + 30 + 50 + 20 = 110 となります。
解法のアプローチ
この問題は貪欲法(グリーディ法)を使うことで効率的に解けます。重要なポイントは「2都市間のコスト差が大きい人から優先的に割り当てを決める」という点です。コスト差が大きい人を後回しにすると、どちらの都市にも割り当てにくくなり、結果的に高額な費用を支払うリスクが高まるためです。
アルゴリズムの手順
- n を配列のサイズとします。
- a := n / 2、b := n / 2 とします(それぞれ都市A・Bに割り当て可能な残り人数)。
- |costs[i][0] − costs[i][1]| の降順で配列をソートし、答え ans を 0 で初期化します。
- i = 0 から n − 1 まで以下を繰り返します。
- b == 0、または costs[i][0] ≤ costs[i][1] かつ a > 0 の場合:a を 1 減らし、ans += costs[i][0]
- それ以外の場合:b を 1 減らし、ans += costs[i][1]
- 最後に ans を返します。
C++での実装例
それでは、実際のC++コードを見てみましょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
static bool cmp(vector<int> a, vector<int> b){
return abs(a[0] - a[1]) > abs(b[0] - b[1]);
}
int twoCitySchedCost(vector<vector<int>>& costs) {
int n = costs.size();
int a = n/2;
int b = n/2;
sort(costs.begin(), costs.end(), cmp);
int ans = 0;
for(int i = 0; i < n; i++){
if(b == 0 || (costs[i][0] <= costs[i][1] && a > 0)){
a--;
ans += costs[i][0];
} else {
b--;
ans += costs[i][1];
}
}
return ans;
}
};
main(){
Solution ob;
vector<vector<int>> c = {{10,20},{30,200},{400,50},{30,20}};
cout << ob.twoCitySchedCost(c);
}実行結果
入力
[[10,20],[30,200],[400,50],[30,20]]
出力
110
計算量について
このアルゴリズムの時間計算量は O(n log n) です。配列のソートが処理全体を支配するためです。一方、追加のデータ構造を使用しないため、空間計算量は O(1) で済みます。人数が増えても高速に動作する、非常に実用的な解法と言えるでしょう。
-
C++で解くジョブスケジューリング問題:重複しないタスク選択による最大利益の求め方
問題の概要n個の異なるタスクがあるとします。各タスクiは startTime[i] から endTime[i] まで実行され、完了すると profit[i] の利益が得られます。startTime・endTime・profit の3つのリストが与えられたとき、実行時間帯が互いに重ならないようなタスクの部分集合の中で、得られる利益の合計が最大になる値を求めてください。なお、あるタスクが時刻Xに終了する場合、同じ時刻Xに開始する別のタスクを選ぶことは可能です(終了時刻と開始時刻が一致していても重複とはみなしません)。入力例startTime = [1,2,3,3]、endTime = [3,4,5
-
C++で2つの二分木をマージする方法
2つの二分木があるとします。一方の木をもう一方の木に重ねてみると、一部のノードは互いに重なり合い、残りのノードは重ならない状態になります。ここで、この2つの木を1つの新しい二分木へマージすることを考えます。マージのルールは次のとおりです。2つのノードが重なっている場合は、それらの値を合計したものをマージ後のノードの新しい値とします。どちらか一方しかノードが存在しない場合は、空でない方のノードをそのまま新しい木のノードとして使用します。たとえば、次のような2つの木が与えられたとします。このときの出力結果は以下のようになります。解法のアプローチこの問題を解くために、以下の手順に従います。メソッド名