페이지

레이블이 shortest path problem인 게시물을 표시합니다. 모든 게시물 표시
레이블이 shortest path problem인 게시물을 표시합니다. 모든 게시물 표시

7332번: Cashier Employment

https://www.acmicpc.net/problem/7332

각 시간대마다 필요한 알바 수와 8시간동안 연속적으로 고용할 수 있는 알바 정보가 주어졌을 때 필요한 알바 수의 최솟값을 구하는 문제이다.

r[i]: i-1 ~ i시에 필요한 알바 수
t[i]: i-1 ~ i+7시에 쓸 수 있는 알바 수
* 굳이 i-1로 한 이유는 후에 prefix sum을 사용하는데 있어 -1번째 인덱스가 나오지 않게 하기 위함이다.

그러면 문제는 0<= a[i] <= t[i]인 a[i]를 잘 골라
i<8 일 때, a[17+i] + ... + a[24] + a[1] + ... + a[i] >= r[i]
i>=8 일 때, a[i-7] + a[i-6] + ... + a[i] >= r[i]
를 만족하는 sum(a[i])의 최솟값을 구하는 문제라고 할 수 있다.

s[i] = sum(a[j]: 1<=j<=i)로 놓고 모든 조건을 일차선형부등식 꼴로 만들어 보면
0 <= s[i] - s[i-1] <= t[i]
i<8 일 때, s[24] - s[16+i] + s[i] >= r[i]
i>=8 일 때, s[i] - s[i-8] >= r[i]

이제 상수 lmt <= s[24]로 놓으면
s[24] - lmt >= s[0]
s[i] >= s[i-1]
s[i-1] + t[i] >= s[i]
i<8 일 때, s[i] + lmt - r[i] >= s[16+i]
i>=8 일 때, s[i] - r[i] >= s[i-8]

모든 부등식이 x + w >= y 꼴의 형태이므로 각 조건에 대응되는 가중치가 w인 x -> y 간선들을 추가한 뒤, 최단거리 알고리즘을 이용해 주어진 부등식들이 가능한지 판단할 수 있다.
부등식들이 모두 성립하는 최소 0 <= lmt <= n를 찾는다. 불가능하면 No Solution을 출력한다.

시간복잡도는 테스트 케이스마다 $O(n)$

#include<cstdio>
#include<algorithm>
using namespace std;
int tc, n, t[25], r[25], dp[25][25];
bool f(int lmt) {
    fill(dp[0], dp[25], 1e9);
    for (int i = 0; i < 25; i++) {
        dp[i][i] = 0;
        if (i) dp[i - 1][i] = t[i], dp[i][i - 1] = 0;
    }
    int i = 1;
    for (; i < 8; i++) dp[i][i + 16] = lmt - r[i];
    for (; i < 25; i++) dp[i][i - 8] = -r[i];
    dp[24][0] = -lmt;
    for (int i = 0; i < 25; i++) for (int j = 0; j < 25; j++) for (int k = 0; k < 25; k++) dp[j][k] = min(dp[j][k], dp[j][i] + dp[i][k]);
    for (int i = 0; i < 25; i++) if (dp[i][i] < 0) return false;
    return true;
}
void task() {
    for (int i = 1; i <= 24; i++) scanf("%d", r + i), t[i] = 0;
    scanf("%d", &n);
    for (int i = 0, x; i < n; i++) scanf("%d", &x), t[x + 1]++;
    for (int i = 0; i <= n; i++) if (f(i)) { printf("%d\n", i); return; }
    puts("No Solution");
}
int main() {
    for (scanf("%d", &tc); tc--;) task();
    return 0;
}

13168번: 내일로 여행

https://www.acmicpc.net/problem/13168

내일로 티켓을 사용했을 때와 사용하지 않았을 때의 도시간 이동하는데 드는 최소 비용을 플로이드 알고리즘으로 구한다. 두 계획에 대해 각각 여행에 드는 비용을 계산하고 비교한다.

시간복잡도는 $O(k\lg n +n^3)$

