C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で購入者が購入できるパッケージの最大数を求めるプログラム


2つのリスト「sales(販売情報)」と「buyers(購入者情報)」が与えられているとします。sales の各要素は [day, price] という形式で、そのパッケージが指定された日に指定された価格でのみ販売されることを表します。一方、buyers の各要素は [payday, amount] という形式で、その購入者が給料日(payday)以降に使える金額 amount を持っていることを意味します。

ここで、各購入者が購入できるパッケージは最大1つまで、また各パッケージは1人の購入者にしか販売できないという条件のもとで、売却できるパッケージの最大数を求めます。

たとえば、入力が sales = [[0, 5], [0, 5], [0, 6], [1, 4], [1, 5], [3, 4]]、buyers = [[0, 4], [0, 6], [1, 5]] である場合、出力は 3 になります。1人目の購入者はパッケージ [1, 4] を、2人目は [0, 6] を、3人目は [1, 5] をそれぞれ購入できるためです。

解法のアプローチ

この問題は貪欲法(グリーディ法)を用いて解きます。手順は以下の通りです。

  • 結果を格納する変数 ret を 0 で初期化します

  • buyers を payday 昇順にソートします。payday が同じ場合は amount でソートします

  • multiset 型の集合 pq を定義します

  • sales をソートします(日付昇順、同一日なら価格降順)

  • i := 0 とします

  • sales の各要素 it について以下を繰り返します

    • i が buyers のサイズ未満 かつ buyers[i][0] <= it[0] である間、次を繰り返します

      • pq に buyers[i][1] を挿入します

      • i を 1 増やします

    • j := pq に it[1] を挿入してもソート順が崩れない位置(lower_bound の結果)とします

    • j が有効な位置であれば

      • ret を 1 増やします

      • pq から j の位置にある要素を削除します

  • ret を返します

アルゴリズムのポイント

このアルゴリズムでは、パッケージを販売日順に処理しながら、その時点で購入資格のある購入者(payday が販売日以前の購入者)の所持金を multiset に登録していきます。lower_bound を使うことで、現在のパッケージ価格を支払える購入者のうち最も所持金の少ない人を効率的に特定できます。余裕のある購入者を、後で登場するより高価なパッケージのために取っておけるため、全体のマッチング数を最大化できます。計算量は O((N + M) log(N + M)) 程度に収まります(N は sales のサイズ、M は buyers のサイズ)。

実装例

理解を深めるために、以下の実装を見てみましょう。

#include <bits/stdc++.h>
using namespace std;
class Solution {
    public:
    static bool cmp(vector<int>& a, vector<int>& b) {
        return a[0] == b[0] ? a[1] > b[1] : a[0] < b[0];
    }
    int solve(vector<vector<int>>& sales, vector<vector<int>>& buyers) {
        int ret = 0;
        sort(buyers.begin(), buyers.end());
        multiset<int> pq;
        sort(sales.begin(), sales.end(), cmp);
        int i = 0;
        for (auto& it : sales) {
            while (i < buyers.size() && buyers[i][0] <= it[0]) {
                pq.insert(buyers[i][1]);
                i++;
            }
            auto j = pq.lower_bound(it[1]);
            if (j != pq.end()) {
                ret++;
                pq.erase(j);
            }
        }
        return ret;
    }
};
int solve(vector<vector<int>>& sales, vector<vector<int>>& buyers) {
    return (new Solution())->solve(sales, buyers);
}
int main(){
    vector<vector<int>> sales = {{0, 5},{0, 5},{0, 6},{1, 4},{1, 5},{3, 4}};
    vector<vector<int>> buyers = {{0, 4},{0, 6},{1, 5}};
    cout << solve(sales, buyers);
}

入力

{{0, 5},{0, 5},{0, 6},{1, 4},{1, 5},{3, 4}}, {{0, 4},{0, 6},{1, 5}}

出力

3
  1. C++で車の売却による最大利益を求めるプログラムの作成方法

    問題の概要赤と青の2色の車に対する販売需要があるとします。ある自動車会社は、価格の異なる赤い車をp台、青い車をq台販売することに決めました。現在、同社の在庫には赤い車がa台、青い車がb台、そしてまだ塗装されていない無彩色の車がc台あります。各車の価値は配列A、B、Cとして与えられます。同社は1日あたりp + q台の車を販売し、そこから利益を最大化しなければなりません。無彩色の車は、赤または青のどちらの色にも塗装することが可能です。この記事では、車の販売によって得られる最大の利益を求める方法を解説します。入力例と出力たとえば、入力が p = 3、q = 3、a = 3、b = 3、c = 2、A

  2. 【C++】グラフの連結性を保ちながら辺を削除し、スコアの最大削減量を求める方法

    問題概要 n 個の頂点と m 本の辺からなる重み付き無向グラフを考えます。グラフの「スコア」は、含まれるすべての辺の重みの総和として定義されます。辺の重みは負になることもあり、そのような辺を取り除くとかえってスコアが増えてしまいます。 ここで求めたいのは、グラフを連結状態に保ったまま不要な辺を削除してスコアを最小化し、「スコアを最大でどれだけ減らせるか」を計算することです。 グラフは配列 edges として与えられ、各要素は {weight, {vertex1, vertex2}}(重みと両端の頂点)という形式で表されます。 入力例と出力 たとえば n = 5、m = 6、edges = {