Algorithm/πŸ‘ 문제

[PICNIC] μ†Œν’ 문제

YURIπŸ•πŸ“πŸΆ 2019. 7. 8. 19:17
λ°˜μ‘ν˜•

문제

μ•ˆλ“œλ‘œλ©”λ‹€ μœ μΉ˜μ› μ΅μŠ€ν”„λ ˆμŠ€λ°˜μ—μ„œλŠ” λ‹€μŒ 주에 μœ¨λ™κ³΅μ›μœΌλ‘œ μ†Œν’μ„ κ°‘λ‹ˆλ‹€. 원석 μ„ μƒλ‹˜μ€ μ†Œν’ λ•Œ 학생듀을 두 λͺ…μ”© 짝을 μ§€μ–΄ ν–‰λ™ν•˜κ²Œ ν•˜λ €κ³  ν•©λ‹ˆλ‹€. 그런데 μ„œλ‘œ μΉœκ΅¬κ°€ μ•„λ‹Œ 학생듀끼리 짝을 μ§€μ–΄ μ£Όλ©΄ μ„œλ‘œ μ‹Έμš°κ±°λ‚˜ 같이 λŒμ•„λ‹€λ‹ˆμ§€ μ•ŠκΈ° λ•Œλ¬Έμ—, 항상 μ„œλ‘œ 친ꡬ인 ν•™μƒλ“€λΌλ¦¬λ§Œ 짝을 μ§€μ–΄ μ€˜μ•Ό ν•©λ‹ˆλ‹€.

각 ν•™μƒλ“€μ˜ μŒμ— λŒ€ν•΄ 이듀이 μ„œλ‘œ μΉœκ΅¬μΈμ§€ μ—¬λΆ€κ°€ μ£Όμ–΄μ§ˆ λ•Œ, 학생듀을 짝지어쀄 수 μžˆλŠ” λ°©λ²•μ˜ 수λ₯Ό κ³„μ‚°ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜μ„Έμš”. 짝이 λ˜λŠ” 학생듀이 μΌλΆ€λ§Œ λ‹€λ₯΄λ”라도 λ‹€λ₯Έ 방법이라고 λ΄…λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ λ‹€μŒ 두 κ°€μ§€ 방법은 μ„œλ‘œ λ‹€λ₯Έ λ°©λ²•μž…λ‹ˆλ‹€.

  • (νƒœμ—°,μ œμ‹œμΉ΄) (μ¨λ‹ˆ,ν‹°νŒŒλ‹ˆ) (νš¨μ—°,유리)
  • (νƒœμ—°,μ œμ‹œμΉ΄) (μ¨λ‹ˆ,유리) (νš¨μ—°,ν‹°νŒŒλ‹ˆ)

μž…λ ₯

μž…λ ₯의 첫 μ€„μ—λŠ” ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ˜ 수 C (C <= 50) κ°€ μ£Όμ–΄μ§‘λ‹ˆλ‹€. 각 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ˜ 첫 μ€„μ—λŠ” ν•™μƒμ˜ 수 n (2 <= n <= 10) κ³Ό 친ꡬ 쌍의 수 m (0 <= m <= n*(n-1)/2) 이 μ£Όμ–΄μ§‘λ‹ˆλ‹€. κ·Έ λ‹€μŒ 쀄에 m 개의 μ •μˆ˜ 쌍으둜 μ„œλ‘œ 친ꡬ인 두 ν•™μƒμ˜ λ²ˆν˜Έκ°€ μ£Όμ–΄μ§‘λ‹ˆλ‹€. λ²ˆν˜ΈλŠ” λͺ¨λ‘ 0 λΆ€ν„° n-1 μ‚¬μ΄μ˜ μ •μˆ˜μ΄κ³ , 같은 μŒμ€ μž…λ ₯에 두 번 μ£Όμ–΄μ§€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ν•™μƒλ“€μ˜ μˆ˜λŠ” μ§μˆ˜μž…λ‹ˆλ‹€.

