Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6-dhlee777 #29

Merged
merged 3 commits into from
Apr 9, 2024
Merged

6-dhlee777 #29

merged 3 commits into from
Apr 9, 2024

Conversation

dhlee777
Copy link
Contributor

@dhlee777 dhlee777 commented Apr 4, 2024

🔗 문제 링크

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

✔️ 소요된 시간

하루

✨ 수도 코드

하루 동안 계속 생각해봤지만 도저히 아이디어가 떠오르지 않아서 답을 봐버렸다..
일단 이 문제는 총 n명의 선수(짝수)를 두팀(start와 link)로 나누는데 각 선수끼리 같은팀이 됐을때 능력치가 있다. 예를들어 stats[2][3] 은2번선수와 3번선수가 같은팀이됐을때 능력치를 나타낸다. 변수 start는 start팀의 능력치 합 link는 link 팀의
능력치 합을 나타낸다. 결국 구하고자하는것은 start와 link의 차가 최소가 되는 값 inf이다.

풀이과정(dfs)

for ( int i = num; i < total_num; i++) {  //전에 확인했던 선수는 확인하지 않기위해 현재선수부터 시작 
		visited[i] = 1;            //현재선수에  방문표시,방문했다면 start팀이 된다.방문안했다면 link팀     
		dfs(cnt + 1, i + 1);
		visited[i] = 0;      //탐색이 끝난후 다시 방문되지않았다고 초기화해준다.
	}

1.현재선수num부터 제일 마지막선수까지 탐색을해준다.
2.현재선수에 방문 표시를 해주고 카운트를 1증가시키고 다음선수로dfs 진행
3.다른경우의 수를 위해 탐색이 끝난후 visited[i]=0으로 초기화

만약 탐색한 선수의 수 전체선수의 절반이 된다면 밑의 코드를 실행한다.(반만 탐색해서 start팀으로 할당해주면 나머지는 자동으로link팀처리)

void dfs(int cnt, int num) {   //cnt는 방문한 선수의 수,num은 현재선수
	if (cnt == (total_num / 2)) {      //방문한선수의 수가 전체선수의 절반이라면
		start = 0;
		link = 0;
		for (int i = 1; i <= total_num; i++) {
			for (int j = 1; j <= total_num; j++) {
				if (visited[i] && visited[j] )    //만약 i,j를 둘다 방문했다면 start팀에 둘의 시너지를더해준다
					start += stats[i][j];
			 else if (!visited[i] &&!visited[j]) {//i,j둘다 방문하지않았다면 link팀에 둘의 시너지를 더해준다.
					link += stats[i][j];
				}

			}
		}
		if (inf > abs(start - link))     //두팀의 능력치차가 그전팀의 능력치 차보다 작다면
			inf = abs(start - link);     //inf가 최소가 되도록 갱신해준다.

		return;

	}
  1. 이중for문을 통해 stats 배열을 탐색해주며
  2. 만약 i,j선수를 둘다 방문했다면 start에 둘의 시너지를 더해준다.
  3. 만약 i,j 선수 둘다 방문하지않았다면 link에 둘의 시너지를 더해준다.
  4. 두팀의 총 능력치차를 구해준다음 만약 기존의 차보다 더 적다면 inf가 최소가되도록 갱신해준다.

전체코드

#include<iostream>
using namespace std;
int stats[21][21];    //i,j가 같은팀일때 능력치
int visited[21];         //선수를 방문했다면 1 방문하지않았다면 0을 할당 
int total_num;        //전체 선수의 수
int start, link;    // start팀 능력치합,link팀 능력치합
int inf = 987654321;   //두 팀의 능력치 차를 나타내는 변수
void dfs(int cnt, int num) {   //cnt는 방문한 선수의 수,num은 현재선수
	if (cnt == (total_num / 2)) {      //방문한선수의 수가 전체선수의 절반이라면
		start = 0;
		link = 0;
		for (int i = 1; i <= total_num; i++) {
			for (int j = 1; j <= total_num; j++) {
				if (visited[i] && visited[j] )    //만약 i,j를 둘다 방문했다면 start팀에 둘의 시너지를더해준다
					start += stats[i][j];
			 else if (!visited[i] &&!visited[j]) {//i,j둘다 방문하지않았다면 link팀에 둘의 시너지를 더해준다.
					link += stats[i][j];
				}

			}
		}
		if (inf > abs(start - link))     //두팀의 능력치차가 그전팀의 능력치 차보다 작다면
			inf = abs(start - link);     //inf가 최소가 되도록 갱신해준다.

		return;

	}
	for ( int i = num; i < total_num; i++) {  //전에 확인했던 선수는 확인하지 않기위해 현재선수부터 시작 
		visited[i] = 1;            //현재선수에  방문표시,방문했다면 start팀이 된다.방문안했다면 link팀     
		dfs(cnt + 1, i + 1);
		visited[i] = 0;      //탐색이 끝난후 다시 방문되지않았다고 초기화해준다.
	}
}
int main(void) {
	cin >> total_num;
	for (int i = 1; i <= total_num; i++) {
		for (int j = 1; j <= total_num; j++) {
			cin >> stats[i][j];
		}
	}
	dfs(0, 1);     //카운트0,선수1부터 시작
	cout << inf;

	return 0;
}

📚 새롭게 알게된 내용

https://hagisilecoding.tistory.com/86
백트래킹은 직관적이지가 않아서 머릿속으로 재귀를 계속 돌려보고 아무리 생각해봐도 너무 이해하기 어렵다... 백트래킹 문제를계속풀면서 감을 익혀야겠다

}
}
if (inf > abs(start - link)) //������ �ɷ�ġ���� �������� �ɷ�ġ ������ �۴ٸ�
inf = abs(start - link); //inf�� �ּҰ� �ǵ��� �������ش�.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

