It's just like pok    version4


어느 회사의 프로그래밍 입사문제

 | 

친구녀석이 자기가 본 모 회사의 입사 프로그래밍 문제를 말해줬다.

어떤 배열이 있는데 그 배열의 원소는 음수, 양수의 값을 가지는 정수이다. 이 배열중 원소의 합이 가장큰 구간을 O(n)의 복잡도로 구하는 알고리듬을 프로그래밍 하라.

원래 이런 종류의 두뇌개발용 프로그래밍은 별로 좋아하지는 않지만, 숙제와 시험에 생성된(…) 뻘짓거리 찾기 탐지망에 딱걸려서 이리저리 생각을 해보았다.

생각해낸 방법은 딱 한번 순회는 아니지만, O(n) = O(2n) 이라고 우기고 들어가서, 다음과 같다.

  1. 배열을 순회하면서 구간을 음수구간의 덩어리와 양수구간의 덩어리로 나눈다.
  2. 구간 덩어리중 제일 큰 양수구간 덩어리를 선택한다.
  3. 제일큰 양수구간 덩어리를 중심으로 작은 구간으로 순회하며 값을 더한다.
    • 만일 더한값이 제일큰 양수구간의 합보다 크면 제일큰 양수구간을 변경한다.
  4. 제일큰 양수구간 덩어리를 중심으로 큰 구간으로 순회하며 값을 더한다.
    • 만일 더한값이 제일큰 양수구간의 합보다 크면 제일큰 양수구간을 변경한다.

아래는 잘 돌아가는가 확인하기 위해 루비로 짠 코드.


class RangeChunk
  attr_accessor :r_start, :r_end, :r_sum
  def initialize( nStart )
    @r_start = nStart
    @r_end = 0
    @r_sum = 0
  end
end

class StrangeRange
  def initialize( _rangeSize = 0 )
    @totalRange = Array.new( _rangeSize )
    nCount = 0
    @totalRange.each do | element |
      # -500 ~ 500
      element = 500 - rand(1000)
      @totalRange[nCount] = element
      print nCount.to_s +  ". " + element.to_s +  "\n"
      nCount += 1
    end
  end
  
  def selectBigRange
    nCurrnetIndex = 0
    nCurrnetBigRangeChunk = 0
    nCurrentBigSumOfChunk = 0
    bPlusRange = true
    
    aChunkArray = Array.new
    aRangeChunk = RangeChunk.new(0)
    
    @totalRange.each do | element |
      if bPlusRange
        if element >= 0
          aRangeChunk.r_end = nCurrnetIndex
          aRangeChunk.r_sum += element
        else
          bPlusRange = false
          # 처음을 양수구간이라고 가정했는데, 알고 보니 음수구간이었다면 그냥 음수구간으로 변경시켜주기
          if aRangeChunk.r_end == nCurrnetIndex
            aRangeChunk.r_sum = element
            nCurrentBigSumOfChunk = element
          else
            aRangeChunk.r_end = nCurrnetIndex - 1
            aChunkArray.push( aRangeChunk )
            if aRangeChunk.r_sum > nCurrentBigSumOfChunk
              nCurrentBigSumOfChunk = aRangeChunk.r_sum
              nCurrnetBigRangeChunk = aChunkArray.length - 1
            end
            aRangeChunk = RangeChunk.new( nCurrnetIndex )
            aRangeChunk.r_sum += element
          end
        end
      else
        if element >= 0
          bPlusRange = true
          aRangeChunk.r_end = nCurrnetIndex - 1
          aChunkArray.push( aRangeChunk )
          aRangeChunk = RangeChunk.new( nCurrnetIndex )
          aRangeChunk.r_sum += element
        else
          aRangeChunk.r_end = nCurrnetIndex
          aRangeChunk.r_sum += element
        end
      end
      nCurrnetIndex += 1
    end
    #마지막 구간
    aRangeChunk.r_end = nCurrnetIndex - 1
    aChunkArray.push( aRangeChunk )
    
    # 음수/양수구간으로 나눈게 맞는지 확인하기.
    aChunkArray.each do | chunk_element |
      print chunk_element.r_sum.to_s + "\n"
    end
    
    aBeforChunk = nil
    aAfterChunk = nil
    
    nNewBigSumOfChunk = nCurrentBigSumOfChunk
    (nCurrnetBigRangeChunk-1).step(0, -1) do | i |
      nNewBigSumOfChunk += aChunkArray[i].r_sum
      if nNewBigSumOfChunk > nCurrentBigSumOfChunk
        nCurrentBigSumOfChunk = nNewBigSumOfChunk
        aBeforChunk = aChunkArray[i] 
      end
    end
    nNewBigSumOfChunk = nCurrentBigSumOfChunk
    (nCurrnetBigRangeChunk+1).step(aChunkArray.length-1, 1) do | i |
      nNewBigSumOfChunk += aChunkArray[i].r_sum
        if nNewBigSumOfChunk > nCurrentBigSumOfChunk
          nCurrentBigSumOfChunk = nNewBigSumOfChunk
          aAfterChunk = aChunkArray[i] 
        end
    end
    nResultStartRange = aBeforChunk == nil ? aChunkArray[nCurrnetBigRangeChunk].r_start : aBeforChunk.r_start
    nResultEndtRange = aAfterChunk == nil ? aChunkArray[nCurrnetBigRangeChunk].r_end : aAfterChunk.r_end
    print "Big Range Start : " + nResultStartRange.to_s + ", End : " + nResultEndtRange.to_s + ". and Sum : " + nCurrentBigSumOfChunk.to_s + "\n"
  end
  
end

if __FILE__ == $0
  nRangeCount = ARGV.empty?  ? 10 : ARGV[0].to_i
  aSample = StrangeRange.new( nRangeCount )
  aSample.selectBigRange
end

나중에 그 친구한테 들은이야기인데, 알고보니 ACM-ICPC 에서의 고전적인 문제라고 한다. ACM-ICPC 같은경우는 전혀 관심이 없었는데, 얼추 재미있는걸 하는구나… 라는 생각이 들었다.

ps. 언제 봐도.. 내 루비코드는 C++ 코드 같다. -_-

cf 댓글들

이응준 says

길다. 왜 이렇게 길어진거지?

and

줄~줄~ 길어졌지.. 생각해보니 이것저것 해줘야할게 많더라. 알고리즘만 검증하다보니 코드품질도 별로 안좋고…

루비나 파이썬이나, 이런종류의 알고리즘 검증용으로 쓰는것도 좋을듯 싶다. 요새 그래서 그래프를 한번 루비로 짜보고 있는중.

낭만고양이 says

안녕하세요? 우연히 들렀습니다.

이 문제는 Programming Pearls라는 책에도 나오는 문제입니다.

저도 같은 문제를 Ruby로 풀어본 것이 있습니다만, 한번 참고해보시기 바랍니다. 링크는 다음과 같습니다. :-)

http://www.buggymind.com/97

Programming Pearls라는 책은 시중에 “생각하는 프로그래밍”이라는 제목으로 번역이 되어 있습니다.

주제넘었습니다만, 도움이 되었으면 좋겠습니다.

건승하세요~

and

남겨주신 링크와 글 잘읽었습니다. 감사합니다.

“알고리즘을 만드는 데 있어서 중요한 것은 문제를 끊임없이 개선해 나가려는 노력”이라는 말이 제게도 크게 와닿네요.


프로그래밍