좜λ ₯

각 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λ§ˆλ‹€ ν•œ 쀄에 λͺ¨λ“  학생을 친ꡬ끼리만 짝지어쀄 수 μžˆλŠ” λ°©λ²•μ˜ 수λ₯Ό 좜λ ₯ν•©λ‹ˆλ‹€.

예제 μž…λ ₯

3  
2 1  
0 1  
4 6  
0 1 1 2 2 3 3 0 0 2 1 3  
6 10  
0 1 0 2 1 2 1 3 1 4 2 3 2 4 3 4 3 5 4 5

예제 좜λ ₯



4

 


문제 풀이

μ™„μ „ 탐색을 μ΄μš©ν•΄ 쑰합을 λͺ¨λ‘ λ§Œλ“€μ–΄ 보자. 

아직 짝을 μ°Ύμ§€ λͺ»ν•œ ν•™μƒλ“€μ˜ λͺ…단이 μ£Όμ–΄μ§ˆ λ•Œ 친ꡬ끼리 λ‘˜μ”© μ§μ§“λŠ” 경우의 수λ₯Ό 계산해라.

  1. 짝을 지은 ν•™μƒλ“€μ˜ λͺ…단 - taken

  2. 친ꡬ인 ν•™μƒλ“€μ˜ λͺ…단 - areFriends

/* 친ꡬ인 학생듀 끼리 짝을 μ§€μ–΄μ£ΌκΈ° */
/* 2019-07-08 μ†Œν’ */

#include <iostream>
using namespace std;
bool areFriends[10][10]; //ν•™μƒμ˜ μˆ˜κ°€ μ΅œλŒ€ 10λͺ…이기 λ•Œλ¬Έμ—

int findPair(int n, bool *taken) {
	//taken - 짝을 κ΅¬ν–ˆλŠ”κ°€ ?
	//n - 학생 수
	int first = -1;
	for (int i = 0; i < n; i++) {
		if (!taken[i]) {
			first = i; // κ°€μž₯ 첫번째둜 짝을 κ΅¬ν•˜μ§€ λͺ»ν•œ μ‚¬λžŒ
			break;
		}
	}
	if (first == -1) return 1; // λ‹€ 짝을 κ΅¬ν–ˆλ‹€λ©΄ 1을 return ν•œλ‹€.
	int ret = 0;

	for (int i = first + 1; i < n; i++) { // 짝을 μ •ν•  μ‚¬λžŒ μ°ΎκΈ°
		if (!taken[i] && areFriends[first][i]) { //이 μΉœκ΅¬λ„ 짝이 μ—†κ³  μ„œλ‘œ 친ꡬ라면
			taken[i] = taken[first] = true; // 짝을 ꡬ함
			ret += findPair(n, taken); // 남은 짝 κ³ λ₯΄λŠ” μž‘μ—…μ„ μž¬κ·€λ‘œ λ– λ„˜κΈ΄ 것. λͺ¨λ‘ 짝을 κ΅¬ν–ˆλ‹€λ©΄ 1이 μ•„λ‹ˆλ©΄ 0이 return λ˜μ—ˆμ„ 것이닀.
			taken[i] = taken[first] = false; // λ‹€λ₯Έ 방법도 μžˆμ„ 수 μžˆμœΌλ‹ˆ λ‹€μ‹œ μ΄ˆκΈ°ν™” ν•΄μ€€λ‹€
		}
	}
	return ret;
}

int main() {
	int c; // ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ˜ 수
	int n, m; // ν•™μƒμ˜ 수, 친ꡬ 쌍의 수
	cin >> c;
	if (c > 50) return -1 ;

	for (int i = 0; i < c; i++) {
		int t1, t2;
		bool taken[10] = { false, };
		for (int i = 0; i < 10; i++)
		{
			for (int j = 0; j < 10; j++)
			{
				areFriends[i][j] = false;
			}
		}
		cin >> n >> m;
		//ν•™μƒμ˜ μˆ˜λŠ” 2~10
		for (int j = 0; j < m; j++) { // 친ꡬ쌍 μ €μž₯ν•˜κΈ°
			cin >> t1 >> t2;
			areFriends[t1][t2] = true;
			areFriends[t2][t1] = true;
		}
		cout << findPair(n, taken) << endl;
	}
	return 0;
}

 

 

μ•Œκ³ λ¦¬μ¦˜ λ¬Έμ œν•΄κ²° μ „λž΅ 1의 μ½”λ“œλ₯Ό μ΄μš©ν•΄μ„œ ν’€μ—ˆλ‹€. μ²˜μŒμ—” ν•™μƒμ˜ μˆ˜μ—μ„œ 2개λ₯Ό λͺ¨λ“  쑰합을 μ°ΎλŠ” μ•Œκ³ λ¦¬μ¦˜μ„ μ‚¬μš©ν•˜λ €κ³  ν–ˆμœΌλ‚˜ λ‹¨μˆœνžˆ 짝을 κ΅¬ν•˜λŠ” 것이 μ•„λ‹ˆλΌ 학생듀을 짝 지을 수 μžˆλŠ” λ°©λ²•μ˜ 수λ₯Ό ꡬ해야 ν–ˆκΈ° λ•Œλ¬Έμ— 책을 μ°Έκ³ ν–ˆλ‹€. 

->μ—¬κΈ°μ„œ μ‚¬μš©ν•œ μ•Œκ³ λ¦¬μ¦˜λ„ λΉ„μŠ·ν•œ λ₯˜μž„을 κΉ¨λ‹¬μ•˜λ‹€.

μ±…μ—μ„œλŠ” 쀑볡 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ κ°€μž₯ λ²ˆν˜Έκ°€ λΉ λ₯Έ ν•™μƒμ˜ 짝을 κ΅¬ν•˜λŠ” 방법을 μ±„νƒν–ˆκ³ , κ·Έλ ‡κΈ° λ•Œλ¬Έμ— findPair ν•¨μˆ˜μ—μ„œλŠ” κ°€μž₯ 첫 번째둜 짝을 κ΅¬ν•˜μ§€ λͺ»ν•œ μ‚¬λžŒμ„ μ°Ύμ•„λ‚Έλ‹€. μ΄λ•Œ λͺ¨λ“  μ‚¬λžŒμ΄ 짝을 κ΅¬ν–ˆλ‹€λ©΄ ν•˜λ‚˜μ˜ 방법을 찾은 것이기 λ•Œλ¬Έμ— 1을 return ν•œλ‹€. 이 return 된 1은 μΆ”ν›„ ret에 더해진닀. 


μ²˜μŒμ— 0λΆ€ν„° 짝 κ΅¬ν•˜κΈ°λ₯Ό μ‹œμž‘ν•œλ‹€.  0의 짝이 될 μ‚¬λžŒμ„ μ°ΎλŠ”λ° 자기 μžμ‹ κ³ΌλŠ” μΉœκ΅¬κ°€ 될 수 μ—†κΈ° λ•Œλ¬Έμ— 1λΆ€ν„° 짝이 될 수 μžˆλŠ”μ§€ νŒλ³„ν•œλ‹€. 짝이 될 수 μžˆλ‹€λ©΄ 짝을 κ΅¬ν–ˆλ‹€κ³  check ν•œ ν›„, λ‹€μ‹œ λ‚˜λ¨Έμ§€ ν•™μƒλ“€μ˜ 짝을 κ΅¬ν•˜κΈ° μœ„ν•΄μ„œ μž¬κ·€λ‘œ 자기 μžμ‹ μ„ ν˜ΈμΆœν•œλ‹€.

