※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 13907번 문제인 세금이다.
문제는 아래 링크를 확인하자.

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

 

13907번: 세금

첫 번째 줄에 세 정수 N (2 ≤ N ≤ 1,000), M (1 ≤ M ≤ 30,000), K (0 ≤ K ≤ 30,000)가 주어진다. 각각 도시의 수, 도로의 수, 세금 인상 횟수를 의미한다. 두 번째 줄에는 두 정수 S와 D (1 ≤ S, D ≤ N, S ≠ D

www.acmicpc.net

도시 S에서 n까지 도로를 k개 이하개 거쳐갈 때의 최단거리를 저장하는 dp테이블 dp[k][n]을 생각하자. 이 dp테이블은 벨만-포드(Bellman-Ford) 알고리즘과 유사한 방식으로 진행해나가면 시간복잡도 O(NM)에 채울 수 있다.

 

세금이 총 P만큼 증가했을 때의 최소 비용은 min(dp[k][K] + P*k)가 된다. 이를 이용해 문제를 해결하자.

k는 N 이하(최단경로는 N개 이상의 노드를 경유하지 않는 점을 관찰하자)이므로, 모든 가능한 dp[k][K]값을 살펴보다도 시간복잡도 O(NK)로 문제를 해결하기에 충분한 시간복잡도임을 알 수 있다.

 

아래의 제출 코드는 min(dp[k][K] + P*k)를 찾는 과정을 CHT(Convex Hull Trick)을 이용해 이 과정의 시간복잡도를 O(N+K)로 한번 더 낮춘 코드이다. CHT를 모른다면 참고만 하고 넘어가자.

 

아래는 제출한 소스코드이다.

#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;

int N, M, K;
int S, D;

vector<pair<int, int>> G[1001];
int dist[1001][1001];

vector<pair<int, int>> stk;

void solve() {
	dist[0][S] = 1;
	for (int k = 0; k < N; k++) {
		for (int i = 1; i <= N; i++) {
			if (dist[k][i]) {
				if (dist[k + 1][i]) dist[k + 1][i] = min(dist[k + 1][i], dist[k][i]);
				else dist[k + 1][i] = dist[k][i];

				for (auto& nxt : G[i]) {
					if (dist[k + 1][nxt.first]) dist[k + 1][nxt.first] = min(dist[k + 1][nxt.first], dist[k][i] + nxt.second);
					else dist[k + 1][nxt.first] = dist[k][i] + nxt.second;
				}
			}
		}
	}
	
	stk.emplace_back(make_pair(0, 1000000007));
	for (int k = 1; k <= N; k++) {
		if (dist[k][D] == 0 || dist[k][D] == stk.back().second) continue;
		while (stk.size() > 1) {
			int idx1 = stk.size() - 1, idx2 = stk.size() - 2;
			ll a1 = stk[idx2].first, b1 = stk[idx2].second, a2 = stk[idx1].first, b2 = stk[idx1].second, a3 = k, b3 = dist[k][D];
			if (a1 * b3 + a2 * b1 + a3 * b2 >= a1 * b2 + a2 * b3 + a3 * b1) stk.pop_back();
			else break;
		}
		stk.emplace_back(make_pair(k, dist[k][D]));
	}
}


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

	cin >> N >> M >> K >> S >> D;
	while (M--) {
		int x, y, w; cin >> x >> y >> w;
		G[x].emplace_back(make_pair(y, w));
		G[y].emplace_back(make_pair(x, w));
	}

	solve();

	cout << stk.back().second - 1 << '\n';

	int p = 0;
	while (K--) {
		int x; cin >> x;
		p += x;
		
		int idx1 = stk.size() - 1, idx2 = stk.size() - 2;
		while (stk[idx1].first * p + stk[idx1].second > stk[idx2].first * p + stk[idx2].second) {
			idx1--, idx2--, stk.pop_back();
		}

		cout << stk.back().first * p + stk.back().second - 1 << '\n';
	}
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 25895 // C++] Don't Break the Ice  (0) 2023.03.18
[BOJ 27855 // C++] Cornhole  (0) 2023.03.17
[BOJ 25826 // C++] 2차원 배열 다중 업데이트 단일 합  (0) 2023.03.16
[BOJ 25943 // C++] 양팔저울  (1) 2023.03.15
[BOJ 1071 // C++] 소트  (0) 2023.03.14

+ Recent posts