소소한 팁인데 비교해서 갱신하는 코드는 아래와 같이 한 줄로 정리가 가능합니다!

// 1
inf = min(abs(start - link), inf);
// 2
inf = abs(start - link) > inf ? abs(start - link) : inf;

Copy link
Collaborator

@yuyu0830 yuyu0830 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재귀랑 DP는 많이 풀어서 어떤 식으로 사용하는지 감을 잡아야하는 것 같아요! 특히 둘 다 완성된 코드의 형태를 먼저 떠올리는게 도움이 되는 것 같아요 ㅎㅎ
재귀 함수는 특히 중간까지는 비슷하게 동작하고, 처음과 끝 부분만 다르게 동작하는 함수니까 처음과 끝이 어떤 식으로 생겼는지 먼저 생각해보고 나머지 중간 로직을 짜는 식으로 풀면 조금 도움이 될거예요 :)

Copy link
Collaborator

@InSange InSange left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

처음에는 iji를 어떻게 구별해서 넣어줘야하지? 고민했는데 그냥 visited에 true만 해놓으면 2중 for문으로 i일때 j일때 기준으로 ij, ji 모두 처리해주는게 인상적이었습니다.

거기에 startlink 모두 visited를 기준점으로 한번에 처리하는 것도 너무 좋았습니다.
점점 성장하고 있군뇨 :)

Copy link
Collaborator

@InSange InSange left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추가로 최대 20개의 요소들을 모두 비교해줄때 시간복잡도가 궁금하군요. 제출했을 당시에 얼마가 걸렸는지 알 수 있을까요?
20개의 요소 중 반만 비교를 해주기때문에 10(i)*10(j)*10(dfs)이 나올까요?

@dhlee777
Copy link
Contributor Author

dhlee777 commented Apr 8, 2024

추가로 최대 20개의 요소들을 모두 비교해줄때 시간복잡도가 궁금하군요. 제출했을 당시에 얼마가 걸렸는지 알 수 있을까요? 20개의 요소 중 반만 비교를 해주기때문에 10(i)*10(j)*10(dfs)이 나올까요?

일단 for문으로 stats을 입력받을때 20(i)*20(j)의 시간복잡도가 발생하고 반만탐색하면 나머지는 자동으로 결정되므로10(i)*10(j)*10(dfs)가 될거라고 생각합니다.

Copy link
Collaborator

@seongwon030 seongwon030 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

백트래킹은 언제 봐도 어렵네요. 절반을 돈 선수는 방문처리해주고 아니면 계속 확인하면서 재귀적으로 처리하는군요. 풀이 잘 보고 갑니다

@dhlee777 dhlee777 merged commit 6397334 into main Apr 9, 2024
@dhlee777 dhlee777 deleted the 6-dhlee777 branch April 9, 2024 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants