Cpp/모두의코드

씹어먹는 C++ - <4 - 2. 클래스의 세계로 오신 것을 환영합니다. (함수의 오버로딩, 생성자)>

둠치킨 2022. 1. 16. 23:34

생각 해볼 문제(modoocode C++)

문제 1

Date 클래스에 여러가지 생성자들을 추가해보세요 (난이도 : 下)

 

-> 이미 저번 4-1 글에서 생성자를 추가한 코드로 올렸으므로 패스!

 

문제 2

수학 관련 소프트웨어를 만드는 회사에서 의뢰가 들어왔습니다. 중학생용 기하학 소프트웨워를 만드는 것인데요, 클래스는 총 두 개로 하나는 Point 로 점에 관한 정보를 담는 것이고 다른 하나는 Geometry 로 점들을 가지고 연산을 하는 클래스 입니다. 즉 아래와 같은 두 클래스의 함수들을 모두 정의하세요 (난이도 : 上)

class Point {
  int x, y;

 public:
  Point(int pos_x, int pos_y);
};

class Geometry {
 public:
  Geometry() {
    num_points = 0;
  }

  void AddPoint(const Point &point) {
    point_array[num_points ++] = new Point(point.x, point.y);
  }

  // 모든 점들 간의 거리를 출력하는 함수 입니다.
  void PrintDistance();

  // 모든 점들을 잇는 직선들 간의 교점의 수를 출력해주는 함수 입니다.
  // 참고적으로 임의의 두 점을 잇는 직선의 방정식을 f(x,y) = ax+by+c = 0
  // 이라고 할 때 임의의 다른 두 점 (x1, y1) 과 (x2, y2) 가 f(x,y)=0 을 기준으로
  // 서로 다른 부분에 있을 조건은 f(x1, y1) * f(x2, y2) <= 0 이면 됩니다.
  void PrintNumMeets();

private:
  // 점 100 개를 보관하는 배열.
  Point* point_array[100];
  int num_points;
};

풀이

와... 이거 진짜 어려웠는데... 클래스를 세우는 것과 생성자를 추가하는 것이 어려운게 아니라 풀이를 생각하는 것과 구현이 진짜로 복잡한 문제다. 사실 제대로 했는지도 모르겠다... 일단 PrintNumMeets()가 가장 복잡하다. 직선의 방정식들이 교점이 되는 곳들을 세는 함수다. PrintNumMeets() 구현은 위의 주어진 코드 그대로 f(x,y) = ax+by+c = 0 에서 두 점에 관해서 a,b,c 계수의 값을 구하고 f(x,y)를 완성해서 f(x1,y1) * f(x2,y2) <= 0 인지 확인하는 것이다. 코드는 모두의코드 4-2 글의 댓글에 있는 여러분의 코드도 보고, 그 중 엄정용님의 코드를 참조했는데, 내가 보기엔 이 분의 코드도 정답을 제대로 출력하지 않아서... 좀 고친 부분이 있다.

 

다른 분들의 코드를 본 결과 pos1(1, 2), pos2(3, 1), pos3(-1, -4), pos4(5,2)를 입력해서 PrintNumMeets()의 결과를 출력했을 때 직선들의 교점은 7개가 나와야하는데 다른 분들의 답은 전부 이 값과 다른 값이 나와서 내 코드를 구현한 후 대조를 할 때 내 답이 맞는지 확인할 수가 없어서... 불안하지만 어쩔 수 없다 넘어가자!

#include <iostream>
#include <cmath>

class Point {
	int x, y;

public:
	Point(int pos_x, int pos_y) : x(pos_x), y(pos_y)
	{ }

	int GetX() const { return x; }
	int GetY() const { return y; }
};

class Geometry {
private:
	Point* point_array[100];	// 점 100개를 보관하는 배열
	int num_points;
public:
	Geometry() : num_points(0)
	{ }
	~Geometry() 
	{
        for (int i = 0; i < num_points; i++)
            delete point_array[i];
    }
	void AddPoint(const Point& point) {
		point_array[num_points++] = new Point(point.GetX(), point.GetY());
	}

	// 모든 점들 간의 거리를 출력하는 함수입니다.
	void PrintDistance();

