넘치게 채우기

[BOJ] 2162 - 선분 그룹 본문

PS/BOJ

[BOJ] 2162 - 선분 그룹

riveroverflow 2025. 2. 4. 10:12
728x90
반응형

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

BOJ - 선분 그룹

문제 유형: CCW, 선형대수, 기하학, 유니온-파인드, 분리 집합

문제 난이도: Platinum V

시간 제한: 2초

메모리 제한: 128MB

 

문제

N개의 선분들이 2차원 평면상에 주어져 있다. 선분은 양 끝점의 x, y 좌표로 표현이 된다.

두 선분이 서로 만나는 경우에, 두 선분은 같은 그룹에 속한다고 정의하며, 그룹의 크기는 그 그룹에 속한 선분의 개수로 정의한다. 두 선분이 만난다는 것은 선분의 끝점을 스치듯이 만나는 경우도 포함하는 것으로 한다.

N개의 선분들이 주어졌을 때, 이 선분들은 총 몇 개의 그룹으로 되어 있을까? 또, 가장 크기가 큰 그룹에 속한 선분의 개수는 몇 개일까? 이 두 가지를 구하는 프로그램을 작성해 보자.

 

입력

첫째 줄에 N(1 ≤ N ≤ 3,000)이 주어진다. 둘째 줄부터 N+1번째 줄에는 양 끝점의 좌표가 x1, y1, x2, y2의 순서로 주어진다. 각 좌표의 절댓값은 5,000을 넘지 않으며, 입력되는 좌표 사이에는 빈칸이 하나 있다.

 

출력

첫째 줄에 그룹의 수를, 둘째 줄에 가장 크기가 큰 그룹에 속한 선분의 개수를 출력한다.

 

풀이

CCW에 대한 이해는 여기서:

https://riveroverflow.tistory.com/entry/BOJ-17387-%EC%84%A0%EB%B6%84-%EA%B5%90%EC%B0%A8-2

 

[BOJ] 17387 - 선분 교차 2

https://www.acmicpc.net/problem/17387BOJ - 선분 교차 2문제 유형: 선형대수, 수학, 기하학문제 난이도: Gold II시간 제한: 0.25초메모리 제한: 512MB 문제2차원 좌표 평면 위의 두 선분 L1, L2가 주어졌을 때, 두

riveroverflow.tistory.com

 

 

CCW를 통해서 선분들 간의 교차관계를 구한다.

ccw1 = ccw(선분i의 두 점, 선분 j의 점 1)

ccw2 = ccw(선분i의 두 점, 선분 j의 점 2)

ccw3 = ccw(선분j의 두 점, 선분 i의 점 1)

ccw4 = ccw(선분j의 두 점, 선분 i의 점 2)

를 구한다.

ccw1 * ccw2 < 0 && ccw3 * ccw4가 음수라면 엄밀히 교차한다.

ccw1 == 0이고, 선분j의 점 1이 선분 i위에 있다면, 같은 기울기의 선분 i, j가 이어져있는 형태이다.

ccw2 == 0이고, 선분j의 점 2가 선분 i위에 있다면, 같은 기울기의 선분 i, j가 이어져있는 형태이다.

ccw3 == 0이고, 선분i의 점 1이 선분 j위에 있다면, 같은 기울기의 선분 i, j가 이어져있는 형태이다.

ccw4 == 0이고, 선분i의 점 2가 선분 j위에 있다면, 같은 기울기의 선분 i, j가 이어져있는 형태이다.

 

교차한다면 유니온파인드로 합치고, 병합에 성공 시 요소의 개수까지 합친다.

최종적으로 parent[i]가 i인, 즉 루트들에 대해서 분리집합의 개수와 최대 크기를 갱신해주고, 출력한다.

 

코드

C++

#include <bits/stdc++.h>
#define ll long long
#define pll pair<ll, ll>

using namespace std;

struct Line {
  int x1;
  int y1;
  int x2;
  int y2;
  Line(int x1, int y1, int x2, int y2) {
    this->x1 = x1;
    this->y1 = y1;
    this->x2 = x2;
    this->y2 = y2;
  }
};

int ccw(ll x1, ll y1, ll x2, ll y2, ll x3, ll y3) {
  ll res = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1);
  if (res > 0)
    return 1;
  if (res < 0)
    return -1;
  return 0;
}

bool isPoint(int x, int y, int x1, int y1, int x2, int y2) {
  return x >= min(x1, x2) && x <= max(x1, x2) && y >= min(y1, y2) &&
         y <= max(y1, y2);
}

bool isIntersect(int i, int j, vector<Line *> &lines) {
  int ccw1 = ccw(lines[i]->x1, lines[i]->y1, lines[i]->x2, lines[i]->y2,
                 lines[j]->x1, lines[j]->y1);
  int ccw2 = ccw(lines[i]->x1, lines[i]->y1, lines[i]->x2, lines[i]->y2,
                 lines[j]->x2, lines[j]->y2);
  int ccw3 = ccw(lines[j]->x1, lines[j]->y1, lines[j]->x2, lines[j]->y2,
                 lines[i]->x1, lines[i]->y1);
  int ccw4 = ccw(lines[j]->x1, lines[j]->y1, lines[j]->x2, lines[j]->y2,
                 lines[i]->x2, lines[i]->y2);

  if (ccw1 * ccw2 < 0 && ccw3 * ccw4 < 0)
    return true;

  if (ccw1 == 0 && isPoint(lines[j]->x1, lines[j]->y1, lines[i]->x1,
                           lines[i]->y1, lines[i]->x2, lines[i]->y2))
    return true;
  if (ccw2 == 0 && isPoint(lines[j]->x2, lines[j]->y2, lines[i]->x1,
                           lines[i]->y1, lines[i]->x2, lines[i]->y2))
    return true;
  if (ccw3 == 0 && isPoint(lines[i]->x1, lines[i]->y1, lines[j]->x1,
                           lines[j]->y1, lines[j]->x2, lines[j]->y2))
    return true;
  if (ccw4 == 0 && isPoint(lines[i]->x2, lines[i]->y2, lines[j]->x1,
                           lines[j]->y1, lines[j]->x2, lines[j]->y2))
    return true;
  return false;
}

int getRoot(int x, vector<int> &parent) {
  if (parent[x] == x)
    return x;
  return parent[x] = getRoot(parent[x], parent);
}

void merge(int u, int v, vector<int> &parent, vector<int> &componentSize) {
  int uRoot = getRoot(u, parent);
  int vRoot = getRoot(v, parent);

  if (uRoot == vRoot)
    return;

  if (uRoot < vRoot) {
    parent[vRoot] = uRoot;
    componentSize[uRoot] += componentSize[vRoot];
  } else {
    parent[uRoot] = vRoot;
    componentSize[vRoot] += componentSize[uRoot];
  }
}

int main(int argc, char *argv[]) {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  int n;
  cin >> n;

  vector<Line *> lines(n);
  vector<int> parent(n);
  vector<int> componentSize(n, 1);
  for (int i = 0; i < n; ++i) {
    int x1, y1, x2, y2;
    cin >> x1 >> y1 >> x2 >> y2;
    lines[i] = new Line(x1, y1, x2, y2);
    parent[i] = i;
  }

  for (int i = 0; i < n; ++i) {
    for (int j = i + 1; j < n; ++j) {
      if (isIntersect(i, j, lines)) {
        merge(i, j, parent, componentSize);
      }
    }
  }

  int count = 0;
  int maxSize = 0;
  for (int i = 0; i < n; ++i) {
    if (parent[i] == i) {
      count++;
      maxSize = max(maxSize, componentSize[i]);
    }
  }

  cout << count << "\n" << maxSize << "\n";

  return 0;
}

 

Go

package main

import (
	"bufio"
	"fmt"
	"os"
)

var reader *bufio.Reader
var writer *bufio.Writer

type Line struct {
	x1 int64
	y1 int64
	x2 int64
	y2 int64
}

func min(a, b int64) int64 {
	if a < b {
		return a
	}
	return b
}

func max(a, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

func ccw(x1, y1, x2, y2, x3, y3 int64) int64 {
	x := (x2-x1)*(y3-y1) - (y2-y1)*(x3-x1)
	if x > 0 {
		return 1
	} else if x < 0 {
		return -1
	} else {
		return 0
	}
}

func isIntersect(i, j int64, lines []Line) bool {
	ccw1 := ccw(lines[i].x1, lines[i].y1, lines[i].x2, lines[i].y2, lines[j].x1, lines[j].y1)
	ccw2 := ccw(lines[i].x1, lines[i].y1, lines[i].x2, lines[i].y2, lines[j].x2, lines[j].y2)
	ccw3 := ccw(lines[j].x1, lines[j].y1, lines[j].x2, lines[j].y2, lines[i].x1, lines[i].y1)
	ccw4 := ccw(lines[j].x1, lines[j].y1, lines[j].x2, lines[j].y2, lines[i].x2, lines[i].y2)

	if ccw1*ccw2 < 0 && ccw3*ccw4 < 0 {
		return true
	}
	if ccw1 == 0 &&
		lines[j].x1 >= min(lines[i].x1, lines[i].x2) && lines[j].x1 <= max(lines[i].x1, lines[i].x2) &&
		lines[j].y1 >= min(lines[i].y1, lines[i].y2) && lines[j].y1 <= max(lines[i].y1, lines[i].y2) {
		return true
	}
	if ccw2 == 0 &&
		lines[j].x2 >= min(lines[i].x1, lines[i].x2) && lines[j].x2 <= max(lines[i].x1, lines[i].x2) &&
		lines[j].y2 >= min(lines[i].y1, lines[i].y2) && lines[j].y2 <= max(lines[i].y1, lines[i].y2) {
		return true
	}
	if ccw3 == 0 &&
		lines[i].x1 >= min(lines[j].x1, lines[j].x2) && lines[i].x1 <= max(lines[j].x1, lines[j].x2) &&
		lines[i].y1 >= min(lines[j].y1, lines[j].y2) && lines[i].y1 <= max(lines[j].y1, lines[j].y2) {
		return true
	}
	if ccw4 == 0 &&
		lines[i].x2 >= min(lines[j].x1, lines[j].x2) && lines[i].x2 <= max(lines[j].x1, lines[j].x2) &&
		lines[i].y2 >= min(lines[j].y1, lines[j].y2) && lines[i].y2 <= max(lines[j].y1, lines[j].y2) {
		return true
	}

	return false
}

func getRoot(x int64, parent []int64) int64 {
	if parent[x] == x {
		return x
	}
	parent[x] = getRoot(parent[x], parent)
	return parent[x]
}

func merge(u, v int64, parent, componentSizes []int64) {
	uRoot := getRoot(u, parent)
	vRoot := getRoot(v, parent)

	if uRoot == vRoot {
		return
	}

	if uRoot < vRoot {
		componentSizes[uRoot] += componentSizes[vRoot]
		parent[vRoot] = uRoot
	} else {
		componentSizes[vRoot] += componentSizes[uRoot]
		parent[uRoot] = vRoot
	}
}

func main() {
	reader = bufio.NewReader(os.Stdin)
	writer = bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	var n int64
	fmt.Fscan(reader, &n)
	var x1, y1, x2, y2 int64
	lines := make([]Line, n)
	parent := make([]int64, n)
	componentSizes := make([]int64, n)

	for i := int64(0); i < n; i++ {
		fmt.Fscan(reader, &x1, &y1, &x2, &y2)
		lines[i] = Line{x1, y1, x2, y2}
		parent[i] = i
		componentSizes[i] = 1
	}

	for i := int64(0); i < n; i++ {
		for j := i + 1; j < n; j++ {
			if isIntersect(i, j, lines) {
				merge(i, j, parent, componentSizes)
			}
		}
	}

	count := int64(0)
	maxSize := int64(0)
	for i := int64(0); i < n; i++ {
		if parent[i] == i {
			count++
			if componentSizes[i] > maxSize {
				maxSize = componentSizes[i]
			}
		}
	}

	fmt.Fprintln(writer, count)
	fmt.Fprintln(writer, maxSize)
}
728x90
반응형

'PS > BOJ' 카테고리의 다른 글

[BOJ] 16566 - 카드 게임  (0) 2025.02.06
[BOJ] 17143 - 낚시왕  (0) 2025.02.05
[BOJ] 15649 - N과 M (1)  (0) 2025.02.03
[BOJ] 2583 - 영역 구하기  (0) 2025.02.03
[BOJ] 7562 - 나이트의 이동  (0) 2025.02.03