AtCoder が提供しているABC(AtCoder Beginner Contest)035 D問題をC++で解いてみました。ABC035は、2016年3月26日21:00に実施されました。
AtCoder の紹介はこちらに、プログラミングの方針はこちらに記事があります。
D問題 トレジャーハント(Difficulty : 1591(参考値))
問題の詳細は、リンク先をご覧ください。
ダイクストラ法を少し工夫して使います。AtCoder Problems による Difficulty は 1591(参考値)でした。
解答案
所持金を最大化するためには、1か所の町に滞在するのが最適です。複数の町に滞在しても、所持金の最大値が増えることはないためです。
始点である頂点1から他の全ての頂点への最短距離(問題設定では時間)は、ダイクストラ法を用いて求められます。帰路として、全ての頂点から頂点1への最短距離を求める必要があります。これは、グラフの辺の向きを逆にした上で、頂点1から全ての頂点への最短距離を求めることで実現できます。
C++ プログラム例(ABC035D)
グラフ $G$ とその辺の向きを逆にしたグラフ $rG$ を設定します(24ー30行目)。ダイクストラ法の定番コードを使って、グラフ $G$ グラフ $rG$ の頂点1からすべての頂点への最短距離を求めます。
最後に滞在する町を決めて、所持金の最大値を求めます。以下が、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, m;
ll t;
cin >> n >> m >> t;
vector<ll> a(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
vector<vector<Edge>> G(n + 1), rG(n + 1);
for (int i = 0; i < m; ++i) {
int u, v;
ll c;
cin >> u >> v >> c;
G[u].push_back(Edge(v, c));
rG[v].push_back(Edge(u, c));
}
vector<ll> dist(n + 1, INF);
priority_queue<P, vector<P>, greater<P>> que;
dist[1] = 0;
que.push(P(0, 1));
while (!que.empty()) {
P p = que.top();
que.pop();
int now = p.second;
if (dist[now] < p.first) {
continue;
}
for (auto next_v : G[now]) {
if (dist[next_v.to] > dist[now] + next_v.weight) {
dist[next_v.to] = dist[now] + next_v.weight;
que.push(P(dist[next_v.to], next_v.to));
}
}
}
vector<ll> r_dist(n + 1, INF);
r_dist[1] = 0;
que.push(P(0, 1));
while (!que.empty()) {
P p = que.top();
que.pop();
int now = p.second;
if (r_dist[now] < p.first) {
continue;
}
for (auto next_v : rG[now]) {
if (r_dist[next_v.to] > r_dist[now] + next_v.weight) {
r_dist[next_v.to] = r_dist[now] + next_v.weight;
que.push(P(r_dist[next_v.to], next_v.to));
}
}
}
ll result = 0;
for (int i = 1; i <= n; ++i) {
if ((dist[i] == INF) || (r_dist[i] == INF)) {
continue;
}
if (dist[i] + r_dist[i] <= t) {
result = max(result, (t - dist[i] - r_dist[i]) * a[i]);
}
}
cout << result << endl;
return 0;
}
AC(Accepted=正しいプログラム)と判定されました。
最後に
辺の向きを逆にしたグラフを考慮することが、この問題のポイントでした。
引き続き ABC の問題を紹介していきます。