공부/알고리즘 (c++)

Prim 알고리즘

원클릭쓰리버그 2022. 3. 24. 17:07
728x90
void Board::GenerateMap_Prim()
{
	struct CostEdge
	{
		int cost;
		Pos vtx;

		bool operator< (const CostEdge& other) const
		{
			return cost < other.cost;
		}
	};


	for (int32 y = 0; y < _size; y++)
	{
		for (int32 x = 0; x < _size; x++)
		{
			if (x % 2 == 0 || y % 2 == 0)
				_tile[y][x] = TileType::WALL;
			else
				_tile[y][x] = TileType::EMPTY;
		}
	}

	//edges[u] : u 정점과 연결된 간선 목록
	map<Pos, vector<CostEdge>> edges;

	for (int32 y = 0; y < _size; y++)
	{
		for (int32 x = 0; x < _size; x++)
		{
			if (x % 2 == 0 || y % 2 == 0)
				continue;
			//가로 연결하는 간선 후보
			if (x < _size - 2)
			{
				const int32 randValue = ::rand() % 100;
				Pos u = Pos{ y,x };
				Pos v = Pos{ y, x + 2 };
				edges[u].push_back(CostEdge{ randValue,v });
				edges[v].push_back(CostEdge{ randValue, u });
			}

			//세로 연결하는 간선 후보
			if (y < _size - 2)
			{
				const int32 randValue = ::rand() % 100;

				Pos u = Pos{ y,x };
				Pos v = Pos{ y+2, x };
				edges[u].push_back(CostEdge{ randValue, v });
				edges[v].push_back(CostEdge{ randValue, u });
			}
		}
	}

	// 해당 정점이 트리에 포함되어 있나?
	map<Pos, bool> added;
	// 어떤 정점이 누구에 의해 연결 되었는지
	map<Pos, Pos> parent;
	// 만들고 있는 트리에 인접한 간선 중, 해당 정점에 닿는 최소 간선의 정보
	map <Pos, int32>best;

	//다익스트라랑 거의 유사함. 단, 다익스트라는 best가 시작점을 기준으로 한 cost라면 
	//프림에서는 best가 현재 트리를 기준으로 한 간선 cost

	// 정점 만들기
	for (int32 y = 0; y < _size; y++)
	{
		for (int32 x = 0; x < _size; x++)
		{
			best[Pos{ y, x }] = INT32_MAX;
			added[Pos{ y,x }] == false;
		}
	}

	priority_queue<CostEdge> pq;
	const Pos startPos = Pos{ 1,1 };
	pq.push(CostEdge{ 0,startPos });
	best[startPos] = 0;
	parent[startPos] = startPos;

	while (pq.empty() == false)
	{
		CostEdge bestEdge = pq.top();
		pq.pop();
		
		// 새로 연결된 정점.
		Pos v = bestEdge.vtx;

		// 이미 추가되었다면 스킵
		if (added[v])
			continue;

		added[v] = true;
		// 맵에 적용
		{
			int y = (parent[v].y + v.y)/2;
			int x = (parent[v].x + v.x) / 2;
			_tile[y][x] = TileType::EMPTY;

		}

		for (CostEdge& edge : edges[v])
		{
			//이미 추가되었다면 스킵
			if (added[edge.vtx])
				continue;
			// 다른 경로에 더 좋은 후보가 발견 되었으면 스킵
			if (edge.cost > best[edge.vtx])
				continue;

			best[edge.vtx] = edge.cost;
			parent[edge.vtx] = v;
			pq.push(edge);
		}
	}
}