AtCoder

ABC325 E問題(Our clients, please wait a moment)を解く

AtCoder_ABC325_E

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

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

E問題 Our clients, please wait a moment(Difficulty : 1093)

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

ABC325 E問題 Our clients, please wait a moment

2種類の重みで最短距離を計算して、合わせて解を求めます。AtCoder Problems による Difficulty は、1093 でした。

解答案

グラフの頂点の最短距離を求める問題です。アルゴリズムは、Dijkstra法を使います。基本的な考え方は、重みが小さい辺から処理をすることにあります。このアルゴリズムのこれ以上の詳細は、この記事では省略します。

この問題は、社用車を使う場合と電車を使う場合で辺の重みが異なります。社用車→電車に切り替えることができます。逆はできません。

最初の都市(問題では都市1、プログラムでは都市0)から各都市の距離は、社用車を使った重みで計算します。最後の都市(問題では都市N、プログラムでは都市N-1)から各都市の距離は、電車を使った重みで計算します。

最後にどの都市で乗り換えるか全探索して、最小値を求めます。

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

プログラムは、いくつかの処理に分けて考えることができます。

  • 重み付きグラフの定番コードと必要なマクロ定義(6ー13行目)
  • 問題の入力をグラフとして変数 G に追加します(21ー30行目)。
  • 都市0からの距離 dist1 をDijekstra法で求めます(32ー49行目)。重みが小さい辺から処理するために priority_queue を使いました。
  • 都市N-1からの距離 distN をDijekstra法で求めます(51ー67行目)。計算方法は、都市0からの距離を求める場合と同じです。
  • dist1 と distN の和の最小値を求めます(69ー72行目)。

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

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

typedef long long int ll;

#define INF (1LL << 60)
typedef pair<ll, int> P;
struct Edge {
	int to;
	ll weight;
	Edge() { }
	Edge(int t, ll w) : to(t), weight(w) { }
};

int main()
{
	int n;
	ll a, b, c;
	cin >> n >> a >> b >> c;

	vector<vector<Edge>> G(n);
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < n; ++j) {
			ll d;
			cin >> d;
			if (i != j) {
				G[i].push_back(Edge(j, d));
			}
		}
	}

	priority_queue<P, vector<P>, greater<P>> que;
	vector<ll> dist1(n, INF);;
	dist1[0] = 0;
	que.push(P(0, 0));
	while (!que.empty()) {
		P p = que.top();
		que.pop();
		int now = p.second;
		if (dist1[now] < p.first) {
			continue;
		}
		for (auto next_v : G[now]) {
			if (dist1[next_v.to] > dist1[now] + next_v.weight * a) {
				dist1[next_v.to] = dist1[now] + next_v.weight * a;
				que.push(P(dist1[next_v.to], next_v.to));
			}
		}
	}

	vector<ll> distN(n, INF);;
	distN[n - 1] = 0;
	que.push(P(0, n - 1));
	while (!que.empty()) {
		P p = que.top();
		que.pop();
		int now = p.second;
		if (distN[now] < p.first) {
			continue;
		}
		for (auto next_v : G[now]) {
			if (distN[next_v.to] > distN[now] + next_v.weight * b + c) {
				distN[next_v.to] = distN[now] + next_v.weight * b + c;
				que.push(P(distN[next_v.to], next_v.to));
			}
		}
	}

	ll result = INF;
	for (int i = 0; i < n; ++i) {
		result = min(result, dist1[i] + distN[i]);
	}

	cout << result << endl;

	return 0;
}

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

Python プログラム例(ABC325E)

基本的な考え方は、C++ と同じです。優先度付きキューとして heapq を使います。以下となります。

"""AtCoder Beginner Contest 325 E"""
import heapq

INF = 1 << 60

n, a, b, c = map(int, input().split())
G = [[] for i in range(n)]
for i in range(n):
    d = list(map(int, input().split()))
    for j in range(n):
        if i != j:
            G[i].append((j, d[j]))

que = []
heapq.heapify(que)
dist1 = [INF] * n
dist1[0] = 0
heapq.heappush(que, (0, 0))
while que:
    p = heapq.heappop(que)
    now = p[1]
    if dist1[now] < p[0]:
        continue
    for next_v in G[now]:
        if dist1[next_v[0]] > dist1[now] + next_v[1] * a:
            dist1[next_v[0]] = dist1[now] + next_v[1] * a
            heapq.heappush(que, (dist1[next_v[0]], next_v[0]))

distN = [INF] * n
distN[n - 1] = 0
heapq.heappush(que, (0, n - 1))
while que:
    p = heapq.heappop(que)
    now = p[1]
    if distN[now] < p[0]:
        continue
    for next_v in G[now]:
        if distN[next_v[0]] > distN[now] + next_v[1] * b + c:
            distN[next_v[0]] = distN[now] + next_v[1] * b + c
            heapq.heappush(que, (distN[next_v[0]], next_v[0]))

print(min([dist1[i] + distN[i] for i in range(n)]))

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

最後に

グラフの頂点間の最短距離を求める問題は定番の問題です。この問題は、重みが2種類あり、組み合わせて最短距離を求めました。

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

COMMENT

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

CAPTCHA