N = 6 일때를 예로 들어 보겠습니다.
만약 A팀 = [1,3,4], B팀=[2,5,6] 이라면 두 팀의 능력치 차이는 밑에 그림에서 파란색 - 빨간색
이 될것입니다.
식을 약간 변형하면, 파란색 - 빨간색 = (빨 + 흰 + 파) - (빨 + 흰 + 빨)
이 됩니다.
여기서 (빨 + 흰 + 파)
는 전체 행렬 원소의 합이고, (빨 + 흰 + 빨)
은 밑에 그림의 초록색 선처럼 A팀에 속한 1, 3, 4번째의 행과 열의 원소를 더한 것입니다.
(그러면 빨간색 부분은 두 번 더해집니다.)
즉 올려주신 코드는 가능한 모든 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)
에서 *stat
은 stat
행렬의 각각의 행으로 unpack이 되고, zip(*stat)
은 각 행들에서 첫번째 원소를 뽑아서 tuple 로 만들고, 두번째 원소를 뽑아서 tuple로 만들고... 이런식으로 차례대로 쌍을 맺어줍니다. 즉 이것은 stat
행렬의 각각의 열이 됩니다.
그러면 zip(stat, zip(*stat))
은 stat
의 원소(= 행) 과 zip(*stat)
(=열) 을 차례대로 뱉어내므로 k번째 행과 열을 뱉어줍니다.
밑의 예시 코드를 보시면 이해가 더 잘 될것입니다.
즉, newstat
은 [1번째 행과 열의 합, 2번째 행과 열의 합, ... N번째 행과 열의 합]
으로 된 list 입니다.
allstat
은 newtstat
의 원소를 다 더한 다음 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년 전 1
어떤 분의 코드인데 코드가 도무지 이해가 안돼요....ㅠㅠㅠ