#include<iostream>
#include<string>
#include<map>
#include<algorithm>
using namespace std;
int n, m, r, k, cost[10000], dp[100][100], c;
string plan[200], t, type[10000], s[10000], e[10000];
map<string, int> city;
int main() {
    cin >> n >> r; r <<= 1;
    fill(dp[0], dp[n], 1e9);
    for (int i = 0; i < n; i++) {
        cin >> t;
        city[t] = i;
        dp[i][i] = 0;
    }
    cin >> m;
    for (int i = 0; i < m; i++) cin >> plan[i];
    cin >> k;
    for (int i = 0; i < k; i++) {
        cin >> type[i] >> s[i] >> e[i] >> cost[i]; cost[i] <<= 1;
        dp[city[e[i]]][city[s[i]]] = dp[city[s[i]]][city[e[i]]] = min(dp[city[s[i]]][city[e[i]]], cost[i]);
    }
    for (int x = 0; x < n; x++) for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) dp[i][j] = min(dp[i][j], dp[i][x] + dp[x][j]);
    for (int i = 1; i < m; i++) c += dp[city[plan[i - 1]]][city[plan[i]]];
    for (int i = 0; i < k; i++) {
        if (type[i][1] == '-') cost[i] /= 2;
        if (type[i].size() > 8) cost[i] = 0;
        dp[city[e[i]]][city[s[i]]] = dp[city[s[i]]][city[e[i]]] = min(dp[city[s[i]]][city[e[i]]], cost[i]);
    }
    for (int x = 0; x < n; x++) for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) dp[i][j] = min(dp[i][j], dp[i][x] + dp[x][j]);
    for (int i = 1; i < m; i++) r += dp[city[plan[i - 1]]][city[plan[i]]];
    puts(c > r ? "Yes" : "No");
    return 0;
}

2795번: KAMPANJA

https://www.acmicpc.net/problem/2795

LA에 가는길과 오는길 모두 포함되어 있는 정점들을 가는길에서 방문한 순서대로 정렬한 수열을 s_1, 오는길에서 방문한 순서대로 정렬한 수열을 s_2라 하자. 또한, s_k 내에서 x는 idx_k(x)번째에 방문했다고 하자.

임의의 s, e에 대해 idx_1(s) <= idx_1(e) & idx_2(s) <= idx_2(e)를 만족하면 s<=s'<=e'<=e인 임의의 s', e'에 대해 idx_1(s')<=idx_1(e') & idx_2(s') <= idx_2(e')을 만족한다.
그렇지 않으면 가는길과 오는길의 s에서 e로 가는 최단경로가 다르므로 모순이다.

이 성질을 만족하는 경로 모양은 꽤나 단순해진다. 오는길과 가는길을 잘 정리해서 그려보면 DNA 같은 모양을 하고 있다.

이 그래프상에서 최단 거리를 구하기 위해 다음과 같은 배열 b를 정의한다.
b[i][j]: 1 ->...-> i ->...-> 2 ->...-> j ->...-> 1가 되기 위해 고용해야 하는 경호원 수
그러면 b[i][j] = min(b[i][k]+dist(j,k) - (i==j), b[k][j]+dist(i,k) - (i==j), b[j][i] + dist(j,i) - 1) (단, dist(i,i) = inf)를 만족한다.

모든 dist(i,j)쌍을 알고있다면 b[i][j]는 최단경로 알고리즘으로 구할 수 있다. 아래 소스에서는 dist(i,j)를 플로이드 알고리즘으로, b[i][j]는 벨만포드 알고리즘으로 구했다.

시간복잡도는 $O(n^2m)$

#include<cstdio>
#include<algorithm>
using namespace std;
int n, m, a[101][101], b[101][101], x[200], y[200];
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) a[i][j] = b[i][j] = 1e9;
    for (int i = 0; i < m; i++) scanf("%d%d", x + i, y + i), a[x[i]][y[i]] = 1;
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= n; k++) a[j][k] = min(a[j][k], a[j][i] + a[i][k]);
    b[1][1] = 1;
    for (int i = n; i--;) {
        for (int j = 1; j <= n; j++) {
            for (int k = 0; k < m; k++) {
                b[y[k]][j] = min(b[y[k]][j], b[x[k]][j] + (j != y[k]));
                b[j][x[k]] = min(b[j][x[k]], b[j][y[k]] + (j != x[k]));
            }
            for (int k = 1; k <= n; k++) b[j][k] = min(b[j][k], b[k][j] + a[k][j] - 1);
        }
    }
    printf("%d", b[2][2]);
    return 0;
}

9370번: Destination Unknown

https://www.acmicpc.net/problem/9370