	// 모든 점들을 잇는 직선들 간의 교점의 수를 출력해주는 함수입니다.
	void PrintNumMeets();
};

void Geometry::PrintDistance()
{
	for (int i = 0; i < num_points-1; i++)
	{
		for (int j = i + 1; j < num_points; j++)
		{
			std::cout << i + 1 << "번째 점과 " << j + 1 << "번째 점 사이의 거리는 "
				<< sqrt(pow(point_array[i]->GetX() - point_array[j]->GetX(), 2)+ pow(point_array[i]->GetY() - point_array[j]->GetY(), 2))<<"입니다.\n";
		}
	}
}

void Geometry::PrintNumMeets()
{
    // 만들 수 있는 직선은 총 num_points*(num_points-1)/2 개이다.
    // 직선의 방정식 계수 a, b, c를 0으로 초기화
    // 직선의 방정식 : a*x + b*y + c = 0    
    const int NUM_OF_LINES = num_points*(num_points-1)/2;

    // 첫번째[0] : 직선의 방정식 계수 a
    // 두번째[1] : 직선의 방정식 계수 b
    // 세번째[2] : 직선의 방정식 계수 c
    // 네번째[3] : 직선의 방정식을 만드는 point1의 index
    // 다섯번째[4] : 직선의 방정식을 만드는 point2의 index
    int coefficients[100000][5] = {0, };

    if (NUM_OF_LINES > 100000)
	{
        std::cout << "직선의 개수가 너무 많아 계산이 불가합니다." << std::endl;
        return;
    }

    // 직선의 방정식 계수 a, b, c를 구한다.
    int x1 = 0, y1 = 0;
    int x2 = 0, y2 = 0;
    int nth_line = 0;
    for (int i = 0; i < num_points -1; i++) {
        for (int j = 0; j < num_points; j++) {
            x1 = point_array[i]->GetX();
            y1 = point_array[i]->GetY();
            x2 = point_array[j]->GetX();
            y2 = point_array[j]->GetY();

            coefficients[nth_line][0] = y2 - y1; // a
            coefficients[nth_line][1] = x1 - x2; // b
            coefficients[nth_line][2] = x1*y1 - 2*x1*y2 + x2*y2; // c
            coefficients[nth_line][3] = i;
            coefficients[nth_line][4] = j;
            nth_line++;
        }
    }


    // 직선이 만나는 포인트의 개수를 구한다.
    int sum_of_meets = 0;
    for (int i = 0; i < NUM_OF_LINES; i++) {
        for (int j = 0; j < num_points - 1; j++) {
            for (int k = j+1 ; k < num_points; k++) {
                // 직선의 방정식을 세운 포인트는 넘어가기
                if (coefficients[i][3] == j || coefficients[i][3] == k) {
                    continue;
                }

                x1 = point_array[j]->GetX();
                y1 = point_array[j]->GetY();
                x2 = point_array[k]->GetX();
                y2 = point_array[k]->GetY();
                
                // 두 포인트가 직선 기준으로 서로 반대면에 있는지 확인
                if ((coefficients[i][0]*x1 + coefficients[i][1]*y1 + coefficients[i][2]) * (coefficients[i][0]*x2 + coefficients[i][1]*y2 + coefficients[i][2]) <= 0) {
                    sum_of_meets++;
                }
            }
        }
    }   

    std::cout << std::endl;
    std::cout << "직선의 개수는 " << NUM_OF_LINES << "개 입니다." << std::endl;
	if(sum_of_meets%2!=0)
    	std::cout << "직선의 방정식이 교차하는 점의 개수는 " << sum_of_meets/2 + 1 << "개 이다." << '\n';
	else
		std::cout << "직선의 방정식이 교차하는 점의 개수는 " << sum_of_meets/2 << "개 이다." << '\n';
}



int main() {
	// 테스트
	Point pos1(1, 2), pos2(3, 1), pos3(-1, -4), pos4(5,2);
	Geometry Geo;
	Geo.AddPoint(pos1);
	Geo.AddPoint(pos2);
	Geo.AddPoint(pos3);
	Geo.AddPoint(pos4);
	
	Geo.PrintDistance();
	Geo.PrintNumMeets();

	return 0;
}