kil_alpaca
기록하는 습관
kil_alpaca
전체 방문자
오늘
어제
  • 분류 전체보기 (117)
    • 잡다한 오류 (2)
    • 머신러닝 & 딥러닝 (4)
      • 이론 (2)
      • 통계 기법 (2)
      • 잡다한 오류 (0)
    • Pytorch (6)
    • Tensorflow 2 (1)
      • 오류 처리 등등 (1)
    • Ubuntu (3)
      • 이것저것 (2)
      • 오류 처리 등등 (1)
    • Problem Solving (93)
      • Dynamic Programming (52)
      • Convex Hull Trick (8)
      • Dijkstra (33)
    • 자료구조 (3)
      • 기본 자료구조 (2)
      • Tree (1)
    • 알고리즘 (5)
      • 기하학 (4)
      • 최적화 기법 (1)

블로그 메뉴

  • 홈

공지사항

인기 글

태그

  • 자료구조
  • 머신러닝
  • glorot 초기화
  • Li Chao Tree
  • 통계
  • 다익스트라
  • convolution layer
  • CCW
  • 백준
  • 데이크스트라
  • 통계기법
  • 파이토치
  • 그래프 이론
  • DP
  • 그래프 탐색
  • dynamic programming
  • Dijkstra
  • he 초기화
  • Convex Hull Optimization
  • kaiming 초기화
  • Convex Hull Trick
  • 기하학
  • Tensor
  • 알고리즘
  • 텐서
  • pyTorch
  • convex hull
  • CHT
  • DP optimization
  • 딥러닝

최근 글

티스토리

hELLO · Designed By 정상우.
kil_alpaca

기록하는 습관

Problem Solving/Dijkstra

[백준] 16118번: 달빛 여우

2023. 4. 1. 14:47

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

 

16118번: 달빛 여우

첫 줄에 나무 그루터기의 개수와 오솔길의 개수를 의미하는 정수 N, M(2 ≤ N ≤ 4,000, 1 ≤ M ≤ 100,000)이 주어진다. 두 번째 줄부터 M개의 줄에 걸쳐 각 줄에 세 개의 정수 a, b, d(1 ≤ a, b ≤ N, a ≠ b

www.acmicpc.net


1. 문제 조건

1) 그루터기 $N$ 개와 양방향 오솔길 $M$ 개가 주어진다.

2) 여우와 늑대는 1번 그루터기에 있다.

3) 여우는 항상 같은 속도로 이동한다.

4) 늑대는 처음에는 여우의 2배 속도로, 다음에는 여우의 절반 속도로 이동한다.

5) 여우가 늑대보다 더 먼저 도착할 수 있는 그루터기의 개수를 출력한다.

 

2. 문제 풀이

상당히 신박한 문제였다.

다익스트라 알고리즘을 이용해서 풀어보자.

 

이 문제에는 당황스러운 부분이 2가지 있는데, 현명하게 해결해야 한다.

 

1) 여우와 늑대의 속도가 주어지지 않는다.

- 문제에서 요구하는 것이 최단 시간이 아니다.

- 여우와 늑대의 상대 속도로 생각하자.

- 즉, 오솔길의 길이로만 문제를 풀어도 된다.

 

2) 늑대가 이동한 최단 시간이 소수가 될 수도 있다.

- 소수로 최단 시간을 구하면, 정확도에서 문제가 생길수도 있다.

- 최대한 소수를 피해 정수로 문제를 풀어야 한다.

- 여우와 늑대의 속도는 상대적이고 여우의 속도가 기준이다.

- 여우의 속도를 2라고 하면, 늑대의 달리기 속도는 4, 걷기 속도는 1이 된다.

- 따라서 주어지는 오솔길의 길이를 2배로 해서 그래프를 구성하면 쉽게 해결된다.

 

나는 Node 구조체를 아래와 같이 정의하였다.

현재 노드에 도달할 때 달렸는지 여부도 저장하도록 하였다.

여우와 늑대의 Node 배열을 따로 선언했는데

늑대는 같은 노드라도 달려서 왔는지 걸어서 왔는지 따로 저장하도록 배열을 선언하였다.

#define MAXN 4001

#define NONE -1
#define RUN   0
#define WALK  1

struct Node
{
	int ID;			// 노드 번호
	ll dist;		// 이 노드까지의 최단 시간
	int state;		// 이 노드로 왔을 때 달렸으면 0, 걸었으면 1 / default -1
	int heapIdx;	// heap 안에서의 index
	bool inHeap;	// heap 안에 있는지 여부

	void init(int ID, int state=NONE)
	{
		this->ID = ID;
		dist = INF;
		this->state = state;
		heapIdx = -1;
		inHeap = false;
	}
};

Node nodeF[MAXN];		// 여우에 관한 경우
Node nodeW[MAXN][2];	// 늑대에 관한 경우

 

이제 모든 준비가 끝났다.

먼저 여우에 대해 전형적인 다익스트라 알고리즘을 돌려준다.

1번 노드에서 시작해서 모든 노드의 최단 시간을 계산해주면 된다.

 

다음으로 늑대에 대해 다익스트라를 돌려준다.

처음에는 달리기로 시작해야 하므로,

nodeW[1][WALK] 를 heap 에 넣고 시작한다.

그래야 다음 노드로 RUN 할 수 있기 때문이다.

참고로, 다음 상태는 현재 상태에 1을 xor 해주면 쉽게 toggle 할 수 있다.

 

다음 상태가 RUN 이라면, 오솔길의 길이를 2로 나누어서 사용하고

다음 상태가 WALK 라면, 오솔길의 길이를 2배 해서 사용하면 된다.

 

다익스트라 알고리즘이 모두 끝났다면

모든 노드에 대해 여우의 최단 시간이 늑대보다 작은 노드의 개수를 세서 출력하면 된다.


전체 풀이 코드는 다음과 같다.

#include <iostream>
#include <vector>
using namespace std;

#define MAXN 4001

#define NONE -1
#define RUN   0
#define WALK  1

typedef long long ll;

const ll INF = 1e16;

struct Edge {int e, w; };

struct Node
{
	int ID;
	ll dist;
	int state;
	int heapIdx;
	bool inHeap;

	void init(int ID, int state=NONE)
	{
		this->ID = ID;
		dist = INF;
		this->state = state;
		heapIdx = -1;
		inHeap = false;
	}
};

struct Heap
{
	Node* data[MAXN<<1];
	int size;

	void init() { size = 0; }

	bool isEmpty() { return size == 0; }

	void swap(int a, int b)
	{
		data[a]->heapIdx = b;
		data[b]->heapIdx = a;

		Node *temp = data[a];
		data[a] = data[b];
		data[b] = temp;
	}

	bool compare(int a, int b) { return data[a]->dist < data[b]->dist; }

	int updateUp(int idx)
	{
		while(true)
		{
			if(!idx) break;

			int parent = (idx - 1) >> 1;
			if(compare(parent, idx)) break;

			swap(idx, parent);
			idx = parent;
		}

		return idx;
	}

	void updateDown(int idx)
	{
		while(true)
		{
			int child1 = (idx << 1) + 1;
			int child2 = (idx << 1) + 2;

			if(child1 >= size) break;

			int child = child1;
			if(child2 < size)
			{
				child = compare(child1, child2) ? child1 : child2;
			}

			if(compare(idx, child)) break;

			swap(idx, child);
			idx = child;
		}
	}

	void update(int idx) { updateDown(updateUp(idx)); }

	void push(Node *newNode)
	{
		newNode->heapIdx = size;
		newNode->inHeap = true;

		data[size] = newNode;
		size++;

		updateUp(size-1);
	}

	Node* pop()
	{
		Node *ret = data[0];
		ret->inHeap = false;

		swap(0, size-1);
		size--;

		updateDown(0);

		return ret;
	}
};

vector<Edge> graph[MAXN];

Node nodeF[MAXN];
Node nodeW[MAXN][2];
Heap heap;

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

	int N, M; cin >> N >> M;
	int a, b, d;
	while(M--)
	{
		cin >> a >> b >> d;

		graph[a].push_back({b, d<<1});
		graph[b].push_back({a, d<<1});
	}

	for(int i=1; i<=N; i++)
	{
		nodeF[i].init(i);
		nodeW[i][RUN].init(i, RUN);
		nodeW[i][WALK].init(i, WALK);
	}

	heap.init();
	nodeF[1].dist = 0;
	heap.push(&nodeF[1]);

	while(!heap.isEmpty())
	{
		Node *curr = heap.pop();
		int ID = curr->ID;
		ll dist = curr->dist;

		for(Edge edge : graph[ID])
		{
			int nID = edge.e;
			ll nDist = dist + edge.w;

			if(nodeF[nID].dist > nDist)
			{
				nodeF[nID].dist = nDist;

				if(nodeF[nID].inHeap) heap.update(nodeF[nID].heapIdx);
				else				  heap.push(&nodeF[nID]);
			}
		}
	}

	heap.init();
	nodeW[1][WALK].dist = 0;
	heap.push(&nodeW[1][WALK]);

	while(!heap.isEmpty())
	{
		Node *curr = heap.pop();
		int ID = curr->ID;
		ll dist = curr->dist;
		int state = curr->state;

		for(Edge edge : graph[ID])
		{
			int nID = edge.e;
			int nState = state ^ 1;
			ll nDist = dist + ((nState == RUN) ? (edge.w >> 1) : (edge.w << 1));

			if(nodeW[nID][nState].dist > nDist)
			{
				nodeW[nID][nState].dist = nDist;

				if(nodeW[nID][nState].inHeap) heap.update(nodeW[nID][nState].heapIdx);
				else						  heap.push(&nodeW[nID][nState]);
			}
		}
	}

	int ans = 0;
	for(int i=1; i<=N; i++)
	{
		ll minW = (nodeW[i][RUN].dist < nodeW[i][WALK].dist) ? nodeW[i][RUN].dist : nodeW[i][WALK].dist;

		if(nodeF[i].dist < minW) ans++;
	}

	cout << ans << '\n';

	return 0;
}
저작자표시 비영리 변경금지 (새창열림)

'Problem Solving > Dijkstra' 카테고리의 다른 글

[백준] 1162번: 도로포장  (0) 2023.04.01
[백준] 11952번: 좀비  (0) 2023.04.01
[백준] 14461번: 소가 길을 건너간 이유 7  (0) 2023.04.01
[백준] 17835번: 면접보는 승범이네  (0) 2023.04.01
[백준] 16681번: 등산  (0) 2023.04.01
    'Problem Solving/Dijkstra' 카테고리의 다른 글
    • [백준] 1162번: 도로포장
    • [백준] 11952번: 좀비
    • [백준] 14461번: 소가 길을 건너간 이유 7
    • [백준] 17835번: 면접보는 승범이네
    kil_alpaca
    kil_alpaca

    티스토리툴바