각 목적지 후보마다 해당 지점까지의 최단 거리가 g와 h사이 도로를 거쳐가는 최단 거리와 같은지 판단하는 문제이다.
간단히 각 교차로마다 g와 h사이 도로를 지났는지의 여부에 따라 두 개의 노드를 만들어서 최단거리를 구해 비교하면 된다.

또는 노드를 두 개씩 만들지 않고 다음과 같은 트릭을 사용해서 풀 수도 있다.
1. 모든 도로의 길이를 2배로 만든다.
2. g와 h사이 도로 길이만 1 감소 시킨다.
3. 최단 경로를 구한 뒤, 후보 까지의 최단 거리가 홀수인 경우 목적지로 가능하다.

시간복잡도는 각 테스트 케이스마다 $O(m\lg n)$

#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
void solve() {
    vector<pair<intint> > adj[2001];
    int cst[2001], n, m, t, s, g, h, c[100];
    priority_queue<pair<intint> > pq;
    scanf("%d%d%d%d%d%d", &n, &m, &t, &s, &g, &h);
    for (int i = 0, a, b, d; i < m; i++) {
        scanf("%d%d%d", &a, &b, &d);
        d *= 2;
        if (a*b == g*h&&a + b == g + h) d--;
        adj[a].push_back({ b,d });
        adj[b].push_back({ a,d });
    }
    fill(cst + 1, cst + 1 + n, 1e9);
    cst[s] = 0;
    pq.push({ 0,s });
    while (!pq.empty()) {
        int tdis = -pq.top().first, tpos = pq.top().second;
        pq.pop();
        if (tdis^cst[tpos]) continue;
        for (auto it : adj[tpos]) if (cst[it.first] > tdis + it.second) {
            cst[it.first] = tdis + it.second;
            pq.push({ -cst[it.first],it.first });
        }
    }
    for (int i = 0; i < t; i++) scanf("%d", c + i);
    sort(c, c + t);
    for (int i = 0; i < t; i++) if (cst[c[i]] & 1) printf("%d ", c[i]);
    puts("");
}
int main() {
    int T;
    for (scanf("%d", &T); T--;) solve();
    return 0;
}

1257번: 엄청난 부자

https://www.acmicpc.net/problem/1257


$\sum_{i=1}^n {A[i]*B[i]} = M (B[i]>=0)$을 만족할 때 $\sum_{i=1}^n {B[i]}$의 최댓값을 구하는 문제이다.
A[1..n]에서 A[n](=L)이 가장 크고 일단 B[n]<0이어도 된다고 하자.
어떤 k에 대해 $\sum_{i=1}^{n-1} {A[i]*B[i]} = k (mod L)$을 만족하는 $\sum_{i=1}^{n-1} {B[i]}$의 최솟값을 구해놓았다면 여기에 L짜리 동전을 x개 추가함으로써 $\sum_{i=1}^{n-1} {A[i]*B[i]} = Lx + k$ 꼴의 $\sum_{i=1}^n {B[i]}$ 최솟값을 모두 알아낼 수 있다.

문제는 x = $[M/L]$, k = M%L일 때를 구하는 것이다.

이를 이용하기 위해 L로 나눈나머지로 가능한 k = 0..L-1에 대한 노드를 만들고, 각 동전의 추가에 따라 간선을 추가한다.
i번 노드와 가치가 t인 동전에 대해
i+t < L인 경우, 단순이 t짜리 동전을 하나 추가한다는 의미에서 i -> i+t, 가중치 1인 간선을 만든다.
i+t >= L인 경우, L짜리 동전을 하나 없애고 t짜리 동전을 하나 추가한다는 의미에서 i -> i+t-L, 가중치 0인 간선을 만든다.
모든 노드와 간선을 만든 후 0에서 M%L까지의 최단 거리를 구한다. 이 값 + $[M/L]$이 답이다.

처음으로 돌아와서 B[n]<0인 경우가 과연 존재할까?
최단경로는 길어야 L-1이므로 M%L을 만들기 위해 제거한 L짜리 동전은 많아야 L-1개이다.
그런데 입력으로 주어지는 M은 10억 이상, L은 10000이하이므로 $[M/L] >= 10^6 > 9999 = L-1$.
고로 주어진 입력에 따라 본 알고리즘을 통해 구한 답은 항상 B[n]>=0이다.

