AtCoder

ABC312 F問題(Cans and Openers)を解く

AtCoder_ABC312_F

AtCoder が提供しているABC(AtCoder Beginner Contest)312 のF問題をC++とPythonで解いてみました。ABC312は、2023年7月29日21:00に実施されました。

AtCoder の紹介はこちらに、プログラミングの方針はこちらに記事があります。

F問題 Cans and Openers(Difficulty : 1534)

問題はリンク先をご覧ください。

ABC312 F問題 Cans and Openers

缶切りの必要有無で缶詰を分けて、それぞれを組み合わせて満足度の最大値を求めます。AtCoder Problems による Difficulty は 1534 でした。

解答案

仮に缶切りが不要な缶しかないとします。この場合は簡単です。満足度が高い順に $M$ 個の缶詰を選べば、最大の満足度を得ることができます。

逆に、缶切りが必要な缶しかないとします。缶切りがない場合は、缶切りを個数が多い順に入手します。それで得た缶切りを使って、満足度が高い順に缶詰を選んでいきます。缶切りを多い順に、缶詰を満足度が高い順に $M$ 個の処理をします。貪欲法と言えます。

注)問題文から読み取りにくいですが、この問題では缶切りを缶詰に対して一度しか使えません。

缶切りが不要な缶しかない場合と、缶切りが必要な缶しかない場合を組み合わせて、最大値を求めます。

C++ プログラム例(ABC312F)

まず、缶切りが不要な缶しかない満足度を sum_a に累積和として格納します。累積和は満足度が多い順に計算します(27ー36行目)。

次に、缶切りが必要な缶しかない満足度を sum_b に累積和として格納します。缶切りを得る場合は、累積和を更新しません(42ー47行目)。缶詰は満足度が多い順に処理します(49ー55行目)。

最後に、この二つの累積和を $M+1$ 通り組み合わせて、最大値を求めます(59ー62行目)。

以下が、C++プログラムとなります。

#include <bits/stdc++.h>
using namespace std;

typedef long long int ll;

int main()
{
	int n, m;
	cin >> n >> m;
	vector<ll> a, b, c;
	for (int i = 0; i < n; ++i) {
		int t;
		ll x;
		cin >> t >> x;
		if (t == 0) {
			a.push_back(x);
		} else if (t == 1) {
			b.push_back(x);
		} else if (t == 2) {
			c.push_back(x);
		}
	}
	sort(a.begin(), a.end());
	sort(b.begin(), b.end());
	sort(c.begin(), c.end());

	vector<ll> sum_a(m + 1);
	sum_a[0] = 0;
	for (int i = 0; i < m; ++i) {
		if (a.size() > 0) {
			sum_a[i + 1] = sum_a[i] + a.back();
			a.pop_back();
		} else {
			sum_a[i + 1] = sum_a[i];
		}
 	}

	vector<ll> sum_b(m + 1);
	sum_b[0] = 0;
	ll opener = 0;
	for (int i = 0; i < m; ++i) {
		if (opener == 0) {
			if (c.size() > 0) {
				opener = c.back();
				c.pop_back();
			}
			sum_b[i + 1] = sum_b[i];
		} else {
			if (b.size() > 0) {
				--opener;
				sum_b[i + 1] = sum_b[i] + b.back();
				b.pop_back();
			} else {
				sum_b[i + 1] = sum_b[i];
			}
		}
 	}

	ll result = 0;
	for (int i = 0; i <= m; ++i) {
		result = max(result, sum_a[i] + sum_b[m - i]);
	}

	cout << result << endl;

	return 0;
}

AC(Accepted=正しいプログラム)と判定されました。

Python プログラム例(ABC312F)

Python 版も基本的な考え方は同じです。以下となります。

"""AtCoder Beginner Contest 312 F"""
n, m = map(int, input().split())
a = []
b = []
c = []
for i in range(n):
    t, x = map(int, input().split())
    if t == 0:
        a.append(x)
    elif t == 1:
        b.append(x)
    elif t == 2:
        c.append(x)
a.sort()
b.sort()
c.sort()

sum_a = [0] * (m + 1)
for i in range(m):
    if a != []:
        sum_a[i + 1] = sum_a[i] + a.pop()
    else:
        sum_a[i + 1] = sum_a[i]

sum_b = [0] * (m + 1)
opener = 0
for i in range(m):
    if opener == 0:
        if c != []:
            opener = c.pop()
        sum_b[i + 1] = sum_b[i]
    else:
        if b != []:
            opener -= 1
            sum_b[i + 1] = sum_b[i] + b.pop()
        else:
            sum_b[i + 1] = sum_b[i]

result = 0
for i in range(m + 1):
    result = max(result, sum_a[i] + sum_b[m - i])

print(result)

こちらも「AC」と判定されました。

最後に

問題の構造が分かれば解きやすい問題でした。

引き続き ABC の問題を紹介していきます。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA