Problem:
高速道路の通行料金 (Highway Tolls)
問題文
JOI 王国は
N
個の都市からなる王国であり，これらの都市には
1
から
N
までの番号が付けられている．
JOI 王国には，これらの都市を結ぶ
一方通行
の高速道路が
M
本あり，
1
から
M
までの番号が付けられている．
高速道路
i
(
1 ≦ i ≦ M
) を通ると都市
A
i
から都市
B
i
に移動することができ，通行にかかる時間は
L
i
である．
それぞれの高速道路を通るたびに，通行料金が発生する．
高速道路
i
の通行料金は最も安い時で
C
i
だが，JOI 王国の労働者は皆時間外労働を嫌うため，ある基準となる時刻
0
から離れれば離れるほど通行料金が増えてしまう．
具体的には，都市
A
i
を時刻
t
に出発して高速道路
i
を通行した場合，通行料金は定数
K
を用いて
C
i
+ K × |t|
と表される．
ただし，
|t|
は
t
の絶対値を表す．
都市
1
に住んでいるあなたは，友達の住む都市
N
へ出かける計画を立てている．
あなたは高速道路を通って都市
1
から都市
N
まで移動したいので，まずはそれが可能かどうか確かめ，可能ならば通行料金の総和が最小でいくらになるかも求めたい．
ただし，移動経路や各都市を出発するタイミングは自由に決めることができる．
特に，都市
1
を負の時刻に出発したり，高速道路を通行せずどこかの都市に留まっている時間があったりしてもよい．
高速道路の情報および定数
K
が与えられたとき，高速道路を通って都市
1
から都市
N
まで移動することが可能かどうか判定し，
可能な場合は通行料金の総和の最小値を求めるプログラムを作成せよ．
なお，この問題の制約の下では，高速道路を通って都市
1
から都市
N
まで移動することが可能な場合，通行料金の総和の最小値は必ず整数になることが証明できる．
制約
2 ≦ N ≦ 4 000
．
1 ≦ M ≦ 8 000
．
0 ≦ K ≦ 100 000
．
1 ≦ A
i
≦ N
(
1 ≦ i ≦ M
)．
1 ≦ B
i
≦ N
(
1 ≦ i ≦ M
)．
A
i
≠ B
i
(
1 ≦ i ≦ M
)．
1 ≦ L
i
≦ 1 000 000
(
1 ≦ i ≦ M
)．
0 ≦ C
i
≦ 10
9
(
1 ≦ i ≦ M
)．
入力される値はすべて整数である．
入力
入力は以下の形式で与えられる．
N
M
K
A
1
B
1
L
1
C
1
A
2
B
2
L
2
C
2
︙
A
M
B
M
L
M
C
M
出力
高速道路を通って都市
1
から都市
N
まで移動することが不可能な場合は，
-1
を出力せよ．
可能な場合は，通行料金の総和の最小値を表す整数を
1
行で出力せよ．
入出力例
入力例 1
4 4 2
1 2 3 2
1 3 1 10
2 3 1 4
3 4 5 3
出力例 1
15
JOI 王国の都市と道路の様子を図にすると以下のようになる．
丸は都市を，矢印は道路を表し，各道路の横にはその道路の
L
i
と
C
i
の値がこの順に書かれている．
なお，丸の中に書かれた数字はその都市の番号を表す．
以下のように移動すると通行料金の総和が
15
になる．
時刻
-1
: 都市
1
を出発し，都市
3
へ向かう．通行料金が
10 + 2 × |-1| = 12
かかる．
時刻
0
: 都市
3
に到着し，すぐに都市
4
へ向かう．通行料金が
3 + 2 × |0|=3
かかる．
時刻
5
: 都市
4
に到着する．
通行料金の総和が
15
より小さくなるような移動方法は存在しないため，
15
を出力する．
入力例 2
4 4 0
1 2 3 2
1 3 1 10
2 3 1 4
3 4 5 3
出力例 2
9
入力例
1
とは
K
の値だけが異なる．
以下のように移動すると通行料金の総和が
9
になる．
時刻
-3
: 都市
1
を出発し，都市
2
へ向かう．通行料金が
2 + 0 × |-3| = 2
かかる．
時刻
0
: 都市
2
に到着し，すぐに都市
3
へ向かう．通行料金が
4 + 0 × |0| = 4
かかる．
時刻
1
: 都市
3
に到着し，そのまま都市
3
に留まる．
時刻
3
: 都市
3
を出発し，都市
4
へ向かう．通行料金が
3 + 0 × |3| = 3
かかる．
時刻
8
: 都市
4
に到着する．
通行料金の総和が
9
より小さくなるような移動方法は存在しないため，
9
を出力する．
入力例 3
2 1 10
2 1 4 7
出力例 3
-1
高速道路を通って都市
1
から都市
2
まで移動することは不可能なので，
-1
を出力する．
入力例 4
4 3 5
1 2 3 1
2 3 1 10
3 4 7 6
出力例 4
37
入力例 5
8 8 2
1 2 1 5
5 6 3 1
2 4 10 18
3 5 3 1
1 3 4 2
5 6 2 2
2 5 2 3
6 8 1 1
出力例 5
25
同じ
(A
i
,B
i
)
のペアを持つ複数の道路が存在することもある．
入力例 6
6 10 100000
4 2 212037 752027141
2 5 667097 1571491
2 1 769275 576006950
1 2 711969 526189398
5 3 733555 206320177
3 4 364807 802102091
1 4 467240 183184247
3 5 44994 15991843
5 3 613192 782356546
4 6 832593 639529758
出力例 6
47546714005

Submitted solutions:

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

using ll = long long;
constexpr ll INF = LLONG_MAX / 4;

template <class T>
inline bool chmin(T &a, const T &b) {
    if (a > b) { a = b; return true; }
    return false;
}

struct Edge {
    int to;
    ll length, cost;
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N, M, K;
    cin >> N >> M >> K;

    vector<vector<Edge>> forwardGraph(N), reverseGraph(N);
    for (int i = 0; i < M; i++) {
        int A, B; ll L, C;
        cin >> A >> B >> L >> C;
        --A; --B;
        forwardGraph[A].push_back({B, L, C});
        reverseGraph[B].push_back({A, L, C});
    }

    vector<vector<ll>> dp(N + 1, vector<ll>(N, INF));
    vector<vector<ll>> reverseDp(N + 1, vector<ll>(N, INF));
    dp[0][0] = 0;
    reverseDp[0][N - 1] = 0;

    // forward DP
    for (int k = 0; k < N; k++) {
        for (int n = 0; n < N; n++) {
            if (dp[k][n] == INF) continue;
            for (auto &edge : forwardGraph[n]) {
                chmin(dp[k + 1][edge.to],
                      dp[k][n] + edge.cost + (ll)(k + 1) * edge.length * K);
            }
        }
    }

    // reverse DP
    for (int k = 0; k < N; k++) {
        for (int n = 0; n < N; n++) {
            if (reverseDp[k][n] == INF) continue;
            for (auto &edge : reverseGraph[n]) {
                chmin(reverseDp[k + 1][edge.to],
                      reverseDp[k][n] + edge.cost + (ll)k * edge.length * K);
            }
        }
    }

    ll answer = INF;
    for (int n = 0; n < N; n++) {
        ll minForward = INF, minReverse = INF;
        for (int k = 0; k <= N; k++) {
            chmin(minForward, dp[k][n]);
            chmin(minReverse, reverseDp[k][n]);
        }
        if (minForward < INF && minReverse < INF) {
            chmin(answer, minForward + minReverse);
        }
    }

    cout << (answer < INF ? answer : -1) << "\n";
    return 0;
}
accuracy: 1
time_ms: 570
memory_kb: 254464
final_rank: 4

code_snippet: #include <bits/stdc++.h>

using namespace std;
using ll = long long;

const ll INF = numeric_limits<ll>::max();

// 値を更新する関数 (小さい値に更新する場合に使用)
inline bool chmin(ll& p, ll q) {
    return p > q ? p = q, true : false;
}

struct Edge {
    int to;           // 行き先
    ll  length, cost; // 距離、コスト
};

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int N, M, K; // 都市数、道路数、コスト定数
    cin >> N >> M >> K;

    vector<vector<Edge>> forwardGraph(N), reverseGraph(N); // 正方向、逆方向のグラフ
    for (int i = 0; i < M; ++i) {
        int A, B, L, C;
        cin >> A >> B >> L >> C;
        --A; --B; // 0-indexed に変換
        forwardGraph[A].push_back({B, L, C});
        reverseGraph[B].push_back({A, L, C});
    }

    // DPテーブルの初期化
    vector<vector<ll>> dp(N + 1, vector<ll>(N, INF));
    vector<vector<ll>> reverseDp(N + 1, vector<ll>(N, INF));
    dp[0][0] = 0; // 出発地点
    reverseDp[0][N - 1] = 0; // 目的地

    // 正方向のDP
    for (int k = 0; k < N; ++k) {
        for (int n = 0; n < N; ++n) {
            if (dp[k][n] == INF) continue; // 更新不要な箇所はスキップ
            for (const auto& edge : forwardGraph[n]) {
                chmin(dp[k + 1][edge.to], dp[k][n] + edge.cost + (k + 1) * edge.length * K);
            }
        }
    }

    // 逆方向のDP
    for (int k = 0; k < N; ++k) {
        for (int n = 0; n < N; ++n) {
            if (reverseDp[k][n] == INF) continue; // 更新不要な箇所はスキップ
            for (const auto& edge : reverseGraph[n]) {
                chmin(reverseDp[k + 1][edge.to], reverseDp[k][n] + edge.cost + k * edge.length * K);
            }
        }
    }

    // 最終的な答えの計算
    ll answer = INF;
    for (int n = 0; n < N; ++n) {
        ll minForward = INF, minReverse = INF;
        for (int k = 0; k <= N; ++k) {
            chmin(minForward, dp[k][n]);
            chmin(minReverse, reverseDp[k][n]);
        }
        if (minForward < INF && minReverse < INF) {
            chmin(answer, minForward + minReverse);
        }
    }

    // 結果の出力
    cout << (answer < INF ? answer : -1) << '\n';

    return 0;
}
accuracy: 1
time_ms: 640
memory_kb: 254148
final_rank: 3

code_snippet: #pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n, m, K;
    cin >> n >> m >> K;
    vector<vector<tuple<int, int, int>>> g(n), rg(n);
    for (int i = 0; i < m; ++i) {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        --a, --b;
        g[a].emplace_back(b, c, d);
        rg[b].emplace_back(a, c, d);
    }
    vector<vector<ll>> dp1(n, vector<ll>(n + 1, 1e18)), dp2(n, vector<ll>(n + 1, 1e18));
    dp1[0][0] = 0;
    for (ll j = 0; j < n; ++j) {
        for (int i = 0; i < n; ++i) {
            if (dp1[i][j] == 1e18) continue;
            for (auto [to, d, c] : g[i]) {
                ll cost = dp1[i][j] + c + (j + 1) * d * K;
                if (dp1[to][j + 1] > cost) {
                    dp1[to][j + 1] = cost;
                }
            }
        }
    }
    dp2[n - 1][0] = 0;
    for (ll j = 0; j < n; ++j) {
        for (int i = 0; i < n; ++i) {
            if (dp2[i][j] == 1e18) continue;
            for (auto [to, d, c] : rg[i]) {
                ll cost = dp2[i][j] + c + j * d * K;
                if (dp2[to][j + 1] > cost) {
                    dp2[to][j + 1] = cost;
                }
            }
        }
    }
    ll res = 1e18;
    for (int i = 0; i < n; ++i) {
        ll uo1 = 1e18, uo2 = 1e18;
        for (int j = 0; j <= n; ++j) {
            uo1 = min(uo1, dp1[i][j]);
            uo2 = min(uo2, dp2[i][j]);
        }
        res = min(res, uo1 + uo2);
    }
    if (res == 1e18) res = -1;
    cout << res << "\n";
}
accuracy: 1
time_ms: 880
memory_kb: 253876
final_rank: 5

code_snippet: // AOJ #779
// Highway Tolls 2025.1.20

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const ll INF = numeric_limits<ll>::max();

// 値を更新する関数 (小さい値に更新する場合に使用)
inline bool chmin(ll& p, ll q) {
    return p > q ? p = q, true : false;
}

struct Edge {
    int to;           // 行き先
    ll  length, cost; // 距離、コスト
};

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int N, M, K; // 都市数、道路数、コスト定数
    cin >> N >> M >> K;

    vector<vector<Edge>> forwardGraph(N), reverseGraph(N); // 正方向、逆方向のグラフ
    for (int i = 0; i < M; ++i) {
        int A, B, L, C;
        cin >> A >> B >> L >> C;
        --A; --B; // 0-indexed に変換
        forwardGraph[A].push_back({B, L, C});
        reverseGraph[B].push_back({A, L, C});
    }

    // DPテーブルの初期化
    vector<vector<ll>> dp(N + 1, vector<ll>(N, INF));
    vector<vector<ll>> reverseDp(N + 1, vector<ll>(N, INF));
    dp[0][0] = 0; // 出発地点
    reverseDp[0][N - 1] = 0; // 目的地

    // 正方向のDP
    for (int k = 0; k < N; ++k) {
        for (int n = 0; n < N; ++n) {
            if (dp[k][n] == INF) continue; // 更新不要な箇所はスキップ
            for (const auto& edge : forwardGraph[n]) {
                chmin(dp[k + 1][edge.to], dp[k][n] + edge.cost + (k + 1) * edge.length * K);
            }
        }
    }

    // 逆方向のDP
    for (int k = 0; k < N; ++k) {
        for (int n = 0; n < N; ++n) {
            if (reverseDp[k][n] == INF) continue; // 更新不要な箇所はスキップ
            for (const auto& edge : reverseGraph[n]) {
                chmin(reverseDp[k + 1][edge.to], reverseDp[k][n] + edge.cost + k * edge.length * K);
            }
        }
    }

    // 最終的な答えの計算
    ll answer = INF;
    for (int n = 0; n < N; ++n) {
        ll minForward = INF, minReverse = INF;
        for (int k = 0; k <= N; ++k) {
            chmin(minForward, dp[k][n]);
            chmin(minReverse, reverseDp[k][n]);
        }
        if (minForward < INF && minReverse < INF) {
            chmin(answer, minForward + minReverse);
        }
    }

    // 結果の出力
    cout << (answer < INF ? answer : -1) << '\n';

    return 0;
}
accuracy: 1
time_ms: 630
memory_kb: 254132
final_rank: 2

code_snippet: // AOJ #779
// Highway Tolls 2025.1.20

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const ll INF = numeric_limits<ll>::max();

// 値を更新する関数 (小さい値に更新する場合に使用)
inline bool chmin(ll& current, ll newValue) {
    if (current > newValue) {
        current = newValue;
        return true;
    }
    return false;
}

struct Edge {
    ll to, length, cost; // 行き先、距離、コスト
};

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int N, M, K; // 都市数、道路数、コスト定数
    cin >> N >> M >> K;

    vector<vector<Edge>> forwardGraph(N), reverseGraph(N); // 正方向、逆方向のグラフ
    for (int i = 0; i < M; ++i) {
        int A, B, L, C;
        cin >> A >> B >> L >> C;
        --A; --B; // 0-indexed に変換
        forwardGraph[A].push_back({B, L, C});
        reverseGraph[B].push_back({A, L, C});
    }

    // DPテーブルの初期化
    vector<vector<ll>> dp(N + 1, vector<ll>(N, INF));
    vector<vector<ll>> reverseDp(N + 1, vector<ll>(N, INF));
    dp[0][0] = 0; // 出発地点
    reverseDp[0][N - 1] = 0; // 目的地

    // 正方向のDP
    for (int k = 0; k < N; ++k) {
        for (int n = 0; n < N; ++n) {
            if (dp[k][n] == INF) continue; // 更新不要な箇所はスキップ
            for (const auto& edge : forwardGraph[n]) {
                chmin(dp[k + 1][edge.to], dp[k][n] + edge.cost + (k + 1) * edge.length * K);
            }
        }
    }

    // 逆方向のDP
    for (int k = 0; k < N; ++k) {
        for (int n = 0; n < N; ++n) {
            if (reverseDp[k][n] == INF) continue; // 更新不要な箇所はスキップ
            for (const auto& edge : reverseGraph[n]) {
                chmin(reverseDp[k + 1][edge.to], reverseDp[k][n] + edge.cost + k * edge.length * K);
            }
        }
    }

    // 最終的な答えの計算
    ll answer = INF;
    for (int n = 0; n < N; ++n) {
        ll minForward = INF, minReverse = INF;
        for (int k = 0; k <= N; ++k) {
            chmin(minForward, dp[k][n]);
            chmin(minReverse, reverseDp[k][n]);
        }
        if (minForward < INF && minReverse < INF) {
            chmin(answer, minForward + minReverse);
        }
    }

    // 結果の出力
    cout << (answer < INF ? answer : -1) << '\n';

    return 0;
}
accuracy: 1
time_ms: 620
memory_kb: 254132
final_rank: 1

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

using ll = long long;
using vll = vector<ll>;
using vvll = vector<vll>;
#define rep(i,s,t) for(ll i=s;i<t;i++)

bool chmin(ll& p, ll q) {
    if (p > q) {
        p = q;
        return 1;
    }
    return 0;
}

struct EDGE {
    ll to, L, C;
};

int main() {
    ll N, M, K;
    cin >> N >> M >> K;
    vector<vector<EDGE>> G(N), IG(N);
    rep(i, 0, M) {
        ll A, B, L, C;
        cin >> A >> B >> L >> C;
        A--; B--;
        G[A].push_back({ B,L,C });
        IG[B].push_back({ A,L,C });
    }
    vvll DP(N + 1, vll(N, 1e18));
    vvll IDP(N + 1, vll(N, 1e18));
    DP[0][0] = 0;
    IDP[0][N - 1] = 0;
    rep(k, 0, N) {
        rep(n, 0, N) {
            for (auto e : G[n]) {
                chmin(DP[k + 1][e.to], DP[k][n] + e.C + (k + 1) * e.L*K);
            }
            for (auto e : IG[n]) {
                chmin(IDP[k + 1][e.to], IDP[k][n] + e.C + (k)*e.L*K);
            }
        }
    }
    ll an = 1e18;
    vll P(N,1e18), IP(N,1e18);
    rep(n, 0, N) {
        rep(k, 0, N + 1) {
            chmin(P[n], DP[k][n]);
            chmin(IP[n], IDP[k][n]);
        }
        an = min(an, P[n] + IP[n]);
    }
    cout << (an<1e17?an:-1) << endl;
}
accuracy: 1
time_ms: 720
memory_kb: 254584
final_rank: 6