아래 소스는 $O(nL\lg {nL})$

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
long long m;
int n, s, a[1000], dp[10000];
int main() {
    scanf("%lld%d", &m, &n);
    for (int i = 0; i < n; i++) scanf("%d", a + i);
    s = *max_element(a, a + n);
    for (int i = 1; i < s; i++) dp[i] = 1e9;
    priority_queue<pair<intint> > pq;
    pq.push({ 0,0 });
    while (!pq.empty()) {
        int tdis = -pq.top().first, tpos = pq.top().second;
        pq.pop();
        if (dp[tpos] ^ tdis) continue;
        for (int i = 0; i < n; i++) {
            int t = tpos + a[i];
            if (dp[t%s] > tdis + 1 - t / s) {
                dp[t%s] = tdis + 1 - t / s;
                pq.push({ -dp[t%s],t%s });
            }
        }
    }
    printf("%lld", dp[m%s] + m / s);
    return 0;
}

14554번: The Other Way

https://www.acmicpc.net/source/5852286


$O(m\lg n)$

최단경로를 따라 DAG를 만들고 DP를 한다.

#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int MXN = 1e5;
int n, m, s, e, dp[MXN + 1];
long long dis[MXN + 1];
vector<pair<intint> > adj[MXN + 1];
priority_queue<pair<long longint> > pq;
int main() {
    scanf("%d%d%d%d", &n, &m, &s, &e);
    for (int i = 0, a, b, c; i < m; i++) {
        scanf("%d%d%d", &a, &b, &c);
        adj[a].push_back({ b,c });
        adj[b].push_back({ a,c });
    }
    fill(dis + 1, dis + 1 + n, 1e18);
    dis[s] = 0;
    dp[s] = 1;
    pq.push({ 0,s });
    while (!pq.empty()) {
        int tpos = pq.top().second;
        long long tdis = -pq.top().first;
        pq.pop();
        if (dis[tpos] ^ tdis) continue;
        for (auto it : adj[tpos]) {
            if (it.second + tdis < dis[it.first]) {
                dis[it.first] = it.second + tdis;
                dp[it.first] = 0;
                pq.push({ -dis[it.first],it.first });
            }
            if (it.second + tdis == dis[it.first]) dp[it.first] = (dp[it.first] + dp[tpos]) % (int(1e9) + 9);
        }
    }
    printf("%d", dp[e]);
    return 0;
}

10282번: Failing Components

https://www.acmicpc.net/problem/10282

$O(td\lg n)$

c로부터 도달 가능한 컴퓨터만 감염되며 최단 시간의 최댓값이 마지막 컴퓨터가 감염되는데 걸리는 시간이다.
아래는 다익스트라 알고리즘을 사용했다.

#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
int t, n, d, c;
int main() {
    scanf("%d", &t);
    while (t--) {
        vector<pair<intint> > adj[10001];
        scanf("%d%d%d", &n, &d, &c);
        for (int i = 0, x, y, z; i < d; i++) {
            scanf("%d%d%d", &x, &y, &z);
            adj[y].push_back({ x,z });
        }
        int mini[10001], cnt = 0, t = 0;
        priority_queue<pair<intint> > pq;
        fill(mini + 1, mini + 1 + n, 1e9);
        mini[c] = 0;
        pq.push({ 0,c });
        while (!pq.empty()) {
            int tpos = pq.top().second, tdis = -pq.top().first;
            pq.pop();
            if (mini[tpos] ^ tdis) continue;
            for (auto it : adj[tpos]) {
                if (mini[it.first] > tdis + it.second) {
                    mini[it.first] = tdis + it.second;
                    pq.push({ -mini[it.first],it.first });
                }
            }
        }
        for (int i = 1; i <= n; i++) if (mini[i] < 1e9) t = max(t, mini[i]), cnt++;
        printf("%d %d\n", cnt, t);
    }
    return 0;
}

2610번: 회의준비

https://www.acmicpc.net/problem/2610


$O(n^3)$

모든 서로 아는 사이에 단위 가중치를 두고 플로이드 알고리즘을 쓰면 임의의 두 사람 사이 의사전달시간을 알 수 있다.

#include<cstdio>
#include<algorithm>
using namespace std;
int n, m, d[101][101], maxi[101], res[100], sz, flag[101];
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (i^j) d[i][j] = 1e9;
    for (int i = 0, x, y; i < m; i++) {
        scanf("%d%d", &x, &y);
        d[x][y] = d[y][x] = 1;
    }
    for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++)
        if (d[i][k] + d[k][j] < d[i][j]) d[i][j] = d[i][k] + d[k][j];
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (d[i][j]<1e9&&d[i][j]>maxi[i]) maxi[i] = d[i][j];
    for (int i = 1; i <= n; i++) if (!flag[i]) {
        int t = i;
        for (int j = i; j <= n; j++) if (d[i][j]<1e9) {
            flag[j] = 1;
            if (maxi[t] > maxi[j]) t = j;
        }
        res[sz++] = t;
    }
    sort(res, res + sz);
    printf("%d", sz);
    for (int i = 0; i < sz; i++) printf("\n%d", res[i]);
    return 0;
}

2660번: 회장뽑기

https://www.acmicpc.net/problem/2660


$O(n^3)$

친구 사이이면 가중치를 1, 그렇지 않으면 무한대로 설정하고 플로이드 알고리즘을 돌린다.
회원 x의 점수는 x와 나머지 회원간 최대 가중치이다.

#include<cstdio>
int d[51][51], n, x, y, maxi[51], can = 1e9, cnt;
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) if (i^j) d[i][j] = 1e9;
    while (scanf("%d%d", &x, &y), ~x) d[x][y] = d[y][x] = 1;
    for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++)
        if (d[i][j] > d[i][k] + d[k][j]) d[i][j] = d[i][k] + d[k][j];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) if (maxi[i] < d[i][j]) maxi[i] = d[i][j];
        if (can > maxi[i]) can = maxi[i], cnt = 0;
        cnt += can == maxi[i];
    }
    printf("%d %d\n", can, cnt);
    for (int i = 1; i <= n; i++) if (maxi[i] == can) printf("%d ", i);
    return 0;
}

1956번: 운동

https://www.acmicpc.net/problem/1956


$O(v^3)$

초기에 i->i 가는 비용을 무한대로 설정하고 플로이드 알고리즘을 돌려본다.
이후 i->i 가는 비용의 최솟값을 출력한다. 이 값이 무한대이면 사이클은 존재하지 않는다.

#include<cstdio>
int v, e, dis[401][401], mini = 1e9, x, y, z;
int main() {
    scanf("%d%d", &v, &e);
    for (int i = 1; i <= v; i++)
        for (int j = 1; j <= v; j++) dis[i][j] = 1e9;
    while (e--) scanf("%d%d%d", &x, &y, &z), dis[x][y] = z;
    for (int k = 1; k <= v; k++) for (int i = 1; i <= v; i++) for (int j = 1; j <= v; j++)
        if (dis[i][j] > dis[i][k] + dis[k][j]) dis[i][j] = dis[i][k] + dis[k][j];
    for (int i = 1; i <= v; i++) if (mini > dis[i][i]) mini = dis[i][i];
    printf("%d", mini < 1e9 ? mini : -1);
    return 0;
}

11657번: 타임머신

https://www.acmicpc.net/problem/11657


$O(nm)$

벨만포드 알고리즘을 사용한다.

#include<cstdio>
int n, m, a[6000], b[6000], c[6000], t[501], last;
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++) scanf("%d%d%d", a + i, b + i, c + i);
    for (int i = 2; i <= n; i++) t[i] = 1e9;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) if (t[a[j]]<1e9&&t[b[j]]>t[a[j]] + c[j]) {
            t[b[j]] = t[a[j]] + c[j];
            last = i;
        }
    }
    if (last == n - 1) puts("-1");
    else for (int i = 2; i <= n; i++) printf("%d\n", t[i] < 1e9 ? t[i] : -1);
    return 0;
}

12752번: Cities

https://www.acmicpc.net/problem/12752

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int MXN = 1e5;
typedef long long ll;
int n, k, m, a[6], p[5];
ll dis[6][MXN + 2], r = 1e15, cst[5][5][MXN + 1];
vector<pair<int, ll> > adj[MXN + 2];
void spath(int h) {
    fill(dis[h], dis[h + 1], 1e15);
    dis[h][a[h]] = 0;
    priority_queue<pair<ll, int> > pq;
    pq.push({ 0,a[h] });
    while (!pq.empty()) {
        int tpos = pq.top().second;
        ll tdis = -pq.top().first;
        pq.pop();
        if (tdis^dis[h][tpos]) continue;
        for (auto it : adj[tpos]) if (it.second + dis[h][tpos] < dis[h][it.first]) {
            dis[h][it.first] = it.second + dis[h][tpos];
            pq.push({ -dis[h][it.first],it.first });
        }
    }
}
int main() {
    scanf("%d%d%d", &n, &k, &m);
    for (int i = 0; i < k; i++) scanf("%d", a + i), p[i] = i;
    for (int i = 0, x, y, z; i < m; i++) {
        scanf("%d%d%d", &x, &y, &z);
        adj[x].push_back({ y,z });
        adj[y].push_back({ x,z });
    }
    for (int i = 0; i < k; i++) spath(i);
    for (int i = 1; i <= n; i++) {
        adj[0].push_back({ i,0 });
        adj[i].push_back({ n + 1,0 });
        ll s = 0;
        for (int j = 0; j < k; j++) s += dis[j][i];
        r = min(r, s);
    }
    if (k > 3) {
        for (int i = 1; i < k; i++) {
            for (int j = 0; j < i; j++) {
                for (int u = 1; u <= n; u++) {
                    adj[0][u - 1].second = dis[i][u] + dis[j][u];
                    adj[u].rbegin()->second = 0;
                    for (int v = 0; v < k; v++) if (v^i&&v^j) adj[u].rbegin()->second += dis[v][u];
                }
                spath(k);
                r = min(r, dis[k][n + 1]);
                for (int u = 1; u <= n; u++) cst[i][j][u] = dis[k][u];
            }
        }
    }
    if (k == 5) {
        for (int i = 0; i < k; i++) {
            for (int p1 = 1; p1 < k; p1++) if (p1^i) {
                for (int p2 = 0; p2 < p1; p2++) if (p2^i) {
                    int q[2], qcnt = 0;
                    for (int u = 0; u < k; u++) if (u^i&&u^p1&&u^p2) q[qcnt++] = u;
                    for (int u = 1; u <= n; u++) r = min(r, cst[p1][p2][u] + cst[q[1]][q[0]][u] + dis[i][u]);
                }
            }
        }
    }
    printf("%lld", r);
    return 0;
}

7040번: 밥 먹기

https://www.acmicpc.net/problem/7040


$O(n(ml+md))$

1~>x 거리를 d[x]라 하면
여러 d[i]<=d[j]+c 꼴의 일차 부등식을 연립하는 문제가 된다.
이는 j->i, 가중치 c인 간선을 추가하여 최단거리를 구하는 문제와 동치이다.
d[i] 중 음수가 있으면 -1
1~>n 경로가 존재하지 않으면 -2

#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
int ck[1001], dis[1001], n, ml, md;
vector<pair<intint> > adj[1001];
queue<int> q;
int main() {
    scanf("%d%d%d", &n, &ml, &md);
    for (int i = 0, x, y, z; i < ml; i++) {
        scanf("%d%d%d", &x, &y, &z);
        adj[x].push_back({ y,z });
    }
    for (int i = 0, x, y, z; i < md; i++) {
        scanf("%d%d%d", &x, &y, &z);
        adj[y].push_back({ x,-z });
    }
    for (int i = 2; i <= n; i++) dis[i] = 2e9;
    q.push(1);
    while (!q.empty()) {
        int h = q.front();
        q.pop();
        ck[h] = 0;
        if (dis[h] < 0) {
            puts("-1");
            return 0;
        }
        for (auto it : adj[h]) if (dis[it.first] > dis[h] + it.second) {
            dis[it.first] = dis[h] + it.second;
            if (!ck[it.first]) {
                ck[it.first] = 1;
                q.push(it.first);
            }
        }
    }
    printf("%d", dis[n] < 2e9 ? dis[n] : -2);
    return 0;
}

2211번: 네트워크 복구

https://www.acmicpc.net/problem/2211


$O(m\lg n)$

1번 지점으로부터 나머지 지점까지의 shortest path로 이뤄진 DAG를 구한다.