λ§Œμ•½ 0κ³Ό 1이 짝이라고 ν•˜λ©΄ 2번 친ꡬ의 짝을 찾게 될 것이닀. 2번 ν•™μƒμ˜ 짝이 μ—†λ‹€λ©΄ λͺ¨λ“  for문을 돌고 ret을 λ°˜ν™˜ν•  텐데 μ΄λ•Œ ret은 0이 λœλ‹€. μ΄λŠ” μ§μ§“λŠ” 것이 μ‹€νŒ¨ν–ˆμŒμ„ λœ»ν•œλ‹€. ν•˜μ§€λ§Œ 2번 학생이 3번 학생과 친ꡬ라면 짝을 지을 수 μžˆλ‹€. 이 κ²½μš°μ—λŠ” if문을 λ§Œμ‘±μ‹œμΌœ λ‹€μ‹œ μž¬κ·€λ‘œ 자기 μžμ‹ μ„ ν˜ΈμΆœν•œλ‹€. λ‹€μ‹œ 호좜된 findpair ν•¨μˆ˜κ°€ 더 이상 짝을 ꡬ할 μ‚¬λžŒμ΄ μ—†λŠ” 것을 λ°œκ²¬ν•˜κ³  1을 return ν•œλ‹€. 

	for (int i = first + 1; i < n; i++) { // 짝을 μ •ν•  μ‚¬λžŒ μ°ΎκΈ°
		if (!taken[i] && areFriends[first][i]) { //이 μΉœκ΅¬λ„ 짝이 μ—†κ³  μ„œλ‘œ 친ꡬ라면
			taken[i] = taken[first] = true; // 짝을 ꡬ함
			ret += findPair(n, taken); // 남은 짝 κ³ λ₯΄λŠ” μž‘μ—…μ„ μž¬κ·€λ‘œ λ– λ„˜κΈ΄ 것. λͺ¨λ‘ 짝을 κ΅¬ν–ˆλ‹€λ©΄ 1이 μ•„λ‹ˆλ©΄ 0이 return λ˜μ—ˆμ„ 것이닀.
			taken[i] = taken[first] = false; // λ‹€λ₯Έ 방법도 μžˆμ„ 수 μžˆμœΌλ‹ˆ λ‹€μ‹œ μ΄ˆκΈ°ν™” ν•΄μ€€λ‹€
		}
	}

 

μ΄λ•Œκ°€ λΌμ„œμ•Ό λ“œλ””μ–΄ firstκ°€ 0이고 iκ°€ 1인 μƒν™©μ—μ„œ ret += findPair(n, taken); 이 λλ‚œ 것이닀. 

0κ³Ό 1이 짝이 λ˜λŠ” 경우뿐 μ•„λ‹ˆλΌ 0은 2,3과도 짝이 될 수 μžˆλ‹€(0이 1,2,3κ³Ό 친ꡬ라면). 이 경우λ₯Ό λͺ¨λ‘ ν™•μΈν•˜κΈ° μœ„ν•΄μ„œ λ‘˜ λ‹€ 짝이 μ—†λ‹€κ³  μ΄ˆκΈ°ν™”ν•΄μ£Όκ³  λ‹€μ‹œ for문은 λŒμ•„κ°€κ²Œ λœλ‹€. 이런 μ‹μœΌλ‘œ μž‘μ€ subproblem을 ν•΄κ²°ν•˜λŠ” μž¬κ·€ ν•¨μˆ˜κ°€ λͺ¨λ‘ λλ‚˜κ²Œ 되고 첫 번째둜 호좜된 findPairν•¨μˆ˜κ°€ retλ₯Ό λ°˜ν™˜ν•˜λ©΄μ„œ λͺ¨λ“  학생을 친ꡬ끼리만 짝지어쀄 수 μžˆλŠ” λ°©λ²•μ˜ κ°œμˆ˜κ°€ 좜λ ₯λœλ‹€.

λ°˜μ‘ν˜•