clssl15   2년 전

어떤 분의 코드인데 코드가 도무지 이해가 안돼요....ㅠㅠㅠ

kimwj317   2년 전

N = 6 일때를 예로 들어 보겠습니다.

만약 A팀 = [1,3,4], B팀=[2,5,6] 이라면 두 팀의 능력치 차이는 밑에 그림에서 파란색 - 빨간색 이 될것입니다.

preview

식을 약간 변형하면, 파란색 - 빨간색 = (빨 + 흰 + 파) - (빨 + 흰 + 빨) 이 됩니다.

여기서 (빨 + 흰 + 파) 는 전체 행렬 원소의 합이고, (빨 + 흰 + 빨) 은 밑에 그림의 초록색 선처럼 A팀에 속한 1, 3, 4번째의 행과 열의 원소를 더한 것입니다. 

(그러면 빨간색 부분은 두 번 더해집니다.)

preview

즉 올려주신 코드는 가능한 모든 A팀의 조합에 대해  전체 행렬 합 - 초록색 행/열의 합 의 최소값을 구하는 코드입니다.


코드 구현에 대해 보면,

newstat = [sum(i) + sum(j) for i, j in zip(stat, zip(*stat))]

여기서 newstat 리스트는 k번째 행의 원소의 합 + k번째 열의 원소의 합 을 원소로 가지는 리스트입니다. 즉, N개의 원소를 가지고있습니다.

이 코드를 이해하려면 zip(stat, zip(*stat)) 이 부분을 이해해야 합니다. zip과 *의 쓰임을 찾아보세요.

간단히 설명드리면 zip은 iterable한 것들을 받아서 순서대로 쌍을 맺어서 tuple로 뱉어주는 놈입니다.

예를 들어, zip([1,2,3], [4,5,6]) 이라고 하면 (1, 4), (2, 5), (3, 6) 이런식으로 뱉어줍니다. (옷에 달린 지퍼를 생각해 보세요)

* (asterisk) 이놈은 iterable한 것을 unpack 해줍니다. 예를 들면, *[[1,2,3],[4,5,6]] 이거는 [1,2,3] 이랑 [4,5,6] 으로 unpack 합니다.

다시 코드로 돌아와서

zip(*stat)에서 *statstat 행렬의 각각의 행으로 unpack이 되고, zip(*stat) 은 각 행들에서 첫번째 원소를 뽑아서 tuple 로 만들고, 두번째 원소를 뽑아서 tuple로 만들고... 이런식으로 차례대로 쌍을 맺어줍니다. 즉 이것은 stat행렬의 각각의 열이 됩니다.

그러면 zip(stat, zip(*stat))stat의 원소(= 행) 과 zip(*stat)(=열) 을 차례대로 뱉어내므로 k번째 행과 열을 뱉어줍니다.

밑의 예시 코드를 보시면 이해가 더 잘 될것입니다.

즉, newstat[1번째 행과 열의 합, 2번째 행과 열의 합, ... N번째 행과 열의 합] 으로 된 list 입니다.

allstatnewtstat의 원소를 다 더한 다음 2로 나눴으므로 전체 행렬의 원소의 합이 됩니다. (2로 나눈 것은 행렬의 각 원소가 2번씩 더해지기 때문입니다)

따라서 전체 행렬의 합allstat이 되고, 초록색 행/열의 합newstat의 원소에서 N//2 개를 뽑아서 더해주면 됩니다. N//2개를 뽑을때 itertools.combinations를 사용한 것이구요.

(코드에서는 3번째 줄에 이미 2로 나눠서 cb(newstat[:-1], N//2) 가 아닌 cb(newstat[:-1], N) 로 되어있습니다)

즉, abs(allstat - sum(l)) 이 부분이  전체 행렬 합 - 초록색 행/열의 합 이 됩니다.

그리고 cb(newstat, N)이 아니라 cb(newstat[:-1], N) 으로 되어있는 이유는 겹치는 경우를 빼 주려고 N개를 뽑을 때 마지막 선수는 제외하고 뽑은 것 입니다.

(마지막 선수는 항상 B팀에 속한다 생각해도 답은 바뀌지 않으니까요)


이해가 되셨을까요?

clssl15   2년 전

우와 감사합니다! 시험기간이라 한동안 백준 안하다가 이제야 봤네요 자세하고 친절한 설명 정말 감사합니다! 덕분에 이해되었네요! !

댓글을 작성하려면 로그인해야 합니다.