#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int MXN = 1e3;
int cst[MXN + 1], fr[MXN + 1], n, m;
vector<pair<intint> > adj[MXN + 1];
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0, x, y, z; i < m; i++) {
        scanf("%d%d%d", &x, &y, &z);
        adj[x].push_back({ y,z });
        adj[y].push_back({ x,z });
    }
    fill(cst + 2, cst + 1 + n, 1e9);
    priority_queue<pair<intint> > pq;
    pq.push({ 0,1 });
    printf("%d\n", n - 1);
    while (!pq.empty()) {
        int h = pq.top().second, d = -pq.top().first;
        pq.pop();
        if (cst[h] ^ d) continue;
        if (fr[h]) printf("%d %d\n", fr[h], h);
        for (auto it : adj[h]) if (d + it.second<cst[it.first]) {
            cst[it.first] = d + it.second;
            fr[it.first] = h;
            pq.push({ -cst[it.first],it.first });
        }
    }
    return 0;
}

10159번: 저울

https://www.acmicpc.net/problem/10159


#include<stdio.h>
int N, M, data[101][101], cnt;
int main()
{
    int i, j, k, x, y;
    scanf("%d", &N);
    scanf("%d", &M);
    for (i = 1; i <= M; i++)
    {
        scanf("%d %d", &x, &y);
        data[x][y] = 1;
    }
    for (i = 1; i <= N; i++)
    {
        for (j = 1; j <= N; j++)
        {
            for (k = 1; k <= N; k++)
            {
                if (data[j][i] && data[i][k])
                    data[j][k] = 1;
            }
        }
    }
    for (i = 1; i <= N; i++)
    {
        cnt = 0;
        for (j = 1; j <= N; j++)
        {
            if (!data[i][j] && !data[j][i])
                cnt++;
        }
        printf("%d\n", cnt - 1);
    }
    return 0;
}

1389번: 케빈 베이컨의 6단계 법칙

https://www.acmicpc.net/problem/1389


$O(m+n^3)$


#include<cstdio>
int n, m, c[101][101], r, mini = 1e9;
int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) c[i][j] = (i != j)*1e9;
    for (int i = 0, x, y; i< m; i++)scanf("%d %d", &x, &y), c[x][y] = c[y][x] = 1;
    for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)for (int k = 1; k <= n; k++) if (c[j][i] + c[i][k] < c[j][k])c[j][k] = c[j][i] + c[i][k];
    for (int i = 1; i <= n; i++) {
        int s = 0;
        for (int j = 1; j <= n; j++) s += c[i][j];
        if (s < mini)mini = s, r = i;
    }
    printf("%d", r);
    return 0;
}

5944번: Apple Delivery

https://www.acmicpc.net/problem/5944

$O(C\lg P)$

답은 min((pb~pa1 최단 거리),(pb~pa2 최단 거리)) + (pa1~pa2 최단 거리)


#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int MXP = 1e5;
int c, p, pb, pa1, pa2, dis[MXP + 1];
vector<pair<intint> > adj[MXP + 1];
void spath(int s) {
    fill(&dis[1], &dis[p + 1], 1e9);
    dis[s] = 0;
    priority_queue<pair<intint> > pq;
    pq.push({ 0,s });
    while (!pq.empty()) {
        int h = pq.top().second, d = -pq.top().first;
        pq.pop();
        if (dis[h] ^ d) continue;
        for (auto it : adj[h]) if (d + it.second < dis[it.first]) {
            dis[it.first] = d + it.second;
            pq.push({ -dis[it.first],it.first });
        }
    }
}
int main() {
    scanf("%d%d%d%d%d", &c, &p, &pb, &pa1, &pa2);
    for (int i = 0, x, y, z; i < c; i++) {
        scanf("%d%d%d", &x, &y, &z);
        adj[x].push_back({ y,z });
        adj[y].push_back({ x,z });
    }
    spath(pa1);
    int t = dis[pa2];
    spath(pb);
    printf("%d", min(dis[pa1], dis[pa2]) + t);
    return 0;
}

9988번: Roadblock

https://www.acmicpc.net/problem/9988


$O(n^3)$

먼저, 최단 경로를 구해 놓는다.
이 경로를 제외한 나머지 간선을 지연시키면 전체 그래프에서 최단 경로는 바뀌지 않는다.
따라서 최단 경로에 존재하는 각각의 간선을 지연시켜보며 최대 n번만 최단 경로를 구하면 된다.

