문제 정의
2차원 평면 위의 점들을 나타내는 두 변수 n과 m이 주어진다고 가정해 봅시다. 전체 n개의 점 중 m개의 점은 한 직선 위에 놓여 있습니다. 우리의 목표는 이 n개의 점을 이용해 만들 수 있는 삼각형의 개수를 구하는 것입니다.
일직선상의 점(Collinear Points)이란 같은 직선 위에 위치한 점들을 의미합니다. 아래 그림에서 점 A와 B는 서로 일직선상에 있습니다.

접근 방식: 조합 공식 활용
n=4(점 A, B, C, D), m=2(점 A, B)인 경우를 예로 들어 살펴보겠습니다.
- 4개의 점 중 임의로 3개를 선택하는 경우의 수 = 4C3
- 단, 일직선상의 점들은 삼각형을 형성할 수 없으므로 앞서 계산한 경우의 수에서 제외해야 함 = 2C3
- 따라서 총 삼각형의 수 = 4C3 − 2C3 = 4 − 0 = 4 (ABC, ACD, BCD, ABD)
일반화하면, n과 m이 주어졌을 때 만들 수 있는 삼각형의 개수는 nC3 − mC3 공식으로 구할 수 있습니다.
예제
예제 1
입력 − n=5, m=3
출력 − 총 n개의 점 중 m개가 일직선상에 있을 때 만들 수 있는 삼각형의 개수 − 9
설명 − 총 삼각형 수 = 5C3 − 3C3 = 10 − 1 = 9
예제 2
입력 − n=10, m=5
출력 − 총 n개의 점 중 m개가 일직선상에 있을 때 만들 수 있는 삼각형의 개수 − 110
설명 − 총 삼각형 수 = 10C3 − 5C3 = 120 − 10 = 110
프로그램에서 사용된 알고리즘
조합(combination) 값을 계산하기 위해 파스칼 삼각형(Pascal Triangle)을 활용합니다. 파스칼 삼각형의 각 행은 이전 행의 값들을 더하는 방식으로 채워지며, 이를 통해 nCr 값을 효율적으로 구할 수 있습니다.
- 점의 개수를 나타내는 변수 n과 m을 입력받습니다.
- 함수 collinear_points(int n, int m)는 n과 m을 인자로 받아 총 n개의 점 중 m개가 일직선상에 있을 때 만들 수 있는 삼각형의 개수를 반환합니다.
- count = check(n, 3) − check(m, 3)으로 설정합니다. (nC3 − mC3 계산)
- 함수 check(int n, int r)는 n과 r을 받아 nCr 값을 반환합니다.
- 길이가 r+1인 배열 arr를 선언합니다.
- memset을 사용하여 배열 전체를 0으로 초기화합니다.
- arr[0] = 1로 설정합니다.
- i=1부터 i≤n까지, 그리고 j=min(i, r)부터 j>0까지 두 개의 for 루프를 돌며 arr[j] = arr[j] + arr[j-1] 연산으로 파스칼 삼각형을 채워 나갑니다.
- 최종적으로 arr[r]이 nCr 값이 되며, 이를 반환합니다.
- check() 함수 호출이 완료되면 삼각형의 개수를 얻을 수 있습니다.
- count를 결과값으로 반환합니다.
C++ 구현 예제
#include <bits/stdc++.h>
using namespace std;
int check(int n, int r){
int arr[r+1];
memset(arr, 0, sizeof(arr));
arr[0] = 1;
for (int i = 1; i <= n; i++){
for (int j = min(i, r); j > 0; j--){
arr[j] = arr[j] + arr[j-1];
}
}
return arr[r];
}
int collinear_points(int n,int m){
int count = check(n, 3) - check(m, 3);
return count;
}
int main(){
int n = 6, m = 2;
cout<<"Count of triangles with total n points with m collinear are: "<< collinear_points(n, m);
return 0;
}출력 결과
위 코드를 실행하면 다음과 같은 결과가 출력됩니다.
Count of triangles with total n points with m collinear are: 20
마무리
이 알고리즘은 팩토리얼을 직접 곱하는 대신 파스칼 삼각형을 덧셈 기반의 동적 프로그래밍 방식으로 채워 나가기 때문에, 중간 계산 과정에서 값이 지나치게 커지는 오버플로우 위험을 줄일 수 있습니다. 또한 시간 복잡도는 O(n × r)로, 점의 개수가 많아져도 안정적으로 조합 값을 계산할 수 있다는 장점이 있습니다.