제민
MST (최소 신장 트리) 본문
https://www.acmicpc.net/problem/1197
1197번: 최소 스패닝 트리
첫째 줄에 정점의 개수 V(1 ≤ V ≤ 10,000)와 간선의 개수 E(1 ≤ E ≤ 100,000)가 주어진다. 다음 E개의 줄에는 각 간선에 대한 정보를 나타내는 세 정수 A, B, C가 주어진다. 이는 A번 정점과 B번 정점이
www.acmicpc.net
유니온 파인드를 사용한 크루스칼 알고리즘으로 MST를 구해보았다.
#include<bits/stdc++.h>
using namespace std;
int n,m; //정점 간선 수
int ans;
int parent[10'003];
int rnk[10'003];
vector<tuple<int,int,int>> E;
void init(){
for(int i=1;i<=n;i++){
parent[i]=i;
rnk[i]=1;
}
}
int find(int u){
if(parent[u]==u) return u;
return parent[u]=find(parent[u]);
}
void merge(int u, int v){
u=find(u); v=find(v);
if(u==v) return;
if(rnk[u]>rnk[v]) swap(u,v);
parent[u]=v;
if(rnk[u]==rnk[v]) rnk[v]++;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n>>m;
init();
while(m--){
int a,b,c; cin>>a>>b>>c;
E.push_back({c,a,b});
}
sort(E.begin(),E.end());
for(auto val : E){
auto[g,u,v]=val;
if(find(u)!=find(v)){
merge(u,v);
ans+=g;
}
}
cout<<ans;
return 0;
}
https://www.acmicpc.net/problem/1368
1368번: 물대기
첫 줄에는 논의 수 N(1 ≤ N ≤ 300)이 주어진다. 다음 N개의 줄에는 i번째 논에 우물을 팔 때 드는 비용 Wi(1 ≤ Wi ≤ 100,000)가 순서대로 들어온다. 다음 N개의 줄에 대해서는 각 줄에 N개의 수가 들어
www.acmicpc.net
전에 그래프 문제 중 지하철? 역을 연결하는 문제와 비슷하다.
가상의 노드를 추가시켜야 한다.
예제 케이스를 보면, 1 2 3 4 로 이어지는 가상의 정점 5를 만들고 각 간선의 가중치를 우물을 파는 비용으로 하는 것이다.
이런 발상을 떠올리기가 정말 어렵다고 생각한다.
#include<bits/stdc++.h>
using namespace std;
int parent[303];
int n,ans;
vector<tuple<int,int,int>> edge;
void init(){
for(int i=1;i<=n;i++){
parent[i]=i;
}
}
int find(int u){
if(u==parent[u]) return u;
return parent[u]=find(parent[u]);
}
void merge(int u, int v){
u=find(u); v=find(v);
if(u==v) return;
parent[u]=v;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n;
init();
for(int i=1;i<=n;i++){
int x; cin>>x;
edge.push_back({x,i,n+1});
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x; cin>>x;
if(i==j) continue;
if(i<j) edge.push_back({x,i,j});
}
}
sort(edge.begin(),edge.end());
for(auto val : edge){
auto[g,u,v]=val;
if(find(u)==find(v)) continue;
merge(u,v);
ans+=g;
}
cout<<ans;
return 0;
}
https://www.acmicpc.net/problem/9372
9372번: 상근이의 여행
첫 번째 줄에는 테스트 케이스의 수 T(T ≤ 100)가 주어지고, 각 테스트 케이스마다 다음과 같은 정보가 주어진다. 첫 번째 줄에는 국가의 수 N(2 ≤ N ≤ 1 000)과 비행기의 종류 M(1 ≤ M ≤ 10 000) 가
www.acmicpc.net
모든 간선의 가중치를 1로 정의하고 크루스칼 알고리즘으로 MST를 구해준다.
#include<bits/stdc++.h>
using namespace std;
int n,m;
int parent[1003];
vector<tuple<int,int,int>> edge;
void init(){
for(int i=1;i<=n;i++){
parent[i]=i;
}
}
int find(int u){
if(u==parent[u]) return u;
return parent[u]=find(parent[u]);
}
void merge(int u, int v){
u=find(u); v=find(v);
if(u==v) return;
parent[u]=v; //v에 u를 붙임
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t; cin>>t;
while(t--){
cin>>n>>m;
int ans=0;
edge.clear();
init();
while(m--){
int u,v; cin>>u>>v;
edge.push_back({1,u,v});
}
sort(edge.begin(),edge.end());
for(auto E : edge){
auto[g,u,v]=E;
if(find(u)!=find(v)){
merge(u,v);
ans+=g;
}
}
cout<<ans<<'\n';
}
return 0;
}
https://www.acmicpc.net/problem/16398
16398번: 행성 연결
홍익 제국의 중심은 행성 T이다. 제국의 황제 윤석이는 행성 T에서 제국을 효과적으로 통치하기 위해서, N개의 행성 간에 플로우를 설치하려고 한다. 두 행성 간에 플로우를 설치하면 제국의 함
www.acmicpc.net
그냥 크루스칼로 MST를 구현하면 된다.
rnk를 사용한 최적화를 하지 않아서 메모리와 시간이 많이 걸린다.
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ans=0;
int n;
int parent[1003];
vector<tuple<int,int,int>> edge;
void init(){
for(int i=1;i<=n;i++){
parent[i]=i;
}
}
int find(int u){
if(u==parent[u]) return u;
return parent[u]=find(parent[u]);
}
void merge(int u, int v){
u=find(u); v=find(v);
if(u==v) return;
parent[u]=v;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n;
init();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x; cin>>x;
if(i>=j) continue;
edge.push_back({x,i,j});
}
}
sort(edge.begin(),edge.end());
for(auto val : edge){
auto[g,u,v]=val;
if(find(u)==find(v)) continue;
merge(u,v);
ans+=g;
}
cout<<ans;
return 0;
}
https://www.acmicpc.net/problem/1647
1647번: 도시 분할 계획
첫째 줄에 집의 개수 N, 길의 개수 M이 주어진다. N은 2이상 100,000이하인 정수이고, M은 1이상 1,000,000이하인 정수이다. 그 다음 줄부터 M줄에 걸쳐 길의 정보가 A B C 세 개의 정수로 주어지는데 A번
www.acmicpc.net
MST를 구하고, 가장 마지막에 이은 간선. 즉, 가장 비용이 비싼 간선을 ans에서 빼주면 답이 나온다.
문제를 잠깐 보고 이런 풀이를 생각했는데 올바른 풀이라 뿌듯했다.
#include<bits/stdc++.h>
using namespace std;
int n,m,ans,lastG;
vector<tuple<int,int,int>> edge;
int parent[100'003];
int rnk[100'003];
void init(){
for(int i=1;i<=n;i++){
parent[i]=i;
rnk[i]=1;
}
}
int find(int u){
if(u==parent[u]) return u;
return parent[u]=find(parent[u]);
}
void merge(int u, int v){
u=find(u); v=find(v);
if(u==v) return;
if(rnk[u]>rnk[v]) swap(u,v);
parent[u]=v;
if(rnk[u]==rnk[v]) rnk[v]++;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n>>m;
init();
while(m--){
int g,u,v; cin>>u>>v>>g;
edge.push_back({g,u,v});
}
sort(edge.begin(),edge.end());
for(auto val : edge){
auto[g,u,v]=val;
if(find(u)==find(v)) continue;
merge(u,v);
ans+=g;
lastG=g;
}
ans-=lastG;
cout<<ans;
return 0;
}
https://www.acmicpc.net/problem/13418
13418번: 학교 탐방하기
입력 데이터는 표준 입력을 사용한다. 입력은 1개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 건물의 개수 N(1 ≤ N ≤ 1,000)과 도로의 개수 M(1 ≤ M ≤ N(N-1)/2) 이 주어진다. 입력의 두 번
www.acmicpc.net
풀다가 이렇게 화가 난 적은 오래간만이었다...
오르막길이 0, 내리막길이 1이므로 가중치가 0인 간선을 만났을 때 카운트를 증가시켜야 하는데, 내리막길일 때 증가시키게 해서 5번 틀렸었다.
주의하자...
#include<bits/stdc++.h>
using namespace std;
int n,m;
int parent[1003];
vector<tuple<int,int,int>> edge;
void init(){
for(int i=0;i<=n;i++){
parent[i]=i;
}
}
int find(int u){
if(u==parent[u]) return u;
return parent[u]=find(parent[u]);
}
void merge(int u, int v){
u=find(u); v=find(v);
if(u==v) return;
parent[v]=u;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n>>m; m++;
while(m--){
int u,v,g; cin>>u>>v>>g;
edge.push_back({g,u,v});
}
//최악의 경우 (오르막(=0) 우선으로 감)
int Worst=0; init();
sort(edge.begin(),edge.end());
for(auto val : edge){
auto[g,u,v]=val;
if(find(u)==find(v)) continue;
merge(u,v);
if(g==0) Worst++;
}
//최적의 경우 (내리막(=1) 우선으로 감)
int Best=0; init();
sort(edge.begin(),edge.end(),greater<>());
for(auto val : edge){
auto[g,u,v]=val;
if(find(u)==find(v)) continue;
merge(u,v);
if(g==0) Best++;
}
cout<<Worst*Worst - Best*Best;
return 0;
}
https://www.acmicpc.net/problem/1774
1774번: 우주신과의 교감
(1,1) (3,1) (2,3) (4,3) 이렇게 우주신들과 황선자씨의 좌표가 주어졌고 1번하고 4번이 연결되어 있다. 그렇다면 1번하고 2번을 잇는 통로를 만들고 3번하고 4번을 잇는 통로를 만들면 신들과 선자씨끼
www.acmicpc.net
n이 1000이라 n(n-1)/2 가 크지 않아 모든 간선을 vector에 넣고 MST를 돌리는 방식으로 구현했다.
n이 작아서 해결이 가능했는데 뒤에 풀 2887 행성 터널에서는 이런 방식이 불가능할 것 같다.
#include<bits/stdc++.h>
using namespace std;
typedef long double ld;
#define x first
#define y second
ld ans;
int n,m;
pair<int,int> node[1003];
vector<tuple<ld,int,int>> edge;
int parent[1003];
void init(){
for(int i=1;i<=n;i++){
parent[i]=i;
}
}
int find(int u){
if(u==parent[u]) return u;
return parent[u]=find(parent[u]);
}
void merge(int u, int v){
u=find(u); v=find(v);
if(u==v) return;
parent[u]=v;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cout<<fixed;
cout.precision(2);
cin>>n>>m;
init();
for(int i=1;i<=n;i++){
int x,y; cin>>x>>y;
node[i]={x,y};
}
while(m--){
int a,b; cin>>a>>b;
merge(a,b);
}
for(int i=1;i<=n-1;i++){
for(int j=i+1;j<=n;j++){
//i와 j를 잇는 간선을 edge에 넣음
ld g = sqrt(pow(node[i].x-node[j].x,2)+pow(node[i].y-node[j].y,2));
edge.push_back({g,i,j});
}
}
sort(edge.begin(),edge.end());
for(auto[g,a,b] : edge){
if(find(a)==find(b)) continue;
ans+=g;
merge(a,b);
}
cout<<ans;
return 0;
}
https://www.acmicpc.net/problem/10423
10423번: 전기가 부족해
첫째 줄에는 도시의 개수 N(1 ≤ N ≤ 1,000)과 설치 가능한 케이블의 수 M(1 ≤ M ≤ 100,000)개, 발전소의 개수 K(1 ≤ K ≤ N)개가 주어진다. 둘째 줄에는 발전소가 설치된 도시의 번호가 주어진다. 셋째
www.acmicpc.net
이미 지어진 발전소끼리 연결하는 가상의 노드를 만들어 해결할 수 있다.
#include<bits/stdc++.h>
using namespace std;
int n,m,k,ans;
int parent[1030];
vector<tuple<int,int,int>> edge;
void init(){
//n+1번 node는 이미 설치되어있는 발전소를 잇는 노드
for(int i=1;i<=n+1;i++){
parent[i]=i;
}
}
int find(int u){
if(parent[u]==u) return u;
return parent[u]=find(parent[u]);
}
void merge(int u, int v){
u=find(u); v=find(v);
if(u==v) return;
parent[u]=v;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n>>m>>k;
init();
for(int i=1;i<=k;i++){
int x; cin>>x;
merge(n+1,x);
}
while(m--){
int u,v,w; cin>>u>>v>>w;
edge.push_back({w,u,v});
}
sort(edge.begin(),edge.end());
for(auto [g,u,v] : edge){
if(find(u)==find(v)) continue;
merge(u,v);
ans+=g;
}
cout<<ans;
return 0;
}
https://www.acmicpc.net/problem/2887
2887번: 행성 터널
첫째 줄에 행성의 개수 N이 주어진다. (1 ≤ N ≤ 100,000) 다음 N개 줄에는 각 행성의 x, y, z좌표가 주어진다. 좌표는 -109보다 크거나 같고, 109보다 작거나 같은 정수이다. 한 위치에 행성이 두 개 이
www.acmicpc.net
n이 10만이므로 최대 50억개의 간선을 살펴보는 경우가 있을 수 있기에 모든 간선을 고려하는 방법은 불가능하다. 그래서 정렬을 통해 이어질 가능성이 있는 간선만 추가해서 MST를 구성해야 한다.
https://github.com/encrypted-def/basic-algo-lecture/blob/master/0x1B/solutions/2887.cpp
해당 코드를 참고해서 코드를 작성했다.
#include<bits/stdc++.h>
using namespace std;
#define Pii pair<int,int>
#define Tiii tuple<int,int,int>
#define x first
#define y second
int n,ans,cnt;
int parent[100003];
Pii arrX[100003];
Pii arrY[100003];
Pii arrZ[100003];
vector<Tiii> edge;
void init(){
for(int i=1;i<=n;i++){
parent[i]=i;
}
}
int find(int u){
if(u==parent[u]) return u;
return parent[u]=find(parent[u]);
}
void merge(int u, int v){
u=find(u); v=find(v);
if(u==v) return;
parent[u]=v;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n;
init();
for(int i=1;i<=n;i++){
int x,y,z; cin>>x>>y>>z;
arrX[i]={x,i};
arrY[i]={y,i};
arrZ[i]={z,i};
}
sort(arrX+1,arrX+n+1);
sort(arrY+1,arrY+n+1);
sort(arrZ+1,arrZ+n+1);
for(int i=2;i<=n;i++){
edge.push_back({abs(arrX[i-1].x-arrX[i].x),arrX[i-1].y,arrX[i].y});
edge.push_back({abs(arrY[i-1].x-arrY[i].x),arrY[i-1].y,arrY[i].y});
edge.push_back({abs(arrZ[i-1].x-arrZ[i].x),arrZ[i-1].y,arrZ[i].y});
}
sort(edge.begin(),edge.end());
for(auto[g,u,v] : edge){
if(find(u)==find(v)) continue;
ans+=g;
cnt++;
merge(u,v);
//if(cnt==n) break;
}
cout<<ans;
return 0;
}
'코테연습일지 > 그래프 이론' 카테고리의 다른 글
10775 공항 (Union Find) (0) | 2024.04.15 |
---|---|
3665 최종 순위 (위상정렬) (1) | 2024.04.15 |
1194 달이 차오른다, 가자 (BFS/비트마스킹) (0) | 2024.04.13 |
14395 4연산 (BFS/Set) (1) | 2024.04.13 |
Union Find (0) | 2024.04.11 |