#include<cstdio>
const int MXN = 250;
int n, m, adj[MXN + 1][MXN + 1], path[MXN + 1], tpath[MXN + 1];
int spath() {
    int dis[MXN + 1], ck[MXN + 1] = {};
    dis[1] = 0;
    for (int i = 2; i <= n; i++) dis[i] = 1e9;
    for (int j = 1; j < n; j++) {
        int pos, maxi = 1e9;
        for (int i = 1; i <= n; i++) if (!ck[i] && dis[i] < maxi) {
            maxi = dis[i];
            pos = i;
        }
        ck[pos] = 1;
        for (int i = 1; i <= n; i++) if (maxi + adj[pos][i] < dis[i]) {
            dis[i] = maxi + adj[pos][i];
            path[i] = pos;
        }
    }
    return dis[n];
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) adj[i][j] = 1e9;
    for (int i = 0, a, b, l; i < m; i++) {
        scanf("%d%d%d", &a, &b, &l);
        adj[a][b] = adj[b][a] = l;
    }
    int opt = spath(), maxi = 0;
    for (int i = 1; i <= n; i++) tpath[i] = path[i];
    for (int i = n; i ^ 1; i = tpath[i]) {
        adj[i][tpath[i]] *= 2; adj[tpath[i]][i] *= 2;
        int t = spath();
        if (t > maxi) maxi = t;
        adj[i][tpath[i]] /= 2; adj[tpath[i]][i] /= 2;
    }
    printf("%d", maxi - opt);
    return 0;
}

2307번: 도로검문

https://www.acmicpc.net/problem/2307


$O(m^2\lg m)$

각 간선을 제외시켜보며 얻은 최단 시간의 최댓값과 원래의 최단 시간 차를 구한다.


#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int MXN = 1e4, MXM = 5e4;
int a[MXM], b[MXM], n, m, maxi;
vector<pair<intint> > adj[MXN + 1];
int spath(int x, int y) {
    priority_queue<pair<intint> > pq;
    int ck[MXN + 1] = {};
    pq.push({ 0,1 });
    while (!pq.empty()) {
        int h = pq.top().second, dis = -pq.top().first;
        pq.pop();
        if (h == n) return dis;
        if (ck[h]) continue;
        ck[h] = 1;
        for (auto it : adj[h]) if (x + y^h + it.first || x*y^h*it.first)
            pq.push({ -it.second - dis,it.first });
    }
    return 1e9;
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0, t; i < m; i++) {
        scanf("%d%d%d", a + i, b + i, &t);
        adj[a[i]].push_back({ b[i],t });
        adj[b[i]].push_back({ a[i],t });
    }
    for (int i = 0; i < m; i++) maxi = max(maxi, spath(a[i], b[i]));
    printf("%d", maxi < 1e9 ? maxi - spath(0, 0) : -1);
    return 0;
}



추가로 모든 간선을 제외하면서 최단 시간을 m번이나 구할 필요는 없다.
처음에 최단 시간을 만드는 경로를 구하자.
이 경로상의 간선을 제외하지 않는다면 그대로 최단 경로를 이용하면 되므로 지연시간은 0이 된다.
따라서 최단 경로 상의 최대 n개 간선 각각을 제외해보며 지연 시간을 따져주면 된다.
이 방법의 전체 시간복잡도는 $O(nm\lg n)$

1162번: 도로포장

https://www.acmicpc.net/problem/1162


$O(km\lg n)$

k번째 최단 경로를 구하는 방법과 비슷하게 푼다.


#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int MXN = 1e4, MXK = 20;
int n, m, k, ck[MXN*(MXK + 1)];
vector<pair<intint> > adj[MXN];
priority_queue<pair<long longint> > pq;
int main() {
    scanf("%d %d %d", &n, &m, &k);
    for (int i = 0, x, y, z; i < m; i++) {
        scanf("%d %d %d", &x, &y, &z);
        adj[x - 1].push_back({ y - 1,z });
        adj[y - 1].push_back({ x - 1,z });
    }
    pq.push({ 0,0 });
    while (!pq.empty()) {
        int h = pq.top().second;
        long long dis = -pq.top().first;
        pq.pop();
        if (h >= n*k + n || ck[h]) continue;
        if (h%n == n - 1) {
            printf("%lld", dis);
            break;
        }
        ck[h] = 1;
        for (auto it : adj[h%n]) pq.push({ -dis - it.second, it.first + h / n*n }), pq.push({ -dis, it.first + (h / n + 1)*n });
    }
    return 0;
}