<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>오늘도 개발로그</title>
    <link>https://www.ohnimdev.com/</link>
    <description>최신 IT 기술, AI, 개발하면서 배운 것들, 알고리즘 문제 풀이를 기록하는 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Tue, 5 May 2026 19:00:40 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Ohnim &amp;middot; 오님</managingEditor>
    <image>
      <title>오늘도 개발로그</title>
      <url>https://tistory1.daumcdn.net/tistory/2460668/attach/3ab906fb3202419a87f14dffb18dc640</url>
      <link>https://www.ohnimdev.com</link>
    </image>
    <item>
      <title>미드저니에서 창의적인 이미지를 생성하는 방법: --weird(--w) 옵션</title>
      <link>https://www.ohnimdev.com/entry/%EB%AF%B8%EB%93%9C%EC%A0%80%EB%8B%88%EC%97%90%EC%84%9C-%EC%B0%BD%EC%9D%98%EC%A0%81%EC%9D%B8-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EC%83%9D%EC%84%B1%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-weird-%EC%98%B5%EC%85%98</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니(Midjourney)는 AI 기반의 이미지 생성 도구로, 예상치 못한 창의적이고 독특한 이미지를 생성할 수 있는 --weird(--w) 옵션을 제공한다. 이 옵션은 0에서 3000까지 값을 설정할 수 있으며, 값이 높을수록 더욱 창의적이고 비현실적인 이미지를 생성한다. 기본값은 0으로, 이때는 프롬프트에 가깝고 현실적인 이미지를 만든다. 이 글에서는 --weird 값에 따라 이미지가 어떻게 변하는지 살펴보고 활용 방법에 대해 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;--weird 옵션을 사용할 수 있는 모델&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 --weird 옵션을 사용할 수 있는 모델은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;미드저니 5 이상 버전&lt;/li&gt;
&lt;li&gt;Niji 5, Niji 6&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, --weird 옵션과 --seed 옵션이 서로 완벽하게 호환되지 않아, 두 가지 옵션을 같이 사용하면 예상치 못한 결과가 나올 수 있다. 그래서 두 가지 옵션을 같이 사용하는 것을 권장하고 있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;--weird 옵션 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--weird 옵션을 사용할 때는 값을 너무 높게 설정하면 이미지가 지나치게 왜곡되어 원하는 스타일에서 크게 벗어날 위험이 있다. 처음에는 250에서 500 사이의 값으로 시작하는 것이 좋다. 이후 원하는 스타일에 맞춰 값을 조금씩 위아래로 조정하면서 최적의 --weird 값을 찾아나간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시 프롬프트 1) 네온빛의 고양이&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Neon cat --weird [옵션값]&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 810px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;결과물&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;옵션값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;비고&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 258px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 258px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;neon_cat_0_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KUA5R/btsJ6TbvYdA/HsvBeuGLsKHji1BYVxd7v0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KUA5R/btsJ6TbvYdA/HsvBeuGLsKHji1BYVxd7v0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KUA5R/btsJ6TbvYdA/HsvBeuGLsKHji1BYVxd7v0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKUA5R%2FbtsJ6TbvYdA%2FHsvBeuGLsKHji1BYVxd7v0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Neon cat --weird 0&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;neon_cat_0_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 258px;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 258px;&quot;&gt;기본값인 0에서는 네온빛으로 표현된 고양이 이미지가 만들어진다. 프롬프트를 충실히 잘 따르고 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 258px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 258px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;neon_cat_250_6_11zon.webp&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;1682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TlVy9/btsJ7nDcw1B/PTpCpV3UK5yDLwAFQ0Z67K/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TlVy9/btsJ7nDcw1B/PTpCpV3UK5yDLwAFQ0Z67K/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TlVy9/btsJ7nDcw1B/PTpCpV3UK5yDLwAFQ0Z67K/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTlVy9%2FbtsJ7nDcw1B%2FPTpCpV3UK5yDLwAFQ0Z67K%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Neon cat --weird 250&quot; loading=&quot;lazy&quot; width=&quot;1682&quot; height=&quot;1682&quot; data-filename=&quot;neon_cat_250_6_11zon.webp&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;1682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 258px;&quot;&gt;250&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 258px;&quot;&gt;이 값부터는 고양이의 형태가 왜곡되기 시작한다. 네온빛은 강렬해지고, 고양이의 윤곽이 흐릿해지며 몽환적인 느낌을 준다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 258px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 258px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;neon_cat_500_7_11zon.webp&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;1682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JbDms/btsJ7RRsAhg/8vkhXn5O0Tu9yQvKk7V3r0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JbDms/btsJ7RRsAhg/8vkhXn5O0Tu9yQvKk7V3r0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JbDms/btsJ7RRsAhg/8vkhXn5O0Tu9yQvKk7V3r0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJbDms%2FbtsJ7RRsAhg%2F8vkhXn5O0Tu9yQvKk7V3r0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Neon cat --weird 500&quot; loading=&quot;lazy&quot; width=&quot;1682&quot; height=&quot;1682&quot; data-filename=&quot;neon_cat_500_7_11zon.webp&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;1682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 258px;&quot;&gt;500&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 258px;&quot;&gt;고양이의 형태가 현실과 달라지며 추상적인 이미지가 생성된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;neon_cat_1000_8_11zon.webp&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;1682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l9Paz/btsJ6e8iE9R/dFY2jV90mJuKy0WqKBZPE0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l9Paz/btsJ6e8iE9R/dFY2jV90mJuKy0WqKBZPE0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l9Paz/btsJ6e8iE9R/dFY2jV90mJuKy0WqKBZPE0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl9Paz%2FbtsJ6e8iE9R%2FdFY2jV90mJuKy0WqKBZPE0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Neon cat --weird 1000&quot; loading=&quot;lazy&quot; width=&quot;1682&quot; height=&quot;1682&quot; data-filename=&quot;neon_cat_1000_8_11zon.webp&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;1682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 17px;&quot;&gt;1000&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 17px;&quot;&gt;프롬프트와 결과물과의 차이가 더욱 커지며 다양한 분의기의 이미지를 생성할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시 프롬프트 2) 기계 나비와 증기 기관차&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;A mechanical butterfly and a steam locomotive --weird [옵션값]&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 810px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;결과물&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;옵션값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;비고&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 258px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 258px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;train_0_9_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bY82jh/btsJ6r0A9B5/8bOmt2Es5ebdk4zpUEUjH0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bY82jh/btsJ6r0A9B5/8bOmt2Es5ebdk4zpUEUjH0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bY82jh/btsJ6r0A9B5/8bOmt2Es5ebdk4zpUEUjH0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbY82jh%2FbtsJ6r0A9B5%2F8bOmt2Es5ebdk4zpUEUjH0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;A mechanical butterfly and a steam locomotive --weird 0&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;train_0_9_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 258px;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 258px;&quot;&gt;기본값인 0에서는 기계 나비와 증기 기관차 두 요소 모두 섬세하게 표현했다. 역시 프롬프트를 잘 따르고 있는것을 알 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 258px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 258px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;train_250_10_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daLmnj/btsJ5zLJHql/K9Alu1dczlnbvoV0LDfGYk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daLmnj/btsJ5zLJHql/K9Alu1dczlnbvoV0LDfGYk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daLmnj/btsJ5zLJHql/K9Alu1dczlnbvoV0LDfGYk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaLmnj%2FbtsJ5zLJHql%2FK9Alu1dczlnbvoV0LDfGYk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;A mechanical butterfly and a steam locomotive --weird 250&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;train_250_10_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 258px;&quot;&gt;250&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 258px;&quot;&gt;기계 나비와 증기 기관차의 모습이 조금씩 섞여 독특한 비주얼을 만들어낸다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 258px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 258px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;train_500_11_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wDWLZ/btsJ7z4yo8R/TTLksXKGJjK9iyyTn01Qf0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wDWLZ/btsJ7z4yo8R/TTLksXKGJjK9iyyTn01Qf0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wDWLZ/btsJ7z4yo8R/TTLksXKGJjK9iyyTn01Qf0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwDWLZ%2FbtsJ7z4yo8R%2FTTLksXKGJjK9iyyTn01Qf0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;A mechanical butterfly and a steam locomotive --weird 500&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;train_500_11_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 258px;&quot;&gt;500&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 258px;&quot;&gt;나비와 기차의 경계가 모호해지고, 메카닉 특유의 묵직한 감성이 강조된 추상적인 이미지를 생성한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;train_1000_12_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EifWy/btsJ6GQXjps/tpuiQmBHRlmZEd6ipG8KAk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EifWy/btsJ6GQXjps/tpuiQmBHRlmZEd6ipG8KAk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EifWy/btsJ6GQXjps/tpuiQmBHRlmZEd6ipG8KAk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEifWy%2FbtsJ6GQXjps%2FtpuiQmBHRlmZEd6ipG8KAk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;A mechanical butterfly and a steam locomotive --weird 1000&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;train_1000_12_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 17px;&quot;&gt;1000&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 17px;&quot;&gt;기계 나비와 증기 기관차의 특징이 많이 희석된 이미지를 생성한다. 하지만 창의적이고 독창적인 이미지를 얻을 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시 프롬프트 3) 물속의 빛나는 돌고래&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Glowing dolphin underwater --weird [옵션값]&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 810px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;결과물&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;옵션값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;비고&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 258px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 258px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;dolphin_0_1_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qbujO/btsJ6pIu022/sv6vXwk7TO6KiWRIxXz3N0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qbujO/btsJ6pIu022/sv6vXwk7TO6KiWRIxXz3N0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qbujO/btsJ6pIu022/sv6vXwk7TO6KiWRIxXz3N0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqbujO%2FbtsJ6pIu022%2Fsv6vXwk7TO6KiWRIxXz3N0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Glowing dolphin underwater --weird 0&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;dolphin_0_1_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 258px;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 258px;&quot;&gt;돌고래가 물속에서 햇빛을 받아 빛나는 이미지가 만들어진다. 물과 돌고래의 표현은 현실적이며 섬세하다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 258px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 258px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;dolphin_250_2_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RtEOZ/btsJ7Um45z2/BRGCcGueTkjAe3Cgeq4w20/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RtEOZ/btsJ7Um45z2/BRGCcGueTkjAe3Cgeq4w20/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RtEOZ/btsJ7Um45z2/BRGCcGueTkjAe3Cgeq4w20/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRtEOZ%2FbtsJ7Um45z2%2FBRGCcGueTkjAe3Cgeq4w20%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Glowing dolphin underwater --weird 250&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;dolphin_250_2_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 258px;&quot;&gt;250&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 258px;&quot;&gt;물속에 비춰지는 빛의 일그러짐이 강해지고, 색의 표현도 다양해진다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 258px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 258px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;dolphin_500_3_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bphToT/btsJ5nYZnTV/K0EowjpnTZHt9Kcivin2k1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bphToT/btsJ5nYZnTV/K0EowjpnTZHt9Kcivin2k1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bphToT/btsJ5nYZnTV/K0EowjpnTZHt9Kcivin2k1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbphToT%2FbtsJ5nYZnTV%2FK0EowjpnTZHt9Kcivin2k1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Glowing dolphin underwater --weird 500&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;dolphin_500_3_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 258px;&quot;&gt;500&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 258px;&quot;&gt;돌고래가 직접 빛나는, 현실에서는 보기 힘든 이미지가 생성되기 시작한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 31.7056%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;dolphin_1000_4_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGAlji/btsJ5xmQOGx/waTJfwCSGFtrso8ZF0CNtK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGAlji/btsJ5xmQOGx/waTJfwCSGFtrso8ZF0CNtK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGAlji/btsJ5xmQOGx/waTJfwCSGFtrso8ZF0CNtK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGAlji%2FbtsJ5xmQOGx%2FwaTJfwCSGFtrso8ZF0CNtK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Glowing dolphin underwater --weird 1000&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;dolphin_1000_4_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.0307%; text-align: center; height: 17px;&quot;&gt;1000&lt;/td&gt;
&lt;td style=&quot;width: 54.2637%; height: 17px;&quot;&gt;돌고래가 직접 빛나는 것에서 멈추지 않고, 현실에서 보기 어려운 빛의 색을 표현한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;맺으며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니의 --weird 옵션은 독창적이고 기이한 이미지를 만들 때 유용하게 사용할 수 있는 옵션이다. 특히, --stylize 옵션과 함께 사용하면 더욱 다채롭고 예술적인 이미지를 만들 수 있다. 다만, --weird 옵션은 실험적인 기능이기 때문에 같은 프롬프트와 옵션값을 사용하더라도 매번 다른 결과가 나온다. 따라서 원하는 이미지를 얻기 위해서 여러 차례 실험을 반복하며 최적의 설정을 찾아가는 과정이 필요하다.&lt;/p&gt;</description>
      <category>AI/미드저니</category>
      <category>Midjourney</category>
      <category>w 옵션</category>
      <category>weird 옵션</category>
      <category>미드저니</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/81</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%AF%B8%EB%93%9C%EC%A0%80%EB%8B%88%EC%97%90%EC%84%9C-%EC%B0%BD%EC%9D%98%EC%A0%81%EC%9D%B8-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EC%83%9D%EC%84%B1%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-weird-%EC%98%B5%EC%85%98#entry81comment</comments>
      <pubDate>Wed, 16 Oct 2024 02:54:39 +0900</pubDate>
    </item>
    <item>
      <title>미드저니에서 --chaos(--c) 옵션 사용법: 이미지 다양성 조정 팁</title>
      <link>https://www.ohnimdev.com/entry/%EB%AF%B8%EB%93%9C%EC%A0%80%EB%8B%88%EC%97%90%EC%84%9C-chaos-%EC%98%B5%EC%85%98-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%8B%A4%EC%96%91%EC%84%B1-%EC%A1%B0%EC%A0%95-%ED%8C%81</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;--chaos 옵션이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니(Midjourney)에서는 이미지를 생성할 때 결과물의 다양성과 불규칙성을 조절할 수 있는 --chaos 옵션을 제공한다. --chaos 옵션은 0에서 100까지 설정할 수 있으며, 값이 높을수록 창의적이고 예상하지 못한 이미지를 생성한다. 반면, 값이 낮을수록 이미지의 일관성이 높아져 프롬프트와 유사한 결과를 얻을&amp;nbsp;수 있다. --chaos 값의 기본값은 0으로 설정되어 있어 안정적이고 반복 가능한 이미지를 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;--chaos 옵션 값에 따른 이미지 생성의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니에서 --chaos 옵션 값에 따라 생성된 이미지가 어떻게 달라지는지 살펴보자. 이를 위해 다음과 같은 프롬프트를 사용한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;a futuristic city skyline at sunset, with flying cars and neon lights --chaos [옵션값]&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;값을 사용하지 않음(기본값)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--chaos 값을 0으로 설정하면, 생성된 이미지들은 대체로 비슷한 구성을 유지한다. 이는 안정적이고 예측 가능한 이미지를 생성하고 싶을 때 유용하다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 289px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 272px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 272px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_1_1_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yedjG/btsJ10A9mgQ/PoX6W8Nun53fQHpdxFODp0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yedjG/btsJ10A9mgQ/PoX6W8Nun53fQHpdxFODp0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yedjG/btsJ10A9mgQ/PoX6W8Nun53fQHpdxFODp0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyedjG%2FbtsJ10A9mgQ%2FPoX6W8Nun53fQHpdxFODp0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 0으로 설정해서 만들어진 이미지 1&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;0_1_1_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 272px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_2_2_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ruJ5Z/btsJ1IAHqvf/KUc0ZxDLr2idhLGsujNkR0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ruJ5Z/btsJ1IAHqvf/KUc0ZxDLr2idhLGsujNkR0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ruJ5Z/btsJ1IAHqvf/KUc0ZxDLr2idhLGsujNkR0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FruJ5Z%2FbtsJ1IAHqvf%2FKUc0ZxDLr2idhLGsujNkR0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 0으로 설정해서 만들어진 이미지 2&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;0_2_2_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 272px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_3_3_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8kKS6/btsJ2xytRH0/r6o9KTfPerSXmqZ4fbK3cK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8kKS6/btsJ2xytRH0/r6o9KTfPerSXmqZ4fbK3cK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8kKS6/btsJ2xytRH0/r6o9KTfPerSXmqZ4fbK3cK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8kKS6%2FbtsJ2xytRH0%2Fr6o9KTfPerSXmqZ4fbK3cK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 0으로 설정해서 만들어진 이미지 3&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;0_3_3_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 99.9999%; text-align: center; height: 17px;&quot; colspan=&quot;3&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;a futuristic city skyline at sunset, with flying cars and neon lights --chaos 0&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;낮은 값을 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--chaos 값을 10으로 설정하면, 기본값과 비교했을 때 약간의 변형이 있지만 그래도 여전히 비슷한 구성을 유지한다. 이미지들 간의 차이가 크지 않으며 조금의 변화를 주고 싶을 때 적합하다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10_1_4_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BS7z4/btsJ12sdZFC/2LPHTZd5AokUrtzq1YNtb1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BS7z4/btsJ12sdZFC/2LPHTZd5AokUrtzq1YNtb1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BS7z4/btsJ12sdZFC/2LPHTZd5AokUrtzq1YNtb1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBS7z4%2FbtsJ12sdZFC%2F2LPHTZd5AokUrtzq1YNtb1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 10으로 설정해서 만들어진 이미지 1&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;10_1_4_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10_2_5_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uai0Y/btsJZ81P1l0/MmUgRy5eX2H9Z3PByxYNM1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uai0Y/btsJZ81P1l0/MmUgRy5eX2H9Z3PByxYNM1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uai0Y/btsJZ81P1l0/MmUgRy5eX2H9Z3PByxYNM1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuai0Y%2FbtsJZ81P1l0%2FMmUgRy5eX2H9Z3PByxYNM1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 10으로 설정해서 만들어진 이미지 2&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;10_2_5_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10_3_6_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pSd1s/btsJ0UaIPFy/soPX9QwLjouZwLaSkhuW70/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pSd1s/btsJ0UaIPFy/soPX9QwLjouZwLaSkhuW70/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pSd1s/btsJ0UaIPFy/soPX9QwLjouZwLaSkhuW70/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpSd1s%2FbtsJ0UaIPFy%2FsoPX9QwLjouZwLaSkhuW70%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 10으로 설정해서 만들어진 이미지 3&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;10_3_6_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 99.9999%; text-align: center;&quot; colspan=&quot;3&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;a futuristic city skyline at sunset&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;, with flying cars and neon lights&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;-chaos 10&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;중간 값을 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--chaos 값을 25로 설정하면, 생성된 이미지들 사이의 차이가 점점 뚜렷해진다. 이미지의 구성이나 스타일에 더 많은 변형이 가해졌지만 아직까지 프롬프트의 핵심적인 요소들은 유지된다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;25_1_7_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BEokf/btsJ0QlYG7D/uqMlcFuokpnrjhe9i1vD41/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BEokf/btsJ0QlYG7D/uqMlcFuokpnrjhe9i1vD41/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BEokf/btsJ0QlYG7D/uqMlcFuokpnrjhe9i1vD41/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBEokf%2FbtsJ0QlYG7D%2FuqMlcFuokpnrjhe9i1vD41%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 25로 설정해서 만들어진 이미지 1&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;25_1_7_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;25_2_8_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MIBb2/btsJ0encndM/pOCI30QWPwQ82AK44S8eU0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MIBb2/btsJ0encndM/pOCI30QWPwQ82AK44S8eU0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MIBb2/btsJ0encndM/pOCI30QWPwQ82AK44S8eU0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMIBb2%2FbtsJ0encndM%2FpOCI30QWPwQ82AK44S8eU0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 25로 설정해서 만들어진 이미지 2&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;25_2_8_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;25_3_9_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C8ra1/btsJ2ohjvCS/HTQVG3Xdssx91LQ3Dbu0qK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C8ra1/btsJ2ohjvCS/HTQVG3Xdssx91LQ3Dbu0qK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C8ra1/btsJ2ohjvCS/HTQVG3Xdssx91LQ3Dbu0qK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC8ra1%2FbtsJ2ohjvCS%2FHTQVG3Xdssx91LQ3Dbu0qK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 25로 설정해서 만들어진 이미지 3&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;25_3_9_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 99.9999%; text-align: center;&quot; colspan=&quot;3&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;a futuristic city skyline at sunset&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;, with flying cars and neon lights&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;-chaos 25&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;높은 값을 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--chaos 값을 50으로 설정하면, 예상치 못한 스타일과 형태가 나타나기 시작한다. 이제부터는 프롬프트와 일치하지 않는 이미지가 생성되기 시작하며, 이미지들간의 다양성이 매우 높아진다. 실험적인 이미지를 원하거나 프롬프트에서 벗어난 새로운 아이디어를 찾을 때 사용해 보면 좋을 것 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;50_1_10_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vINI0/btsJ0CVF0nu/gkaVYV4J0lNl19WKjIZCpK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vINI0/btsJ0CVF0nu/gkaVYV4J0lNl19WKjIZCpK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vINI0/btsJ0CVF0nu/gkaVYV4J0lNl19WKjIZCpK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvINI0%2FbtsJ0CVF0nu%2FgkaVYV4J0lNl19WKjIZCpK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 50으로 설정해서 만들어진 이미지 1&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;50_1_10_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;50_2_11_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpIs7D/btsJ0U9C9kG/kfhc1MTdE6k6N173yROak0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpIs7D/btsJ0U9C9kG/kfhc1MTdE6k6N173yROak0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpIs7D/btsJ0U9C9kG/kfhc1MTdE6k6N173yROak0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpIs7D%2FbtsJ0U9C9kG%2Fkfhc1MTdE6k6N173yROak0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 50으로 설정해서 만들어진 이미지 2&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;50_2_11_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;50_3_12_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFT1kS/btsJ1Igqzlg/sme4STNkCOz2WdAa9gJXDk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFT1kS/btsJ1Igqzlg/sme4STNkCOz2WdAa9gJXDk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFT1kS/btsJ1Igqzlg/sme4STNkCOz2WdAa9gJXDk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFT1kS%2FbtsJ1Igqzlg%2Fsme4STNkCOz2WdAa9gJXDk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 50으로 설정해서 만들어진 이미지 3&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;50_3_12_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 99.9999%; text-align: center;&quot; colspan=&quot;3&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;a futuristic city skyline at sunset&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;, with flying cars and neon lights&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;-chaos 50&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;매우 높은 값을 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--chaos 값을 80으로 설정하면, 결과물의 예측이 거의 불가능하고 프롬프트와 전혀 다른 해석을 가진 이미지가 생성된다. 이 값은 독창적인 스타일을 찾거나 기존의 틀을 깨는 이미지를 생성하고 할 때 적합하다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;80_1_13_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgyQPR/btsJ1e0W2mT/6KbEuEmZ7HnPJvlRGPV4a1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgyQPR/btsJ1e0W2mT/6KbEuEmZ7HnPJvlRGPV4a1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgyQPR/btsJ1e0W2mT/6KbEuEmZ7HnPJvlRGPV4a1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgyQPR%2FbtsJ1e0W2mT%2F6KbEuEmZ7HnPJvlRGPV4a1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 80으로 설정해서 만들어진 이미지 1&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;1490&quot; data-filename=&quot;80_1_13_11zon.webp&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;80_2_14_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YXT7o/btsJ1D7kHvD/ZiO4PU3yRHq19FxKw2LpZk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YXT7o/btsJ1D7kHvD/ZiO4PU3yRHq19FxKw2LpZk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YXT7o/btsJ1D7kHvD/ZiO4PU3yRHq19FxKw2LpZk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYXT7o%2FbtsJ1D7kHvD%2FZiO4PU3yRHq19FxKw2LpZk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 80으로 설정해서 만들어진 이미지 2&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;80_2_14_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;80_3_15_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/922zT/btsJ153nyhu/45wmjn6HAPmiHcDMkcJFf1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/922zT/btsJ153nyhu/45wmjn6HAPmiHcDMkcJFf1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/922zT/btsJ153nyhu/45wmjn6HAPmiHcDMkcJFf1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F922zT%2FbtsJ153nyhu%2F45wmjn6HAPmiHcDMkcJFf1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;chaos 값을 80으로 설정해서 만들어진 이미지 3&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;80_3_15_11zon.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 99.9999%; text-align: center;&quot; colspan=&quot;3&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;a futuristic city skyline at sunset&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;, with flying cars and neon lights&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;-chaos 80&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;맺으며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니의 --chaos 옵션은 이미지의 다양성과 창의성을 조절할 수 있는 강력한 도구이다. 낮은 --chaos 값으로는 일관된 이미지를, 높은 --chaos 값으로는 독창적이고 예상치 못한 이미지를 생성할 수 있다. 이를 통해 다양한 요구와 목적에 맞는 이미지를 생성할 수 있으며, 실험적이고 창의적인 아이디어를 얻는데 도움을 받을 수 있다.&lt;/p&gt;</description>
      <category>AI/미드저니</category>
      <category>--c</category>
      <category>--chaos</category>
      <category>Midjourney</category>
      <category>다양성</category>
      <category>미드저니</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/80</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%AF%B8%EB%93%9C%EC%A0%80%EB%8B%88%EC%97%90%EC%84%9C-chaos-%EC%98%B5%EC%85%98-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%8B%A4%EC%96%91%EC%84%B1-%EC%A1%B0%EC%A0%95-%ED%8C%81#entry80comment</comments>
      <pubDate>Fri, 11 Oct 2024 04:17:35 +0900</pubDate>
    </item>
    <item>
      <title>CSS의 선택자로 자식 선택하기: 첫 번째, 마지막, N번째</title>
      <link>https://www.ohnimdev.com/entry/CSS%EC%9D%98-%EC%84%A0%ED%83%9D%EC%9E%90%EB%A1%9C-%EC%9E%90%EC%8B%9D-%EC%84%A0%ED%83%9D%ED%95%98%EA%B8%B0-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EB%A7%88%EC%A7%80%EB%A7%89-N%EB%B2%88%EC%A7%B8</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 개발에서 &lt;b&gt;CSS 선택자&lt;/b&gt;는 HTML 요소에 효율적으로 스타일을 적용하는 데 필수적인 도구이다. 특히 &lt;b&gt;자식 선택자&lt;/b&gt;인 &lt;span style=&quot;background-color: #ffffee;&quot;&gt;&lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:first-child&lt;/span&gt;,&lt;/span&gt; &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:last-child&lt;/span&gt;, &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:nth-child()&lt;/span&gt;는 목록이나 구조화된 데이터에서 특정 위치의 자식 요소에 스타일을 적용하는 데 매우 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 CSS 자식 선택자는 &lt;b&gt;첫 번째 자식 요소, 마지막 자식 요소,&lt;/b&gt; 그리고 &lt;b&gt;N번째 자식 요소&lt;/b&gt;와 같이 HTML 구조 내에서 특정 요소에 선택적으로 스타일을 적용할 수 있도록 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 이러한 CSS 자식 선택자를 활용하여 첫 번째, 마지막, N번째 자식 요소에 스타일을 적용하는 방법을 자세히 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;첫 번째 자식 요소 선택하기(:first-child)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS의 &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:first-child&lt;/span&gt; 선택자는 부모 요소의 &lt;span data-token-index=&quot;1&quot;&gt;첫 번째 자식 요소&lt;/span&gt;에 스타일을 적용하는데 사용됩니다. HTML 목록의 첫 번째 항목이나 문단의 첫 줄에만 스타일을 적용하고 싶을 때 유용하다. 예를 들어, 아래와 같이 &amp;lt;ul&amp;gt; 요소 안의 &amp;lt;li&amp;gt; 요소들 중 첫 번째 &amp;lt;li&amp;gt;에만 스타일을 적용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1728405492747&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;첫 번째 아이템&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;두 번째 아이템&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;세 번째 아이템&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;NWQRzeG&quot; data-pen-title=&quot;:first-child 선택자를 이용해 첫 번째 자식 요소에 스타일을 적용&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/NWQRzeG&quot;&gt; :first-child 선택자를 이용해 첫 번째 자식 요소에 스타일을 적용&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마지막 자식 요소 선택하기(:last-child)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS에서 &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:last-child&lt;/span&gt; 선택자는 부모 요소의 마지막 자식 요소를 선택하여 스타일을 적용하는데 사용된다. 이 선택자는 HTML 문서의 목록 마지막 항목이나 문단의 마지막 물에 특별한 스타일을 적용할 때 유용하다. 예를 들어, 아래와 같이 여러 목록 항목 중에서 마지막 &amp;lt;li&amp;gt; 요소에만 다른 색상을 적용하고자 할 때 &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:last-child&lt;/span&gt;를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;zYgKaVR&quot; data-pen-title=&quot;:last-child 선택자를 이용해 마지막 자식 요소에 스타일을 적용&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/zYgKaVR&quot;&gt; :last-child 선택자를 이용해 마지막 자식 요소에 스타일을 적용&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;수식을 이용해 자식 요소 선택하기(:nth-child())&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS에서 &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:nth-child()&lt;/span&gt; 선택자는 부모 요소의 자식 중 N번째 요소를 선택할 때 사용된다. 이 선택자는 순서를 기반으로 하는 선택자로, HTML 구조에서 규칙적으로 반복되는 요소에 스타일을 적용하는데 유용하다. 특히, 수학적 표현식을 활용하면 복잡한 패턴에 맞춰 자식 요소를 선택할 수 있어 규칙적인 디자인 적용에 효과적으로 대응할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt; :nth-child()&lt;/span&gt;에서 선택하는 자식 요소는 1-based 인덱싱을 사용한다. 즉, 첫 번째 자식 요소는 1을 의미한다. 대부분의 프로그래밍 언어에서 0-based 인덱싱을 사용하는 것과 다르므로, 혼동하지 않도록 주의해야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특정 순번의 요소를 선택&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:nth-child()&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;선택자는 부모 요소의 자식 중 특정 순번에 위치한 요소에 스타일을 적용할 수 있다. 예를 들어, 다음과 같이 두 번째 자식 요소에 스타일을 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;ZEgpjzX&quot; data-pen-title=&quot;:nth-child 선택자를 이용해 특정 순번에 위치한 요소에 스타일을 적용&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/ZEgpjzX&quot;&gt; :nth-child 선택자를 이용해 특정 순번에 위치한 요소에 스타일을 적용&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 CSS는 HTML 목록의 두 번째 &amp;lt;li&amp;gt; 요소에만 배경색을 밝은 파란색으로 변경한다. 만약 &lt;span style=&quot;background-color: #f5f5f5; color: #ef5369; text-align: start;&quot;&gt;:nth-child()&lt;/span&gt; 안에 있는 숫자를 7로 변경하면, 일곱 번째 자식 요소에만 스타일이 적용된다. 이러한 방법은 HTML 목록이나 테이블에 특정 순서에 있는 요소를 시각적으로 강조할 때 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;홀수/짝수 요소 선택&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f5f5f5; color: #ef5369; text-align: start;&quot;&gt;:nth-child()&lt;/span&gt;&amp;nbsp;선택자는 홀수와 짝수 번째 자식 요소를 쉽게 선택할 수 있다. 이를 위해 예약된 odd와 even 키워드를 사용한다. 이 키워드들은 수학적 표현식으로 처리되는데, odd는 2n + 1로 1, 3, 5, 7&amp;hellip; 처럼 홀수 번째 요소를, even은 2n으로 2, 4, 6, 8&amp;hellip; 과 같은 짝수 번째 요소를 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;WNVGKvo&quot; data-pen-title=&quot;:nth-child 선택자를 이용해 특정 순번에 위치한 요소에 스타일을 적용&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/WNVGKvo&quot;&gt; :nth-child 선택자를 이용해 특정 순번에 위치한 요소에 스타일을 적용&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 CSS는 HTML 목록의 홀수 번째 &amp;lt;li&amp;gt; 요소에는 파란색 배경이 적용되고, 짝수 번째 &amp;lt;li&amp;gt; 요소에는 초록색 배경이 적용되어 교차하는 배경색 효과를 구현할 수 있다. 이 방식은 긴 목록이나 HTML 테이블에서 행을 시각적으로 구분해 가독성을 높이는데 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;X번째 요소부터 마지막 요소까지 선택하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f5f5f5; color: #ef5369; text-align: start;&quot;&gt;:nth-child()&lt;/span&gt;&amp;nbsp;선택자는 특정한 X번째 요소부터 마지막 자식 요소까지 스타일을 적용하는 방법도 제공한다. 아래의 예시에는 HTML 목록에서 세 번째 &amp;lt;li&amp;gt; 요소부터 마지막 &amp;lt;li&amp;gt; 요소까지 이탤릭체가 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;bGXwjVe&quot; data-pen-title=&quot;:nth-child 선택자를 이용해 X번째에 위치한 요소부터 스타일을 적용&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/bGXwjVe&quot;&gt; :nth-child 선택자를 이용해 X번째에 위치한 요소부터 스타일을 적용&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 n+3는 n이 0부터 시작하여 1씩 커지기 때문에, 세 번째 자식 요소부터 스타일을 적용하게 된다. 즉, n=0일 때는 세 번째 요소에 스타일이 적용되고, n=1일 때는 세 번째, n=2일때는 다섯 번째 요소에 스타일이 적용되는 방식이다. 만약 일곱 번째 요소부터 이탤릭체를 적용하고 싶다면 &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:nth-child(n+7)&lt;/span&gt;로 뒤의 숫자만 7로 변경해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 번째 요소부터 X번째 요소까지 선택하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f5f5f5; color: #ef5369; text-align: start;&quot;&gt;:nth-child()&lt;/span&gt;를 이용하면 부모 요소의 첫 번째 자식 요소부터 X번째 자식 요소까지 스타일을 적용할 수 있다. 예를 들어, 아래의 CSS는 첫 번째부터 세 번째까지의 자식 요소에 밑줄을 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;zYgKLpM&quot; data-pen-title=&quot;:nth-child 선택자를 이용해 X번째에 위치한 요소까지 스타일을 적용&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/zYgKLpM&quot;&gt; :nth-child 선택자를 이용해 X번째에 위치한 요소까지 스타일을 적용&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 -n+3은 n이 0부터 시작해 1씩 증가하면서, 세 번째부터 첫 번째 자식 요소까지 선택하는 방식이다. 즉, n=0일 때는 세 번째 요소, n=1일 때는 두 번째 요소, n=2일 때는 1번째 요소를 선택하게 된다. HTML 목록이 1-based 인덱싱을 사용하므로, 실제로 선택되는 요소가 첫 번째, 두 번째, 세 번째 요소라는 것에 유의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 처음 일곱번째 요소까지 스타일을 적용하고 싶다면, &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:nth-child(-n+7)&lt;/span&gt;와 같이 뒤의 숫자만 7로 바꿔 적용하면 된다. 이 방식은 콘텐츠가 많을 때, 처음 몇 개의 요소에만 스타일을 적용하여 주요 내용을 강조하고 싶을 때 주로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특정 패턴의 요소 선택&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f5f5f5; color: #ef5369; text-align: start;&quot;&gt;:nth-child()&lt;/span&gt;&amp;nbsp;선택자의 수식에서는 n에 계수를 설정하여 조금 더 복잡한 패턴을 만들 수 있다. 예를 들어, 3n+1이라는 수식을 사용하면 세 번째마다 첫 번째 요소가 선택된다. 이는 1, 4, 7, 10&amp;hellip; 번째 요소들을 선택하여, 다음과 같이 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;gOVwjeM&quot; data-pen-title=&quot;:nth-child 선택자를 이용해 특정 패턴의 요소에 스타일을 적용&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/gOVwjeM&quot;&gt; :nth-child 선택자를 이용해 특정 패턴의 요소에 스타일을 적용&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;맺으며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:first-child&lt;/span&gt;, &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:last-child&lt;/span&gt;, &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:nth-child()&lt;/span&gt;와 같은 CSS 자식 선택자들은 첫 번째, 마지막, 또는 특정 순번의 자식 요소에 효율적으로 스타일을 적용할 수 있는 강력한 도구이다. 특히 :nth-child() 선택자는 수학적 표현식을 활용해 복잡한 패턴도 손쉽게 처리할 수 있어, HTML 구조 내에서 반복적이고 규칙적인 디자인 작업에 유연하게 대응할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트를 사용해 동적으로 스타일을 적용하는 대신, &lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:first-child&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:last-child&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ef5369; background-color: #f5f5f5;&quot;&gt;:nth-child()&lt;/span&gt; 선택자를 활용하면 성능 최적화와 함께 코드의 간결함을 유지할 수 있다. CSS 선택자만으로도 다양한 조건을 처리할 수 있기 때문에, 이러한 방식으로 리팩토링을 시도해 보는 것도 좋은 방법이다. 이를 통해 DOM 조작을 줄이면서도 효과적으로 원하는 스타일을 적용할 수 있다.&lt;/p&gt;</description>
      <category>개발로그/HTML, CSS</category>
      <category>:first-child</category>
      <category>:last-child</category>
      <category>:nth-child()</category>
      <category>CSS</category>
      <category>html</category>
      <category>자식 선택자</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/79</guid>
      <comments>https://www.ohnimdev.com/entry/CSS%EC%9D%98-%EC%84%A0%ED%83%9D%EC%9E%90%EB%A1%9C-%EC%9E%90%EC%8B%9D-%EC%84%A0%ED%83%9D%ED%95%98%EA%B8%B0-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EB%A7%88%EC%A7%80%EB%A7%89-N%EB%B2%88%EC%A7%B8#entry79comment</comments>
      <pubDate>Wed, 9 Oct 2024 02:34:42 +0900</pubDate>
    </item>
    <item>
      <title>미드저니에서 이미지의 예술성 조절하기: --stylize(--s) 옵션</title>
      <link>https://www.ohnimdev.com/entry/%EB%AF%B8%EB%93%9C%EC%A0%80%EB%8B%88%EC%97%90%EC%84%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%9D%98-%EC%98%88%EC%88%A0%EC%84%B1-%EC%A1%B0%EC%A0%88%ED%95%98%EA%B8%B0-stylize-%EC%98%B5%EC%85%98</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;서론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니(Midjourney)는 사용자가 원하는 이미지 스타일을 세밀하게 조정할 수 있는 다양한 옵션을 제공한다. 그중에서도 --stylize(줄여서 --s) 옵션은 이미지 생성 시 예술적인 표현과 스타일을 얼마나 강조할지 결정하는 역할을 한다. 이 옵션을 통해서 이미지가 프롬프트에 충실할지, 혹은 더 창의적이고 예술적인 표현을 강조할지 선택할 수 있다. --s 값이 높을수록 이미지의 예술성이 강해지며, 낮을수록 프롬프트에 더욱 일치하는 결과를 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;--s 옵션의 영향도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니에서 기본적으로 --s 값은 100으로 설정되어 있으며, 이는 이미지의 예술적 요소와 프롬프트 간의 균형을 유지하는 값이다. 이 옵션은 0부터 1000까지의 정수 값을 지정할 수 있으며, 값이 높을수록 미드저니의 이미지 생성 모델이 더 창의적이고 예술적인 결과물을 만들어낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 1. 2D 로고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--s 옵션에 따른 이미지 스타일 변화를 확인하기 위해 간단한 2D 로고의 예시를 살펴보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;프롬프트: Simple logo of a cute cat wearing sunglasses.&amp;nbsp;--s [값]&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 87px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;결과물&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.6589%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;--s 값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;비고&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cat_s_0_compressed.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3Seme/btsJV0aWmdZ/Gh5h2FIRdKPBJrDtorTtyK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3Seme/btsJV0aWmdZ/Gh5h2FIRdKPBJrDtorTtyK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3Seme/btsJV0aWmdZ/Gh5h2FIRdKPBJrDtorTtyK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3Seme%2FbtsJV0aWmdZ%2FGh5h2FIRdKPBJrDtorTtyK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Simple logo of a cute cat wearing sunglasses. --s 0&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-filename=&quot;cat_s_0_compressed.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 10.6589%; height: 17px; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%; height: 17px;&quot;&gt;프롬프트에 가장 충실한 결과물로, 예술적인 터치가 거의 없다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cat_s_100_compressed.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDQhyi/btsJVYc4qZC/xfwrWsuIOK0ZpnD9CIDUQ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDQhyi/btsJVYc4qZC/xfwrWsuIOK0ZpnD9CIDUQ0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDQhyi/btsJVYc4qZC/xfwrWsuIOK0ZpnD9CIDUQ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDQhyi%2FbtsJVYc4qZC%2FxfwrWsuIOK0ZpnD9CIDUQ0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Simple logo of a cute cat wearing sunglasses. --s 100&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-filename=&quot;cat_s_100_compressed.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 10.6589%; height: 17px; text-align: center;&quot;&gt;100&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%; height: 17px;&quot;&gt;기본값으로, 약간의 예술적인 요소가 가미된 결과물을 확인할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cat_s_500_compressed.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/853KI/btsJV1Vd27m/nZZ26mULLa0XRADiRd1dq1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/853KI/btsJV1Vd27m/nZZ26mULLa0XRADiRd1dq1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/853KI/btsJV1Vd27m/nZZ26mULLa0XRADiRd1dq1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F853KI%2FbtsJV1Vd27m%2FnZZ26mULLa0XRADiRd1dq1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Simple logo of a cute cat wearing sunglasses. --s 500&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-filename=&quot;cat_s_500_compressed.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 10.6589%; height: 17px; text-align: center;&quot;&gt;500&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%; height: 17px;&quot;&gt;프롬프트에 서술하지 않은 예술적인 스타일이 더 강해진 이미지가 출력된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cat_s_1000_compressed.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M2Jad/btsJVC9t1RU/Pm34tvh8r4fpKHlSXYtYz1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M2Jad/btsJVC9t1RU/Pm34tvh8r4fpKHlSXYtYz1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M2Jad/btsJVC9t1RU/Pm34tvh8r4fpKHlSXYtYz1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM2Jad%2FbtsJVC9t1RU%2FPm34tvh8r4fpKHlSXYtYz1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Simple logo of a cute cat wearing sunglasses. --s 1000&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-filename=&quot;cat_s_1000_compressed.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 10.6589%; height: 17px; text-align: center;&quot;&gt;1000&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%; height: 17px;&quot;&gt;독특하고 예술적인 이미지가 만들어 졌지만, 프롬프트의 내용과는 다소 차이가 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 2.  실사 이미지&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 2D 이미지가 아닌, 실사에 가까운 이미지를 생성하는 프롬프트에서는 --s 값에 따라 생성된 이미지가 어떻게 달라지는지 테스트를 해보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;프롬프트:  South korea, 20-year-old woman, intelligent appearance, gold hair, long haircut, Cover her face with a glass. --s [값]&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 87px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;결과물&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.6589%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;--s 값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;비고&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;glass_s_0_compressed.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;2048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqAYuw/btsJV71X2ty/UtiebSwsQ2fUOEdLLbmlSk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqAYuw/btsJV71X2ty/UtiebSwsQ2fUOEdLLbmlSk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqAYuw/btsJV71X2ty/UtiebSwsQ2fUOEdLLbmlSk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqAYuw%2FbtsJV71X2ty%2FUtiebSwsQ2fUOEdLLbmlSk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;South korea, 20-year-old woman, intelligent appearance, gold hair, long haircut, Cover her face with a glass. --s 0&quot; loading=&quot;lazy&quot; width=&quot;2048&quot; height=&quot;2048&quot; data-filename=&quot;glass_s_0_compressed.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;2048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 10.6589%; height: 17px; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%; height: 17px;&quot;&gt;생성된 네 개의 이미지 모두 프롬프트에 충실하며, 예술적이거나 창의적인 표현은 거의 없다. 프롬프트와 일치하는 단순하고 직관적인 이미지를 얻을 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;glass_s_100_compressed.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;2048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baYL9r/btsJV2GkVNJ/pZnCG72wDTOwEZcWx4yV7k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baYL9r/btsJV2GkVNJ/pZnCG72wDTOwEZcWx4yV7k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baYL9r/btsJV2GkVNJ/pZnCG72wDTOwEZcWx4yV7k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaYL9r%2FbtsJV2GkVNJ%2FpZnCG72wDTOwEZcWx4yV7k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;South korea, 20-year-old woman, intelligent appearance, gold hair, long haircut, Cover her face with a glass. --s 100&quot; loading=&quot;lazy&quot; width=&quot;2048&quot; height=&quot;2048&quot; data-filename=&quot;glass_s_100_compressed.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;2048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.6589%; height: 17px; text-align: center;&quot;&gt;100&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%; height: 17px;&quot;&gt;프롬프트에 서술하지 않은 배경에 대한 디테일이 추가되고, 컵을 들고 있는 구도나 표정들이 미세하게 변하기 시작한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;glass_s_500_compressed.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;2048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0pNsa/btsJWadkShw/ywVtxkCmo7vZW9af3qUsgk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0pNsa/btsJWadkShw/ywVtxkCmo7vZW9af3qUsgk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0pNsa/btsJWadkShw/ywVtxkCmo7vZW9af3qUsgk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0pNsa%2FbtsJWadkShw%2FywVtxkCmo7vZW9af3qUsgk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;South korea, 20-year-old woman, intelligent appearance, gold hair, long haircut, Cover her face with a glass. --s 500&quot; loading=&quot;lazy&quot; width=&quot;2048&quot; height=&quot;2048&quot; data-filename=&quot;glass_s_500_compressed.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;2048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 10.6589%; height: 17px; text-align: center;&quot;&gt;500&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%; height: 17px;&quot;&gt;여기서부터는 다양한 배경의 표현과 빛의 효과가 추가되며, 창의적인 요서가 강해진다. 하지만, 컵으로 얼굴을 가리지 않는 등 프롬프트와 다소 멀어진 이미지가 나온다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;glass_s_1000_compressed.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;2048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HTCFb/btsJWSpv6ht/6KyKruxZJKtkwKJSoUHFUk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HTCFb/btsJWSpv6ht/6KyKruxZJKtkwKJSoUHFUk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HTCFb/btsJWSpv6ht/6KyKruxZJKtkwKJSoUHFUk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHTCFb%2FbtsJWSpv6ht%2F6KyKruxZJKtkwKJSoUHFUk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;South korea, 20-year-old woman, intelligent appearance, gold hair, long haircut, Cover her face with a glass. --s 1000&quot; loading=&quot;lazy&quot; width=&quot;2048&quot; height=&quot;2048&quot; data-filename=&quot;glass_s_1000_compressed.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;2048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 10.6589%; height: 17px; text-align: center;&quot;&gt;1000&lt;/td&gt;
&lt;td style=&quot;width: 56.0077%; height: 17px;&quot;&gt;인물의 표정, 빛의 표현, 색감이 예술적으로 변한다. 하지만, 프롬프트에 서술한 컵이 아예 사라지는 등 프롬프트와 거리가 먼 이미지가 생성된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--s 옵션은 미드저니에서 이미지의 예술성과 창의성을 조정하는 데 중요한 역할을 한다. 이 옵션을 통해 프롬프트와 예술적 표현 간의 균형을 쉽게 조절할 수 있다. 만약 프롬프트에 충실한 이미지를 원한다면 --s 값을 낮게 설정하고, 창의적이고 예술적인 이미지가 더 중요하다면 --s 값을 높여 사용할 수 있다. 원하는 결과물에 맞춰 적절한 값을 선택하여 다양한 이미지를 실험해 본다면, 더욱 퀄리티 높은 이미지를 얻을 수 있을 것이다.&lt;/p&gt;</description>
      <category>AI/미드저니</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/78</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%AF%B8%EB%93%9C%EC%A0%80%EB%8B%88%EC%97%90%EC%84%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%9D%98-%EC%98%88%EC%88%A0%EC%84%B1-%EC%A1%B0%EC%A0%88%ED%95%98%EA%B8%B0-stylize-%EC%98%B5%EC%85%98#entry78comment</comments>
      <pubDate>Sun, 6 Oct 2024 18:35:58 +0900</pubDate>
    </item>
    <item>
      <title>[백준 1564번] 팩토리얼5: 정식 풀이</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-1564%EB%B2%88-%ED%8C%A9%ED%86%A0%EB%A6%AC%EC%96%BC5-%EC%A0%95%EC%8B%9D-%ED%92%80%EC%9D%B4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1564&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1564&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(N\)이 주어졌을 때, \(N!\)의 마지막 0이 아닌 5자리 수를 출력하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, \(N\)이 10인 경우 \(10!\)은 &lt;b&gt;3628800&lt;/b&gt;이며, 마지막 0이 아닌 5자리 숫자는 &lt;b&gt;36288&lt;/b&gt;이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BigInt와 같은 큰 수 자료형을 사용해 팩토리얼을 직접 계산하기 위해서는 많은 시간과 공간 복잡도가 필요하다. 따라서 더 효율적인 방법이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼핏 보면 팩토리얼의 마지막 다섯 자리만 유지하면 될 것 같지만, 팩토리얼 값의 끝에 붙는 0, 즉 &lt;b&gt;트레일링 제로&lt;/b&gt;를 제거하고 출력해야 하기 때문에 단순히 마지막 다섯 자리만 유지하는 것으로는 문제를 해결할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 트레일링 제로가 언제 생기는지 파악하는 것이다. 팩토리얼 값의 트레일링 제로는 2와 5의 인수 개수에 의해 결정된다. 곱셈 과정에서 2와 5가 짝을 이루면 10이 되고, 이는 곧 끝에 오는 0이 하나 생기는 것을 의미하기 때문이다. 따라서 팩토리얼을 계산할 때 각 수에서 2와 5의 인수를 제거하여 트레일링 제로를 없앨 수 있다. 그러나 팩토리얼에서 2의 인수는 5의 인수보다 항상 같거나 더 많으므로, 제거한 5의 인수 개수보다 초과된 2의 인수는 최종 결과에 다시 곱해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 다섯 자리를 출력할 때 앞에 0을 채워 출력하면 문제를 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

long long n;
const long long MOD = 1e5;

int main() {
    scanf(&quot;%lld&quot;, &amp;amp;n);
    
    long long ans = 1;
    int cnt2 = 0, cnt5 = 0;
    
    for (long long i = 2 ; i &amp;lt;= n ; ++i) {
        long long num = i;
        
        // 2의 인수를 제거
        while (num % 2 == 0) {
            cnt2 += 1;
            num /= 2;
        }
        
        // 5의 인수 제거
        while (num % 5 == 0) {
            cnt5 += 1;
            num /= 5;
        }
        
        ans = (ans * num) % MOD;
    }
    
    int extra2s = cnt2 - cnt5;
    
    // 트레일링 제로를 만들지 않는 2의 인수를 다시 곱함
    for (int i = 0 ; i &amp;lt; extra2s ; ++i){
        ans = (ans * 2) % MOD;
    }
    
    printf(&quot;%05lld\n&quot;, ans);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 1564번</category>
      <category>백준 1564번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/77</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-1564%EB%B2%88-%ED%8C%A9%ED%86%A0%EB%A6%AC%EC%96%BC5-%EC%A0%95%EC%8B%9D-%ED%92%80%EC%9D%B4#entry77comment</comments>
      <pubDate>Sat, 28 Sep 2024 01:14:52 +0900</pubDate>
    </item>
    <item>
      <title>미드저니에서 원하는 요소 제거하기: --no 옵션과 멀티 프롬프트</title>
      <link>https://www.ohnimdev.com/entry/%EB%AF%B8%EB%93%9C%EC%A0%80%EB%8B%88%EC%97%90%EC%84%9C-%EC%9B%90%ED%95%98%EB%8A%94-%EC%9A%94%EC%86%8C-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0-no-%EC%98%B5%EC%85%98%EA%B3%BC-%EB%A9%80%ED%8B%B0-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;서론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니(Midjourney)는 텍스트 기반의 프롬프트를 사용하여 이미지를 생성할 수 있는 AI 이미지 생성 도구이다. 그러나 때로는 불필요한 요소들이 이미지에 포함되어 원하는 결과를 얻지 못할 때도 있다. 이런 문제를 해결하기 위해 미드저니는 &lt;b&gt;--no 옵션&lt;/b&gt;과 &lt;b&gt;멀티 프롬프트&lt;/b&gt; 기능을 제공한다. 이 기능을 활용하면 통해 특정 요소를 제거하거나 요소들의 비중을 조절하여, 원하는 것과 더 가까운 이미지를 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 미드저니에서 원하는 요소를 제거하는 두 가지 방법을 소개하고, 이를 활용해 이미지의 완성도를 높이는 방법을 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;--no 옵션을 사용해 원하는 요소 제거하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;--no&lt;/b&gt; 옵션은 이미지 생성 프롬프트에서 특정 요소를 포함시키고 싶지 않을 때 사용한다. 이 옵션을 프롬프트에 추가하면, 제외할 항목을 명확하게 지정하여 원하는 결과에 더 가까운 이미지를 생성할 수 있다. 풍경 수채화를 만드는 &quot;Landscape watercolor painting&quot; 프롬프트로 생성된 이미지를 살펴보자. 생성된 이미지에는 나무와 물들이 포함되어 있다.&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;table style=&quot;border-collapse: collapse; width: 58.1395%; height: 34px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 17px; text-align: center;&quot;&gt;Landscape watercolor painting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 17px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;no_옵션_넣기_전_compressed.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFC2Bq/btsJMAqLCc2/WOIUdemReCaiZ8EkBDopQ0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFC2Bq/btsJMAqLCc2/WOIUdemReCaiZ8EkBDopQ0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFC2Bq/btsJMAqLCc2/WOIUdemReCaiZ8EkBDopQ0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFC2Bq%2FbtsJMAqLCc2%2FWOIUdemReCaiZ8EkBDopQ0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Landscape watercolor painting&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1000&quot; data-filename=&quot;no_옵션_넣기_전_compressed.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이미지에서 나무나 물을 제외하고 싶다면, 다음과 같은 프롬프트를 사용할 수 있다.&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;table style=&quot;border-collapse: collapse; width: 93.0232%; height: 402px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;Landscape watercolor painting --no trees&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;Landscape watercolor painting --no water&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 385px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 385px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;no_tress_옵션_넣기_compressed.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqfqK8/btsJOl6Dmtg/kZ0DLRK18Eit535rPvxiUK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqfqK8/btsJOl6Dmtg/kZ0DLRK18Eit535rPvxiUK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqfqK8/btsJOl6Dmtg/kZ0DLRK18Eit535rPvxiUK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqfqK8%2FbtsJOl6Dmtg%2FkZ0DLRK18Eit535rPvxiUK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Landscape watercolor painting --no trees&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1000&quot; data-filename=&quot;no_tress_옵션_넣기_compressed.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 385px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;no_water_옵션_넣기_compressed.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wujbh/btsJMBwnUl9/sRlEI79jcuXXycEAvEK45k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wujbh/btsJMBwnUl9/sRlEI79jcuXXycEAvEK45k/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wujbh/btsJMBwnUl9/sRlEI79jcuXXycEAvEK45k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwujbh%2FbtsJMBwnUl9%2FsRlEI79jcuXXycEAvEK45k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Landscape watercolor painting --no water&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;no_water_옵션_넣기_compressed.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 여러 요소를 동시에 제거할 수도 있다. 예를 들어, 나무와 물이 모두 없는 이미지를 생성하고 싶다면, 다음과 같이 콤마(,)를 사용해 제거하고 싶은 요소들을 연결하면 된다.&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;table style=&quot;border-collapse: collapse; width: 58.1395%; height: 62px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%; text-align: center;&quot;&gt;Landscape watercolor painting --no trees, water&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;no_trees_water_옵션_넣기_compressed.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TdF4F/btsJN0Itm0q/bPkNso1ELkgxmmoxWyavgk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TdF4F/btsJN0Itm0q/bPkNso1ELkgxmmoxWyavgk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TdF4F/btsJN0Itm0q/bPkNso1ELkgxmmoxWyavgk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTdF4F%2FbtsJN0Itm0q%2FbPkNso1ELkgxmmoxWyavgk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Landscape watercolor painting --no trees, water&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1000&quot; data-filename=&quot;no_trees_water_옵션_넣기_compressed.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  (주의) 부정문을 사용하지 말기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니 공식 문서에 의하면, 프롬프트에 포함된 모든 단어들은 잠재적으로 이미지에 포함될 가능성이 있다. 따라서 프롬프트에서 &lt;b&gt;don't &lt;/b&gt;나 &lt;b&gt;without&lt;/b&gt; 과 같은 부정문을 사용해 특정 요소를 제거하려고 시도할 경우, 미드저니는 이를 인간과 다르게 해석하여&amp;nbsp;오히려 해당 요소가 이미지에서 더 많이 나타날 수 있다.&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;table style=&quot;border-collapse: collapse; width: 58.1395%; height: 58px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%; text-align: center;&quot;&gt;Landscape watercolor painting. Please don't add trees! No water!!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;부정문으로_trees_water_제거해보기_compressed.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baTQbz/btsJNQ0oe9Z/15eG3RxYjkloLZumPi9KW1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baTQbz/btsJNQ0oe9Z/15eG3RxYjkloLZumPi9KW1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baTQbz/btsJNQ0oe9Z/15eG3RxYjkloLZumPi9KW1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaTQbz%2FbtsJNQ0oe9Z%2F15eG3RxYjkloLZumPi9KW1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Landscape watercolor painting. Please don't add trees! No water!!&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1000&quot; data-filename=&quot;부정문으로_trees_water_제거해보기_compressed.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 프롬프트에서는 나무와 물을 제외하길 원했지만, 실제로 나무와 물이 강조된 이미지가 생성됐다. 이처럼 부정문을 사용하면 의도와 다른 결과가 나올 수 있으므로, &lt;b&gt;--no&lt;/b&gt; 옵션을 사용해 제거할 항목을 정확하게 지정하는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;멀티 프롬프트를 사용해 요소들의 가중치를 조절하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니에서는 &lt;b&gt;멀티 프롬프트&lt;/b&gt;를 사용해 여러 요소들의 가중치를 조정할 수 있다. 이를 통해 이미지에 포함될 요소들의 비율을 세밀하게 조정하거나, 특정 요소의 비중을 줄일 수 있다. 멀티 프롬프트는 이미지에서 원하는 요소를 완전히 제거하는 대신, 해당 요소를 조금 덜 보이게 하고 싶을 때 유용하게 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 풍경 수채화를 만드는 &quot;Landscape watercolor painting&quot; 프롬프트에서 나무의 비중을 조금 줄이고 싶다면, 다음과 같은 &lt;b&gt;멀티 프롬프트&lt;/b&gt;를 사용할 수 있다.&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;table style=&quot;border-collapse: collapse; width: 58.1395%; height: 61px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%; text-align: center;&quot;&gt;Landscape watercolor painting:: trees::-0.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;멀티_프롬프트에서_trees를_음수_쩜삼으로_표현_compressed.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsM18K/btsJNfTX4M4/khnyObUmKkAtlA0J0gJ8x0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsM18K/btsJNfTX4M4/khnyObUmKkAtlA0J0gJ8x0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsM18K/btsJNfTX4M4/khnyObUmKkAtlA0J0gJ8x0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsM18K%2FbtsJNfTX4M4%2FkhnyObUmKkAtlA0J0gJ8x0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Landscape watercolor painting:: trees::-0.3&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;700&quot; data-filename=&quot;멀티_프롬프트에서_trees를_음수_쩜삼으로_표현_compressed.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 프롬프트는 나무의 비중을 조금 낮추어, 이미지에서 나무가 덜 나타나게 한다. 참고로, &lt;b&gt;--no&lt;/b&gt; 옵션을 사용하면 멀티 프롬프트에서 -0.5로 가중치를 주는 것과 동일한 효과를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;멀티 프롬프트&lt;/b&gt;의 큰 장점은 여러 요소를 비율을 동시에 조정할 수 있다는 것이다. 예를 들어, 산과 물을 강조하고 나무의 비율을 줄이고 싶다면 다음과 같은 프롬프트를 사용할 수 있다.&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;table style=&quot;border-collapse: collapse; width: 58.1395%; height: 20px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%; text-align: center;&quot;&gt;Landscape watercolor painting:: mountain::2 water::1 trees::-0.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;멀티_프롬프트로_산물_강조_나무_제거_compressed.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RTosH/btsJOydBG9q/vyPkJ7hjK3mZjl3a9BSxkk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RTosH/btsJOydBG9q/vyPkJ7hjK3mZjl3a9BSxkk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RTosH/btsJOydBG9q/vyPkJ7hjK3mZjl3a9BSxkk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRTosH%2FbtsJOydBG9q%2FvyPkJ7hjK3mZjl3a9BSxkk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Landscape watercolor painting:: mountain::2 water::1 trees::-0.5&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;1000&quot; data-filename=&quot;멀티_프롬프트로_산물_강조_나무_제거_compressed.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프롬프트는 산을 가장 크게 강조하고, 물을 그다음으로 강조하며, 나무의 비중을 가장 낮춘 결과를 얻을 수 있다. 이처럼 &lt;b&gt;멀티 프롬프트&lt;/b&gt;를 사용한다면 이미지 속 다양한 요소들을 더 유연하게 제어할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미드저니에서 원하지 않는 요소를 제거하는 두 가지 주요한 방법은 &lt;b&gt;--no&lt;/b&gt; 옵션과 &lt;b&gt;멀티 프롬프트&lt;/b&gt;가 있다. &lt;b&gt;--no&lt;/b&gt; 옵션은 특정 요소를 완전히 제거하는 데 적합하며, &lt;b&gt;멀티 프롬프트&lt;/b&gt;는 여러 요소들의 비중을 세밀하게 조정하는데 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만드려고 하는 이미지의 목적과 세부 사항에 따라 &lt;b&gt;--no&lt;/b&gt; 옵션과 &lt;b&gt;멀티 프롬프트&lt;/b&gt;를 적절히 활용한다면, 원하는 결과에 근접하는 높은 퀄리티 결과물을 얻을 수 있을 것이다.&lt;/p&gt;</description>
      <category>AI/미드저니</category>
      <category>--no</category>
      <category>가중치</category>
      <category>멀티 프롬프트</category>
      <category>미드저니</category>
      <category>요소제거</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/76</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%AF%B8%EB%93%9C%EC%A0%80%EB%8B%88%EC%97%90%EC%84%9C-%EC%9B%90%ED%95%98%EB%8A%94-%EC%9A%94%EC%86%8C-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0-no-%EC%98%B5%EC%85%98%EA%B3%BC-%EB%A9%80%ED%8B%B0-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8#entry76comment</comments>
      <pubDate>Fri, 27 Sep 2024 09:20:40 +0900</pubDate>
    </item>
    <item>
      <title>[백준 21925번] 짝수 팰린드롬</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-21925%EB%B2%88-%EC%A7%9D%EC%88%98-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/21925&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/21925&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 길이가 \(N\)인 수열이 \(A\)가 주어진다. 수열 \(A\)를 길이가 짝수인 부분 수열들로 나눌 때, 짝수 팰린드롬을 최대한 많이 만드는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 부분 수열의 팰린드롬 여부를 미리 계산하는 전처리와 동적 계획법(Dynamic Programming)을 사용하여 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 부분 수열의 팰린드롬 여부를 미리 계산하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부분 수열이 팰린드롬인지 여부를 나타내기 위해, boolean 타입의 2차원 배열 isPalindrome을 사용한다. 이 배열에서 isPalindrome[j][i]는 인덱스 \(j\)부터 \(i\)까지의 부분 수열이 팰린드롬인지를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 가능한 모든 \(j\)와 \(i\)에 대해 \(A[j...i]\)가 팰린드롬인지 확인하려면, 각 구간을 하나하나 비교해야 하므로 시간 복잡도는 \(\mathcal{O}(N^3)\)이 된다. 이는 시간제한 내에 전처리를 할 수 없다는 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해 팰린드롬의 특성을 이용할 수 있다. 팰린드롬이 성립하려면 양 끝이 같아야 하고, 그 안쪽 부분도 팰린드롬이어야만 한다. 즉, \(A[j...i]\)가 팰린드롬이려면 \(A[j +1 ...i - 1]\)도 팰린드롬이어야 한다는 의미다. 이 특성을 활용하면 전체 구간을 매번 비교하지 않고도 빠르게 팰린드롬 여부를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수열의 한 점 \(i\)와 \(i + 1\)을 기준으로 잡고 양쪽으로 확장해 가면서 팰린드롬 여부를 확인한다. 만약 중간에 팰린드롬이 아닌 경우가 나온다면, 양쪽을 더 확장한다고 하더라도 더 이상 팰린드롬인 부분 수열을 발견하지 못할 것이다. 이 방법은 \(\mathcal{O}(N^2)\)의 시간 복잡도를  가지기 때문에, 시간제한 내에 전처리를 완료할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726936526561&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void calcIsPalindrome() {
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        int lo = i, hi = i + 1;
        
        while (lo &amp;gt;= 1 &amp;amp;&amp;amp; hi &amp;lt;= n &amp;amp;&amp;amp; A[lo] == A[hi]) {
            isPalindrome[lo][hi] = true;
            
            --lo;
            ++hi;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 동적 계획법으로 문제 해결하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팰린드롬 여부를 미리 계산했으면, 동적 계획법(Dynamic Programming)을 사용해 최대 몇 개의 짝수 길이 팰린드롬으로 나눌 수 있는지 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(dp[i]\)를 수열의 첫 번째 인덱스부터 \(i\)번째 인덱스까지 짝수 길이 팰린드롬으로 분할할 수 있는 최대 개수라고 정의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 인덱스 \(i\)에 대해서, \(i\)보다 작은 모든 인덱스 \(j\)를 탐색한다. \(j\)부터 \(i\)까지의 부분 수열이 짝수 길이의 팰린드롬이라면, \(dp[i]\)를 \(dp[j - 1] + 1\)과 현재 값 중 큰 값으로 갱신한다. 이를 점화식으로 표현한다면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$dp[i] = max(dp[i], dp[j - 1] + 1)$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점화식을 코드로 옮길 때에는 부분 수열 \(A[...j - 1]\)이 짝수 팰린드롬으로 나눌 수 있는 경우인지 확인하는 것을 잊지 말자.&lt;/p&gt;
&lt;pre id=&quot;code_1726936751346&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void solve() {
    memset(dp, -1, sizeof(dp));
    
    dp[0] = 0;
    
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        for (int j = i - 1 ; j &amp;gt;= 1 ; j -= 2) {
            if (isPalindrome[j][i] &amp;amp;&amp;amp; dp[j - 1] != -1) {
                dp[i] = max(dp[i], dp[j - 1] + 1);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1726936785249&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int n, A[5010], dp[5010];
bool isPalindrome[5010][5010];

void calcIsPalindrome() {
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        int lo = i, hi = i + 1;
        
        while (lo &amp;gt;= 1 &amp;amp;&amp;amp; hi &amp;lt;= n &amp;amp;&amp;amp; A[lo] == A[hi]) {
            isPalindrome[lo][hi] = true;
            
            --lo;
            ++hi;
        }
    }
}

void solve() {
    memset(dp, -1, sizeof(dp));
    
    dp[0] = 0;
    
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        for (int j = i - 1 ; j &amp;gt;= 1 ; j -= 2) {
            if (isPalindrome[j][i] &amp;amp;&amp;amp; dp[j - 1] != -1) {
                dp[i] = max(dp[i], dp[j - 1] + 1);
            }
        }
    }
}

int main() {
    scanf(&quot;%d&quot;, &amp;amp;n);
    
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        scanf(&quot;%d&quot;, &amp;amp;A[i]);
    }
    
    calcIsPalindrome();
    solve();
    
    printf(&quot;%d\n&quot;, dp[n]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/75</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-21925%EB%B2%88-%EC%A7%9D%EC%88%98-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC#entry75comment</comments>
      <pubDate>Sun, 22 Sep 2024 23:19:46 +0900</pubDate>
    </item>
    <item>
      <title>[백준 20040번] 사이클 게임</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-20040%EB%B2%88-%EC%82%AC%EC%9D%B4%ED%81%B4-%EA%B2%8C%EC%9E%84</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/20040&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/20040&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(N\)개의 점이 있다. 이때 서로 다른 두 개의 점을 연결하는 선분들이 차례대로 주어졌을 때, 사이클이 생기는 순간을 찾는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 점을 노드로, 선분을 간선으로 하는 무향 그래프로 모델링을 한다. 새로운 간선을 추가할 때마다 두 노드가 같은 집합에 속해 있는지 확인한다. 만약 같은 집합에 속해 있다면, 해당 간선을 추가하면 사이클이 형성되는 것이다. 반대로, 같은 집합에 속해있지 않으면 두 집합을 합쳐서 하나의 집합으로 만든다. 유니온 파인드(Union Find) 자료구조를 이용해 집합을 효율적으로 관리할 수 있으며&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;, 제한시간 내에 문제를 해결할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

// Union Find 자료구조
struct UnionFind {
    vector&amp;lt;int&amp;gt; parent;
    vector&amp;lt;int&amp;gt; rank;
    
    UnionFind(int n): rank(n, 1), parent(n) {
        for (int i = 0 ; i &amp;lt; n ; ++i) {
            parent[i] = i;
        }
    }
    
    int find(int u) {
        if (u == parent[u]) {
            return u;
        }
        
        return parent[u] = find(parent[u]);
    }
    
    bool merge(int u, int v) {
        u = find(u);
        v = find(v);
        
        if (u == v) {
            return false;
        }
        
        if (rank[u] &amp;gt; rank[v]) {
            swap(u, v);
        }
        
        parent[u] = v;
        
        if (rank[u] == rank[v]) {
            ++rank[v];
        }
        
        return true;
    }
};

int main() {
    int n, m;
    
    scanf(&quot;%d%d&quot;, &amp;amp;n, &amp;amp;m);
    
    UnionFind uf(n + 1);
    
    for (int i = 1 ; i &amp;lt;= m ; ++i) {
        int u, v;
        
        scanf(&quot;%d%d&quot;, &amp;amp;u, &amp;amp;v);
        
        if (!uf.merge(u, v)) {
            printf(&quot;%d\n&quot;, i);
            
            return 0;
        }
    }
    
    puts(&quot;0&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 20040번</category>
      <category>백준 20040번</category>
      <category>사이클 검출</category>
      <category>유니온 파인드</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/74</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-20040%EB%B2%88-%EC%82%AC%EC%9D%B4%ED%81%B4-%EA%B2%8C%EC%9E%84#entry74comment</comments>
      <pubDate>Sun, 22 Sep 2024 00:02:36 +0900</pubDate>
    </item>
    <item>
      <title>HTML에서 form을 원격으로 제출하는 방법</title>
      <link>https://www.ohnimdev.com/entry/HTML%EC%97%90%EC%84%9C-form%EC%9D%84-%EC%9B%90%EA%B2%A9%EC%9C%BC%EB%A1%9C-%EC%A0%9C%EC%B6%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;서론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 개발에서 사용자로부터 입력받은 데이터를 서버로 전송하는 작업은 흔한 일이다. 일반적으로는 HTML의 &lt;span style=&quot;background-color: #fafafa; color: #eb5757;&quot; data-token-index=&quot;1&quot;&gt;form&lt;/span&gt; 요소를 사용하여 아래와 같이 간단히 구현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726857198143&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form action=&quot;/api/posts&quot; method=&quot;POST&quot;&amp;gt;
  &amp;lt;input type=&quot;text&quot; name=&quot;title&quot; required /&amp;gt;
  &amp;lt;input type=&quot;text&quot; name=&quot;content&quot; required /&amp;gt;
  &amp;lt;button type=&quot;submit&quot;&amp;gt;작성&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 직관적이지만, 더 복잡한 요구사항이 생기는 동적인 웹 애플리케이션에서는 제약이 생길 수 있다. 예를 들어, &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt; 내부가 아닌 웹 애플리케이션의 GNB의 영역에도 &amp;ldquo;작성&amp;rdquo; 버튼을 추가해야 한다면 어떻게 해결할 수 있을까? 이 글에서는 submit 타입의 버튼이 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt; 내부에 없어도, HTML의 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt;을 원격으로 제출하는 방법들을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript를 이용해 form을 원격으로 제출하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript는 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt;과 제출 버튼이 부모-자식 관계가 아닐 때에도 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt;을 원격으로 제출할 수 있는 방법을 제공한다. 이 방식은 단순히 form을 원격으로 제출하는 것뿐만 아니라 복잡한 유효성 검사와 다양한 이벤트 처리를 포함할 수 있어, 동적인 웹 애플리케이션에서 자주 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 form의 id 속성에 고유한 값을 지정하고, JavaScript를 이용해 form 요소를 선택한 후 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;submit()&lt;/span&gt; 메서드를 호출하여 원격으로 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt;을 제출할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726857263447&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.getElementById(&quot;myForm&quot;).submit();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 간단히 테스트 해볼 수 있는 예제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;rNXBLGv&quot; data-pen-title=&quot;JavaScript를 이용해 form을 원격으로 제출하기&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/rNXBLGv&quot;&gt; JavaScript를 이용해 form을 원격으로 제출하기&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HTML을 이용해 form을 원격으로 제출하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML만으로도 form을 원격으로 제출할 수 있다. 이 방법은 JavaScript 코드를 작성할 필요가 없기 때문에, 간단한 웹 애플리케이션을 구축할 때 유용하다. HTML의 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;button&lt;/span&gt;&amp;nbsp;요소(element)의 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt; 속성(attribute)을 사용하면, 서로 다른 위치에 있는 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt;&amp;nbsp;요소(element)를 참조하여 원격으로 제출할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726857325301&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;form id=&quot;myForm&quot; action=&quot;/api/posts&quot; method=&quot;POST&quot;&amp;gt;
  &amp;lt;input type=&quot;text&quot; name=&quot;title&quot; required /&amp;gt;
  &amp;lt;input type=&quot;text&quot; name=&quot;content&quot; required /&amp;gt;
&amp;lt;/form&amp;gt;

&amp;lt;button type=&quot;submit&quot; form=&quot;myForm&quot;&amp;gt;작성&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법의 가장 큰 장점은 코드가 간결해 유지 보수가 용이하다는 것이다. 그러나, 복잡한 유효성 검사나 조건부 제출 등 더 복잡한 요구사항이 있다면 JavaScript를 사용하는 것이 더 적합할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 간단히 테스트 해볼 수 있는 예제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;gOVYMeP&quot; data-pen-title=&quot;HTML을 이용해 form을 원격으로 제출하기&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/gOVYMeP&quot;&gt; HTML을 이용해 form을 원격으로 제출하기&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML이나 JavaScript를 이용하면 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt;과 관련되 요소들(&lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;button&lt;/span&gt;, &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;input&lt;/span&gt; 등)이 부모-자식 관계를 유지하지 않아도 원격으로 제출할 수 있는 유연한 방법을 제공한다. 이를 통해 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt; 요소와 제어 요소들이 분리된 구조를 만들 수 있으며, 다양한 웹 디자인이 가능해 다양한 사용자 경험을 제공할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, HTML 공식 문서에서는 &lt;span style=&quot;background-color: #fafafa; color: #eb5757; text-align: start;&quot;&gt;form&lt;/span&gt; 요소와 그 제어 요소들이 부모-자식 관계를 구성하는 것이 더 자연스럽다고 설명하고 있다. 따라서 이러한 기능을 사용할 때는 프로젝트의 복잡도나 유지보수성을 고려해 선택을 하는 것이 좋을 것 같다.&lt;/p&gt;</description>
      <category>개발로그/HTML, CSS</category>
      <category>form</category>
      <category>form 원격 제출</category>
      <category>html</category>
      <category>javascript</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/73</guid>
      <comments>https://www.ohnimdev.com/entry/HTML%EC%97%90%EC%84%9C-form%EC%9D%84-%EC%9B%90%EA%B2%A9%EC%9C%BC%EB%A1%9C-%EC%A0%9C%EC%B6%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry73comment</comments>
      <pubDate>Sat, 21 Sep 2024 10:56:05 +0900</pubDate>
    </item>
    <item>
      <title>네이밍 컨벤션(Naming Convention)이란? - 변수의 이름을 짓는 규칙</title>
      <link>https://www.ohnimdev.com/entry/%EB%84%A4%EC%9D%B4%EB%B0%8D-%EC%BB%A8%EB%B2%A4%EC%85%98-Naming-Convention-%EC%9D%B4%EB%9E%80-%EB%B3%80%EC%88%98%EC%9D%98-%EC%9D%B4%EB%A6%84%EC%9D%84-%EC%A7%93%EB%8A%94-%EA%B7%9C%EC%B9%99</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;서론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;협업을 하면 좋은 변수 이름을 짓기 위해 노력하는 것은 더 이상 선택의 문제가 아니게 된다. 변수 이름은 코드의 가독성, 유지보수성, 그리고 업무의 효율성에 큰 영향을 미치는 중요한 요소이다. 잘못된 변수 이름은 코드를 이해하기 어렵게 만들고, 팀의 생산성을 저하시킬 수 있다. 그래서 개발자들 사이에서는 코딩 시간의 절반 이상을 변수 이름에 짓는데 쓴다는 농담이 있을 정도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위한 대표적인 방법들 중 하나가 &lt;span data-token-index=&quot;1&quot;&gt;&lt;b&gt;네이밍 컨벤션(Naming Convention)&lt;/b&gt;이다&lt;/span&gt;. 변수 이름은 보통 여러 개의 영어 단어들로 이루어지는데, 네이밍 컨벤션은 이 단어들을 어떻게 결합할지에 대한 규칙을 제공한다. 일관성 있는 네이밍 컨벤션을 따르면, 변수 이름만 보고도 그 변수가 어떤 역할을 하는지 쉽게 예측할 수 있다. 이제 프로그래밍에서 사용되는 다양한 네이밍 컨벤션에 대해서 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;camelCase(카멜 케이스)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;camelCase&lt;/span&gt;&lt;/b&gt;는 첫 번째 단어만 소문자로 작성하고, 이후 각 단어들의 첫 글자는 대문자로 작성하는 네이밍 컨벤션이다. 중간의 대문자들이 낙타의 혹처럼 생겼다고 해서 &lt;span data-token-index=&quot;2&quot;&gt;camelCase&lt;/span&gt;라고 이름이 붙여졌다.&lt;/p&gt;
&lt;pre id=&quot;code_1726071256269&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let productCategoryId = 123;
let customerMembershipLevel = &quot;vip&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;camelCase&lt;/span&gt;는 JavaScript, Java, Swift와 같은 프로그래밍 언어에서 변수명, 함수명, 멤버 변수 등을 정의할 때 많이 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;PascalCase(파스칼 케이스)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;PascalCase&lt;/span&gt;&lt;/b&gt;는 모든 단어의 첫 글자를 대문자로 작성하는 네이밍 컨벤션이다. 이 규칙은 Pascal 프로그래밍 언어에서 유래된 것으로 알려져 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726071314658&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ProductCategory {
  private CategoryId = 123;
  private CategoryName = &quot;vip&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PascalCase는 JavaScript, Java, Swift와 같은 언어에서 클래스나 인터페이스 등을 정의할 때 널리 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;snake_case(스네이크 케이스)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;snake_case&lt;/b&gt;는 모든 문자를 소문자로 작성하고, 각 단어는 밑줄(&amp;rdquo;_&amp;rdquo;)로 구분하는 네이밍 컨벤션이다. 단어들 사이에 있는 밑줄들이 마치 뱀처럼 이어진다고 해서 snake_case라는 이름이 붙었다고 알려져 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726071347637&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int product_category_id = 123;
string customer_membership_level = &quot;vip&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;snake_case는 C, C++과 같은 언어에서 변수를 선언하거나 데이터베이스에서 테이블, 컬럼 이름을 작성할 때 주로 사용된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SCREAMING_SNAKE_CASE(스크리밍 스네이크 케이스)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SCREAMING_SNAKE_CASE&lt;/b&gt;는 모든 문자를 대문자로 작성하고, 각 단어를 밑줄(&amp;rdquo;_&amp;rdquo;)로 구분하는 네이밍 컨벤션이다. 모든 문자가 대문자라서 마치 외치고(SCREAMING) 있는 것처럼 보인다는 데서 유래됐다.&lt;/p&gt;
&lt;pre id=&quot;code_1726071383181&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const MAX_CONNECTIONS = 100;
const DEFAULT_TIMEOUT = 30;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SCREAMING_SNAKE_CASE는 대부분의 프로그래밍 언어에서 변하지 않는 값을 나타내는 상수나 매크로, 그리고 환경 변수를 정의하는 데 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;kebab-case(케밥 케이스)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;kebab-case&lt;/b&gt;는 각 단어를 소문자로 작성하고, 단어 사이를 하이픈(&amp;rdquo;-&amp;rdquo;)으로 구분하는 네이밍 컨벤션이다. 단어들 사이에 있는 하이픈이 마치 케밥을 끼운 꼬치처럼 해 보여 kebab-case라는 이름이 붙여졌다.&lt;/p&gt;
&lt;pre id=&quot;code_1726071414736&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.product-category-container {
  background-image: url(https://example.com/product-category/new-arrivals.png);
  font-size: 20px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서 볼 수 있듯이, kebab-case는 주로 CSS, HTML, URL 경로와 같은 웹 개발에서 널리 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그 외&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 현대 프로그래밍에서 주로 사용되는 네이밍 컨벤션에 대해 알아봤다. 그런데 자료를 조사하다 보니 이 외에도 다양한 네이밍 컨벤션이 있다는 것을 알았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들은 현대 프로그래밍에서 자주 사용되지는 않지만, 네이밍 컨벤션의 발전 역사를 알 수 있어 간략히 소개하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;flatcase(플랫 케이스)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;b&gt;flatcase&lt;/b&gt;는 &lt;/span&gt;모든 문자를 소문자로 작성하며, 단어와 단어 사이에 구분 기호를 사용하지 않는 네이밍 컨벤션이다. 주로 과거 저수준 언어나 하드웨어 용량 제한이 있었던 환경에서 사용되었다고 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1726071496372&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let productcategoryid = 123;
let customermembershiplevel = &quot;vip&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flatcase는 단어와 단어가 구분되지 않아 직관적인 의미 전달이 어렵다. 이 때문에 코드 가독성을 중요시하는 현대 프로그래밍에서는 거의 사용되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;UPPERCASE(어퍼 케이스)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;UPPERCASE&lt;/span&gt;&lt;/b&gt; 또는 &lt;b&gt;&lt;span data-token-index=&quot;2&quot;&gt;SCREAMINGCASE(스크리밍 케이스)&lt;/span&gt;&lt;/b&gt;는 모든 문자를 대문자로 작성하며, 단어와 단어 사이에 구분 기호를 사용하지 않는 네이밍 컨벤션이다. 이 방식은 주로 상수(Constant)처럼 변경되지 않는 값을 나타낼 때 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1726071529464&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const MAXCONNECTIONS = 100;
const DEFAULTTIMEOUT = 30;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UPPERCASE는 flatcase와 마찬가지로 단어와 단어를 구분하기 힘들어 가독성이 떨어지며 현대 프로그래밍에서는 거의 사용되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Hungarian Notation (헝가리안 표기법)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Hungarian Notation&lt;/b&gt;은 변수명 앞에 그 변수의 자료형을 명시하는 네이밍 컨벤션이다. 이 방식은 변수의 데이터 타입을 직관적으로 파악할 수 있도록 도와준다.&lt;/p&gt;
&lt;pre id=&quot;code_1726071560380&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int iAge = 25;
float fPrice = 19.99;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 현대 프로그래밍 언어에서는 자동으로 타입 추론이 되거나, 강타입 언어들인 경우가 대부분이라 그 필요성이 많이 줄어들어 거의 사용하지 않는 네이밍 컨벤션이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;aLtErNaTiNgCaSe(교차 대소문자 케이스)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Alternating Case는 단어의 각 문자를 대문자와 소문자를 반복적으로 교차시키는 네이밍 컨벤션이다.&lt;/p&gt;
&lt;pre id=&quot;code_1726071583938&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let pRoDuCtCaTeGoRyId = 123;
let cUsToMeRmEmBeRsHiPlEvEl = &quot;vip&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Alternating Case는 대소문자를 번갈아가며 작성하기 때문에 가독성이 매우 떨어진다. 이런 이유 때문에 실무 코드에서는 거의 사용되지 않고, 비공식적인 코드나 SNS에서 주로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 다양한 네이밍 컨벤션에 대해 알아보았다. 네이밍 컨벤션에는 정답이 없으며, 사용하는 프로그래밍 언어나 협업하는 팀의 문화에 따라 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구 그 자체보다는 코드의 가독성을 높이고 팀의 생산성을 향상시키는 목적에 중점을 두어, 현재 상황에 맞는 네이밍 컨벤션을 선택하는 것이 중요하다.&lt;/p&gt;</description>
      <category>개발로그/잡다한 이야기</category>
      <category>Naming Convention</category>
      <category>네이밍 컨벤션</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/72</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%84%A4%EC%9D%B4%EB%B0%8D-%EC%BB%A8%EB%B2%A4%EC%85%98-Naming-Convention-%EC%9D%B4%EB%9E%80-%EB%B3%80%EC%88%98%EC%9D%98-%EC%9D%B4%EB%A6%84%EC%9D%84-%EC%A7%93%EB%8A%94-%EA%B7%9C%EC%B9%99#entry72comment</comments>
      <pubDate>Thu, 12 Sep 2024 01:56:17 +0900</pubDate>
    </item>
    <item>
      <title>[백준 17216번] 가장 큰 감소 부분 수열</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17216%EB%B2%88-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EA%B0%90%EC%86%8C-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17216&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/17216&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 수열 \(A\)에서 합이 가장 큰 감소하는 부분 수열을 찾는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감소하는/증가하는 부분 수열을 찾는 문제는 전형적인 동적 계획법(Dynamic Programming) 문제들 중 하나이다. 이 문제 역시 점화식을 세워 동적 계획법으로 문제를 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점화식을 세우기 전에 DP 배열부터 정의를 해보자. \(dp[i]\)는 수열 \(A\)에서 \(A[i]\)로 끝나는 감소 부분 수열의 합 중에서 가장 큰 값을 저장하는 배열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음으로 DP 배열을 초기화한다. 각 원소는 자기 자신만을 포함하는 감소 부분 수열이 될 수 있으므로, \(dp[i]\)의 초기값을 \(A[i]\)로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 제일 중요한 점화식을 세우는 과정이다. 감소하는 부분 수열을 만들어야 하므로, \(i\)번째 원소를 기준으로 이전 위치의 원소들 중 \(i\)보다 큰 \(A[j]\)를 찾는다. 만약 \(A[j]\)가 \(A[i]\)보다 크다면, \(A[j]\)로 끝나는 감소 부분 수열의 최대 합에 \(A[i]\)를 더해 새로운 최대 합을 업데이트할 수 있다. 이를 바탕으로 점화식을 세우면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$dp[i] = \max(dp[i], dp[j] + A[i]) \quad \text{(단, } j &amp;lt; i \text{ 그리고 } A[j] &amp;gt; A[i])$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이중 반복문을 사용해 모든 \(i\)에 대해 자신보다 앞선 \(j\)들을 확인하기 때문에 시간 복잡도는 \(\mathcal{O}(n^2)\)이다. 하지만 \(n\)이 크지 않기 때문에 시간제한 내에 충분히 통과할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int n, A[1010], dp[1010];

int main() {
    scanf(&quot;%d&quot;, &amp;amp;n);
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        scanf(&quot;%d&quot;, &amp;amp;A[i]);
    }

    for (int i = 0 ; i &amp;lt; n ; ++i) {
        dp[i] = A[i];

        for (int j = 0 ; j &amp;lt; i ; ++j) {
            if (A[j] &amp;gt; A[i]) {
                dp[i] = max(dp[i], dp[j] + A[i]);
            }
        }
    }
    
    int ans = 0;
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        ans = max(ans, dp[i]);
    }
    
    printf(&quot;%d\n&quot;, ans);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 17216번</category>
      <category>dynamic programming</category>
      <category>동적 계획법</category>
      <category>백준 17216번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/71</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17216%EB%B2%88-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EA%B0%90%EC%86%8C-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4#entry71comment</comments>
      <pubDate>Tue, 10 Sep 2024 01:34:33 +0900</pubDate>
    </item>
    <item>
      <title>HSL 색상 모델이란? - RGB보다 직관적인 색상 모델</title>
      <link>https://www.ohnimdev.com/entry/HSL-%EC%83%89%EC%83%81-%EB%AA%A8%EB%8D%B8%EC%9D%B4%EB%9E%80-RGB%EB%B3%B4%EB%8B%A4-%EC%A7%81%EA%B4%80%EC%A0%81%EC%9D%B8-%EC%83%89%EC%83%81-%EB%AA%A8%EB%8D%B8</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;HSL 색상 모델의 등장 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨간색, 초록색, 파란색 빛을 결합해 색상을 만드는 &lt;a title=&quot;RGB 색상 모델의 위키피디아 페이지&quot; href=&quot;https://en.wikipedia.org/wiki/RGB_color_model&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;RGB 색상 모델&lt;/a&gt;은 컴퓨터 그래픽스와 웹 디자인에서 널리 사용되는 색상 표현 방식이다. RGB 색상 모델은 다양한 색상을 만들 수 있다는 장점이 있지만, 각 색상이 최종 색에 어떤 영향을 미치는지 직관적으로 이해하기 어렵다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 R = 255, G = 0, B = 0의 선명한 빨간색에서 채도를 낮춘 빨간색을 만들고자 한다면, R 값을 64 낮추고, G와 B 값을 각각 64씩 올려야 한다. RGB 값을 직접 조정하는 것은 많은 시행착오가 필요로 하며, 직관적이지 않아 상당히 불편하다. 아래 RGB 팔레트를 이용해 원하는 색상을 만드는 것이 얼마나 어려운 일인지 직접 경험해 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;oNrmLgw&quot; data-pen-title=&quot;Untitled&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/oNrmLgw&quot;&gt; Untitled&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 불편함을 해결하기 위해 컴퓨터 그래픽스 엔지니어들은 색상을 더 직관적으로 조절할 수 있는 HSL(Hue, Saturation, Lightness) 색상 모델을 개발했다. 1978년 8월 &lt;a title=&quot;Alvy Ray Smith의 위키피디아 페이지&quot; href=&quot;https://en.wikipedia.org/wiki/Alvy_Ray_Smith&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span data-token-index=&quot;1&quot;&gt;Alvy Ray Smith&lt;/span&gt;&lt;/a&gt;가 이를 &lt;span data-token-index=&quot;3&quot;&gt;&lt;a title=&quot;Color gamut transform pairs 논문 아카이브 페이지&quot; href=&quot;https://dl.acm.org/doi/10.1145/965139.807361&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Color Gamut Transform Pairs&lt;/a&gt; 논문&lt;/span&gt;에서 이를 공식 발표하였으며, 이후 HSL은 그래픽 소프트웨어, 웹 디자인, 이미지 분석 등 다양한 분야에서 널리 사용되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HSL 색상 모델의 동작 원리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HSL 색상 모델은 Hue(색조), Saturation(채도), Lightness(명도)라는 세 가지 요소로 색상을 정의한다. 이 모델은 RGB 색상 모델에 비해 더 직관적인 방식으로 색상을 조절할 수 있도록 설계되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Hue(색조)&lt;/b&gt;는 색상환(Color Wheel)에서 색을 나타내는 각도로, 0도에서 360도 사이의 값을 가진다. 각도에 따라 색상이 변하며 0도는 빨간색, 120도는 초록색, 240도는 파란색을 나타낸다. 색상환에서 비슷한 각도에 위치한 색은 유사한 색상이며, 반대편에 위치한 색상은 보색 관계에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;hue_v2.webp&quot; data-origin-width=&quot;2788&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VHBKQ/btsJuuRokCq/lk19OQGo51leFJWuc83RT1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VHBKQ/btsJuuRokCq/lk19OQGo51leFJWuc83RT1/img.webp&quot; data-alt=&quot;Hue(색조) 값에 따른 색상 변화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VHBKQ/btsJuuRokCq/lk19OQGo51leFJWuc83RT1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVHBKQ%2FbtsJuuRokCq%2Flk19OQGo51leFJWuc83RT1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Hue(색조) 값에 따른 색상 변화&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;95&quot; data-filename=&quot;hue_v2.webp&quot; data-origin-width=&quot;2788&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Hue(색조) 값에 따른 색상 변화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Saturation(채도)&lt;/b&gt;는 색의 순도를 나타낸다. 채도는 색상에 백색광이 얼마나 혼합되었는지에 따라 달라지며 0% 일 때는 회색(무채색)이 되고, 100% 일 때는 가장 선명한 색상이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;saturation_v2.webp&quot; data-origin-width=&quot;2906&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q32Ql/btsJvYDJ11L/JkSdV94kKaFgiXeTSRKGTK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q32Ql/btsJvYDJ11L/JkSdV94kKaFgiXeTSRKGTK/img.webp&quot; data-alt=&quot;Saturation(채도) 값에 따른 색상 변화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q32Ql/btsJvYDJ11L/JkSdV94kKaFgiXeTSRKGTK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq32Ql%2FbtsJvYDJ11L%2FJkSdV94kKaFgiXeTSRKGTK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Saturation(채도) 값에 따른 색상 변화&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;97&quot; data-filename=&quot;saturation_v2.webp&quot; data-origin-width=&quot;2906&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Saturation(채도) 값에 따른 색상 변화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Lightness(명도)&lt;/b&gt;는 색상의 밝기를 조절하는 요소로 0%는 완전한 검정색, 100%는 완전한 흰색을 의미한다. 50%의 명도는 중간 밝기를 나타내며, 본래의 색을 가장 자연스럽게 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;lightness_v2.webp&quot; data-origin-width=&quot;2864&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8clyu/btsJvaxRZIa/ANBbHtQwSpQdLS1pjyKRM1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8clyu/btsJvaxRZIa/ANBbHtQwSpQdLS1pjyKRM1/img.webp&quot; data-alt=&quot;Lightness(명도) 값에 따른 색상 변화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8clyu/btsJvaxRZIa/ANBbHtQwSpQdLS1pjyKRM1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8clyu%2FbtsJvaxRZIa%2FANBbHtQwSpQdLS1pjyKRM1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Lightness(명도) 값에 따른 색상 변화&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;101&quot; data-filename=&quot;lightness_v2.webp&quot; data-origin-width=&quot;2864&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Lightness(명도) 값에 따른 색상 변화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HSL 색상 모델이 사용되는 곳&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HSL 색상 모델은 우리가 사용하는 다양한 소프트웨어에서 널리 활용된다. 그중 가장 대표적인 예는 컬러 팔레트이다. 그래픽 스프트웨어나 웹 브라우저 개발 도구에서 사용하는 컬러 팔레트는 주로 HSL 색상 모델을 기반으로 구현되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷-2024-09-09-오전-4.47.02 (1).webp&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9UKsG/btsJt4yTvgX/LSt0xGdZujEcPexXk5mFDk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9UKsG/btsJt4yTvgX/LSt0xGdZujEcPexXk5mFDk/img.webp&quot; data-alt=&quot;HSL 색상 모델을 기반으로 만들어진 팔레트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9UKsG/btsJt4yTvgX/LSt0xGdZujEcPexXk5mFDk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9UKsG%2FbtsJt4yTvgX%2FLSt0xGdZujEcPexXk5mFDk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;HSL 색상 모델을 기반으로 만들어진 팔레트&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;330&quot; data-filename=&quot;스크린샷-2024-09-09-오전-4.47.02 (1).webp&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;948&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HSL 색상 모델을 기반으로 만들어진 팔레트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 색상환에서 원하는 색조를 선택한 후, 채도와 명도를 슬라이더를 이용해 직관적으로 조절하여 다양한 색상을 만들 수 있다. 이러한 직관적인 인터페이스 덕분에 HSL 색상 팔레트는 개발자와 디자이너에게 매우 유용한 도구로 자리매김했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이미지 분석에서도 HSL 색상 모델은 널리 사용된다. 예를 들어 이미지의 특정 색상 범위를 분석하거나, 밝기나 채도를 기준으로 필터링할 때 HSL 모델은 강력한 분석 도구로 활용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HSL 색상 모델의 한계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HSL 색상 모델은 색상을 쉽게 선택하고 조절할 수 있는 장점이 있지만, 색상이 실제로 보이는 것과 다르게 표현될 수 있는 한계가 있다. HSL은 색상을 색조, 채도, 명도로 나누어 표현하지만, 사람이 실제로 색을 인식하는 방식과는 약간의 차이가 있다. 그래서 빨간색과 파란색을 같은 명도와 채도로 설정하더라도 파란색이 빨간색보다 더 어둡다고 느낄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 한계를 극복하기 위해 더 정밀한 색상 모델들이 개발되었다. 대표적으로 인간의 시각을 기반으로 설계된 CIELAB이 있다. CIELAB 모델은 사람이 실제로 색을 어떻게 보는지를 중점적으로 고려한 색상 모델이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 과학과 기술의 발전에 따라 색상 간의 차이를 더 정확하게 반영하고 다양한 환경에서 색상 왜곡을 줄이기 위한 연구도 지속적으로 이루어지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HSL 색상 모델은 직관적인 인터페이스 덕분에 색상을 빠르고 쉽게 조정할 수 있는 색상 모델이다. 하지만 사람이 색을 인식하는 방식과 다소 차이가 있을 수 있다는 점도 고려해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 웹 개발에서는 HSL 색상 모델을 사용해도 큰 문제가 없지만, 정밀한 색상 조절이 필요한 작업에는 다른 색상 모델을 고려하는 것이 더 적합할 수 있다. 각 색상 모델은 사용하는 환경과 목적에 따라 다르게 동작하므로, 작업의 요구 사항에 맞는 모델을 선택하는 것이 중요하다.&lt;/p&gt;</description>
      <category>개발로그/HTML, CSS</category>
      <category>hsl</category>
      <category>hsl 색상 모델</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/70</guid>
      <comments>https://www.ohnimdev.com/entry/HSL-%EC%83%89%EC%83%81-%EB%AA%A8%EB%8D%B8%EC%9D%B4%EB%9E%80-RGB%EB%B3%B4%EB%8B%A4-%EC%A7%81%EA%B4%80%EC%A0%81%EC%9D%B8-%EC%83%89%EC%83%81-%EB%AA%A8%EB%8D%B8#entry70comment</comments>
      <pubDate>Mon, 9 Sep 2024 18:35:47 +0900</pubDate>
    </item>
    <item>
      <title>[백준 22742번] Make Friendships</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-22742%EB%B2%88-Make-Friendships</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/22742&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/22742&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이팍이는 파티에서 여러 여자친구를 새롭게 사귀었고, 이들과 데이트 일정을 잡으려고 한다. 이삭이는 한 번에 한 명의 여자친구만 만날 수 있으며, 만날 수 있는 시간은 정해져 있다. 또한 각 여자친구마다 이삭이를 만날 수 있는 시간이 정해져 있다. 이 때 이삭이가 최적의 일정으로 스케줄링을 했을 때 최대한 만날 수 있는 여자친구들의 수를 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이삭이가 사용할 수 있는 시간들을 왼쪽 노드로, 각 여자친구들을 오른쪽 노드로 설정하면 문제를 이분 그래프로 모델링 할 수 있다. 아래 그림은 예제 입력을 이분 그래프로 모델링 한 결과이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;boj_22742_bipartite_graph_compressed.jpg&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;2100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxyHDr/btsJpIgiaLp/HAisANz6AP0TQ1lySWKH20/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxyHDr/btsJpIgiaLp/HAisANz6AP0TQ1lySWKH20/img.jpg&quot; data-alt=&quot;이분 그래프로 모델링 한 예제입력&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxyHDr/btsJpIgiaLp/HAisANz6AP0TQ1lySWKH20/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxyHDr%2FbtsJpIgiaLp%2FHAisANz6AP0TQ1lySWKH20%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;이분 그래프로 모델링 한 예제입력&quot; loading=&quot;lazy&quot; width=&quot;360&quot; height=&quot;360&quot; data-filename=&quot;boj_22742_bipartite_graph_compressed.jpg&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;2100&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이분 그래프로 모델링 한 예제입력&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 문제는 이분 그래프에서 최대 매칭을 구하는 문제로 바뀌었고, 이분 매칭 알고리즘을 이용해 문제를 풀 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프를 구현할 때에는 이삭이가 가능한 시간들에 Map과 같은 자료구조를 이용해서 인덱스를 기록해 놓는다. 그런 다음 여자친구들이 이삭이를 만날 수 있는 시간들이 Map에 있는지 확인을 한 뒤, 해당 시간이 Map에 존재하면 간선을 연결하여 이분 그래프를 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;map&amp;gt;

using namespace std;

vector&amp;lt;int&amp;gt; matched;
vector&amp;lt;bool&amp;gt; visited;
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; node;

// n: 왼쪽 노드의 수
void init(int n) {
    matched.assign(n, -1);;
    visited.assign(n, false);
    node.assign(n, vector&amp;lt;int&amp;gt;());
}

// DFS를 이용해 최대 매칭을 찾는 함수
bool dfs(int here) {
    if (visited[here]) {
        return false;
    }
    
    visited[here] = true;
    
    for (int there: node[here]) {
        if (matched[there] == -1 || dfs(matched[there])) {
            matched[there] = here;
            
            return true;
        }
    }
    
    return false;
}

int main() {
    int m;
    
    // 오른쪽 노드의 수를 입력 받음(이삭이가 사귄 여자친구들의 수)
    while (scanf(&quot;%d&quot;, &amp;amp;m) &amp;amp;&amp;amp; m) {
        int n, t, x;
        
        // 시간과 왼쪽 노드 인덱스를 매핑하는 해시맵
        map&amp;lt;int, int&amp;gt; hash;
        
        // 왼쪽 노드의 수를 입력 받음(이삭이가 가능한 시간)
        scanf(&quot;%d&quot;, &amp;amp;n);
        for (int i = 0 ; i &amp;lt; n ; ++i) {
            scanf(&quot;%d&quot;, &amp;amp;x);
            hash[x] = i;
        }
        
        init(n);
        
        for (int i = 0 ; i &amp;lt; m ; ++i) {
            scanf(&quot;%d&quot;, &amp;amp;t);
            
            while (t--) {
                scanf(&quot;%d&quot;, &amp;amp;x);
                
                // 입력된 시간이 해시맵에 존재하면
                if (hash.count(x)) {
                    // 왼쪽 노드에 오른쪽 노드 번호를 연결
                    node[hash[x]].push_back(i);
                }
            }
        }
        
        int ans = 0;
        
        for (int i = 0 ; i &amp;lt; n ; ++i) {
            visited.assign(n, false);
            
            if (dfs(i)) {
                ++ans;
            }
        }
        
        printf(&quot;%d\n&quot;, ans);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>Bipartite Graph</category>
      <category>bipartite matching</category>
      <category>boj 22742번</category>
      <category>백준 22742번</category>
      <category>이분 그래프</category>
      <category>이분 매칭</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/69</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-22742%EB%B2%88-Make-Friendships#entry69comment</comments>
      <pubDate>Tue, 3 Sep 2024 01:34:59 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript에서 배열의 원소들을 그룹핑하는 방법들</title>
      <link>https://www.ohnimdev.com/entry/JavaScript%EC%97%90%EC%84%9C-%EB%B0%B0%EC%97%B4%EC%9D%98-%EC%9B%90%EC%86%8C%EB%93%A4%EC%9D%84-%EA%B7%B8%EB%A3%B9%ED%95%91%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;그룹핑의 필요성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 애플리케이션을 개발하다 보면 데이터를 특정 기준에 따라 그룹화해야 하는 상황이 자주 발생한다. 예를 들어 게시판에서 사용자들이 작성한 글을 카테고리별로 묶어서 보여주거나, 사용자 로그를 시간대별로 분류해서 보여줘야 하는 경우가 있다. 이 글에서는 JavaScript에서 배열의 원소들을 그룹핑하는 다양한 방법들을 소개한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;방법 1. Lodash의 groupBy 함수 이용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript에서 널리 사용되는 라이브러리인 Lodash는 배열의 원소들을 그룹화할 수 있는 groupBy 함수를 제공한다. 이 함수는 데이터를 특정 기준에 따라 손쉽게 그룹핑할 수 있는 직관적인 인터페이스를 제공한다.&lt;/p&gt;
&lt;pre id=&quot;code_1725188268845&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 인터페이스
_.groupBy(데이터, 함수 혹은 프로퍼티키)

// 방법 1)
_.groupBy(posts, (posts) =&amp;gt; posts.category);

// 방법 2)
_.groupBy(posts, &quot;category&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 Lodash를 이용해서 배열의 원소들을 그룹핑하는 예제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;js,result&quot; data-slug-hash=&quot;OJeoqvR&quot; data-pen-title=&quot;Lodash를 이용해서 배열의 원소들을 그룹핑하기&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/OJeoqvR&quot;&gt; Lodash를 이용해서 배열의 원소들을 그룹핑하기&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lodash의 groupBy 함수는 데이터를 쉽게 그룹핑 할 수 있게 해 준다. 그러나 외부 라이브러리 사용이 제한되거나, 굳이 외부 라이브러리를 도입하고 싶지 않다면 JavaScript에서 제공하는 기본 메서드들을 활용해 직접 그룹핑 기능을 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;방법 2. 배열의 reduce 메서드 이용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lodash와 같은 외부 러이브러리르 사용하지 않고도 JavaScript에서 기본으로 제공하는 메서드인 reduce를 활용하면 배열의 원소들을 그룹핑할 수 있다. 이 방법은 외부 라이브러리의 의존성을 줄이면서도, 순수 JavaScript만으로 데이터를 그룹화할 수 있다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;js,result&quot; data-slug-hash=&quot;XWLPGPx&quot; data-pen-title=&quot;reduce 메서드를 이용해서 배열의 원소들을 그룹핑하기&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/XWLPGPx&quot;&gt; reduce 메서드를 이용해서 배열의 원소들을 그룹핑하기&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;방법 3. Object.groupBy 메서드 이용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript의 최신 표준인 ECMAScript 2023에서는 Object.groupBy라는 메서드가 새롭게 추가됐다. 이 메서드는 Lodash의 groupBy와 유사한 인터페이스를 제공하며, 데이터를 특정 기준에 따라 그룹핑할 수 있게 해 준다. reduce와 마찬가지로 Object.groupBy는 JavaScript 표준에 포함된 기능이기 때문에 외부 라이브러리에 의존하지 않고 사용할 수 있는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;js,result&quot; data-slug-hash=&quot;poXOYxa&quot; data-pen-title=&quot;Object.groupBy 메서드를 이용해서 배열의 원소들을 그룹핑하기&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/poXOYxa&quot;&gt; Object.groupBy 메서드를 이용해서 배열의 원소들을 그룹핑하기&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 이 메서드는 비교적 최신 스펙에 속하기 때문에 지원하지 않는 브라우저들이 있을 수 있다. 따라서, 프로젝트에서 이 메서드를 사용하기 전에 프로젝트의 타겟타깃 브라우저들이 Object.groupBy를 지원하는지 반드시 확인해야 한다. 만약 지원하지 않는 타깃 브라우저가 있다면, core-js와 같은 폴리필을 적용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;방법 4. Map.groupBy 메서드 이용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript의 최신 표준인 ECMAScript 2023에서는 Map.groupBy라는 메서드도 새롭게 추가됐다. 이 메서드는 Object.groupBy와 비슷한 방식으로 동작하지만, 그룹핑된 데이터를 Map 객체로 반환한다는 점에서 차이가 있다. Map은 다양한 편의 메서드를 제공하므로 그룹화된 데이터를 더욱 효율적으로 관리할 수 있다. 특히 Symbol이나 String 타입만 키로 사용할 수 있는 Object와는 다르게 클래스나 Object도 키로 사용할 수 있어, 복잡한 데이터를 다룰 때 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;js,result&quot; data-slug-hash=&quot;XWLPGxy&quot; data-pen-title=&quot;Map.groupBy 메서드를 이용해서 배열의 원소들을 그룹핑하기&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/XWLPGxy&quot;&gt; Map.groupBy 메서드를 이용해서 배열의 원소들을 그룹핑하기&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Map.groupBy도 Object.groupBy와 마찬가지로 비교적 최신 스펙이기 때문에 지원하는 브라우저를 확인한 뒤, 필요하다면 폴리필을 적용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 JavaScript에서 배열의 원소들을 그룹핑하는 다양한 방법을 살펴보앗다. Lodash와 같은 외부 라이브러리를 사용하면 손쉽게 데이터를 그룹핑할 수 있지만, JavaScript에서 기본적으로 제공하는 메서드들을 활용해서 데이터를 그룹핑하는 기능을 직접 구현할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 ECMAScript 2023에 새롭게 추가된 Object.groupBy와 Map.groupBy 메서드는 Lodash와 비슷한 인터페이스를 제공하며, 표준 기능만드로도 효율적으로 데이터를 그룹화할 수 있는 방법을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꼭 어떤 방법을 사용해야 한다고 정해진 것이 아니므로, 프로젝트의 요구사항에 따라 적합한 그룹핑 방법을 선택하면 될 것 같다.&lt;/p&gt;</description>
      <category>개발로그/JavaScript, TypeScript</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/68</guid>
      <comments>https://www.ohnimdev.com/entry/JavaScript%EC%97%90%EC%84%9C-%EB%B0%B0%EC%97%B4%EC%9D%98-%EC%9B%90%EC%86%8C%EB%93%A4%EC%9D%84-%EA%B7%B8%EB%A3%B9%ED%95%91%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EB%93%A4#entry68comment</comments>
      <pubDate>Sun, 1 Sep 2024 20:27:57 +0900</pubDate>
    </item>
    <item>
      <title>React에서 useState의 인자로 함수 반환값 대신 함수를 넘겨준다면?</title>
      <link>https://www.ohnimdev.com/entry/React%EC%97%90%EC%84%9C-useState%EC%9D%98-%EC%9D%B8%EC%9E%90%EB%A1%9C-%ED%95%A8%EC%88%98-%EB%B0%98%ED%99%98%EA%B0%92-%EB%8C%80%EC%8B%A0-%ED%95%A8%EC%88%98%EB%A5%BC-%EB%84%98%EA%B2%A8%EC%A4%80%EB%8B%A4%EB%A9%B4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;React에서 useState의 인자로 함수 반환값을 사용할 때 생길 수 있는 퍼포먼스 이슈&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 useState 훅을 사용하여 상태를 관리할 때, 대부분은 단순한 primitive 값을 초기값으로 설정한다. 그러나 때로는 복잡한 연산이 필요한 경우도 있다. 예를 들어, 100개의 랜덤 한 Todo 아이템을 초기값으로 설정해야 한다고 가정해 보자. 일반적으로는 다음과 같은 방식으로 이를 해결한다.&lt;/p&gt;
&lt;pre id=&quot;code_1724691146120&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function createInitialTodoList() {
  const ret = [];
  
  for (let i = 0 ; i &amp;lt; 100 ; ++i) {
    ret.push({
      id: crypto.randomUUID(),
      text: `todo Item #${i + 1}`,
    });
  }
  
  return ret;
}

function TotoList() {
  const [todoList, setTodoList] = useState(createInitialTodoList());
  
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 이 코드를 보면 아무런 문제가 없어 보일 수 있다. 초기 렌더링 시, createInitialTodoList 함수가 실행되어 Todo 리스트를 생성하고 이를 useState 훅의 초기값으로 설정하기 때문에, 기능적으로 잘 동작하는 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이 접근 방식은 심각한 퍼포먼스 이슈를 일으킬 수 있다. 왜 그럴까? 바로 TodoList 컴포넌트가 다시 렌더링될 때마다 createInitialTodoList 함수가 불필요하게 호출되기 때문이다. 이 함수는 컴포넌트가 마운트 될 때만 실행되어야 하지만, 매번 리렌더링 될 때마다 호출되어 비효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 불필요한 연산이 반복되면, 특히 createInitialTodoList 함수가 고비용 연산을 포함하고 있을 경우, 심각한 퍼포먼스 저하로 이어질 수 있다. 결국, 앱의 응답 속도가 느려지고, 전반적인 사용자 경험이 저하될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 createInitialTodoList 함수에 고비용 연산이 포함된 경우, 얼마나 퍼포먼스가 저하될 수 있는지를 보여주는 예제이다. input 창에 텍스트를 쳐보면 눈에 띄게 반응 속도가 느린 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 376.43359375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;376.43359375&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;KKjevXK&quot; data-pen-title=&quot;Untitled&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/KKjevXK&quot;&gt; Untitled&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제, 이러한 문제를 React에서 어떻게 해결할 수 있는지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;React에서 useState의 인자로 함수 자체를 넘겨주자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 설명한 문제가 복잡해 보일 수 있지만, 해결 방법은 의외로 간단하다. 불필요한 함수 호출을 방지하려면 useState의 인자로 함수의 반환값이 아닌, 함수 그 자체를 넘겨주기만 하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1724691226269&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function createInitialTodoList() {
  // ...
}

function TotoList() {
  const [todoList, setTodoList] = useState(createInitialTodoList);
  
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 useState의 인자로 createInitialTodoList 함수를 넘겨주면, React는 이 함수를 초기 렌더링 시 딱 한 번만 호출한다. 따라서 useState의 인자로 넘겨주는 함수 안에 고비용 연산이 포함되어 있더라도, 초기 렌더링 시에만 그 비용이 발생한다. 이후에는 이 함수가 다시 호출되지 않으므로, 렌더링 성능이 크게 개선된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 이 방법을 적용한 예제이다. 간단한 수정이 얼마나 큰 퍼포먼스 개선을 가져올 수 있는지 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;텍스트 입력을 해 보면서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;테스트 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;abgKyGw&quot; data-pen-title=&quot;React에서 useState의 인자로 함수 반환값을 사용할 때 생길 수 있는 퍼포먼스 이슈&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/abgKyGw&quot;&gt; React에서 useState의 인자로 함수 반환값을 사용할 때 생길 수 있는 퍼포먼스 이슈&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;React에서 useState의 인자로 넘겨줄 함수에 파라미터를 받고 싶을 때&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서 작업을 하다 보면 useState의 인자로 넘겨줄 함수를 단독으로 사용하는 것보다, 외부 파라미터에 의존하는 함수가 필요할 때가 많다. 이럴 때는 함수를 반환하는 함수를 사용해 문제를 해결할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1724691264356&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function todoListInitializerGenerator(todos) {
  return function() {
    return todos.map((todo) =&amp;gt; ({
      id: todo.id,
      text: `${todo.text} (${todo.completed ? 'V' : ' '})`,
    }));
  }
}

function TotoList(props) {
  const [todoList, setTodoList] = useState(todoListInitializerGenerator(props.todos));
  
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서는 todoListInitializerGenerator라는 함수가 외부 파라미터 todos에 의존하여 내부적으로 초기 상태를 반환하는 함수를 생성한다. 이렇게 생성된 함수는 useState에 인자로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 문제를 제기할 때 설명한 것처럼, todoListInitializerGenerator 함수는 컴포넌트가 다시 렌더링 될 때마다 실행된다. 하지만, 이 함수는 단지 새로운 함수를 반환할 뿐, 실제 초기값을 만드는 함수는 오직 한 번만 실행된다. 이를 통해 외부 파라미터에 의존하면서도, 고비용 연산이 포함된 초기화 함수가 컴포넌트가 리렌더링 될 때마다 불필요하게 실행되는 것을 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 이 방법을 간단히 테스트해볼 수 있는 예제 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;abgKyjy&quot; data-pen-title=&quot;React에서 useState의 인자로 넘겨주는 함수가 외부 파라미터에 의존하는 예제&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/abgKyjy&quot;&gt; React에서 useState의 인자로 넘겨주는 함수가 외부 파라미터에 의존하는 예제&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 useState를 사용할 때, 함수의 반환값 대신 함수 자체를 인자로 넘겨주는 것만으로도 성능 저하를 방지할 수 있다. 이를 통해 복잡한 초기 상태를 효율적으로 설정하면서도, 불필요한 연산을 최소화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가는 이 글을 읽고, 애플리케이션 성능을 높이는데 도움이 되길 바란다.&lt;/p&gt;</description>
      <category>개발로그/JavaScript, TypeScript</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/67</guid>
      <comments>https://www.ohnimdev.com/entry/React%EC%97%90%EC%84%9C-useState%EC%9D%98-%EC%9D%B8%EC%9E%90%EB%A1%9C-%ED%95%A8%EC%88%98-%EB%B0%98%ED%99%98%EA%B0%92-%EB%8C%80%EC%8B%A0-%ED%95%A8%EC%88%98%EB%A5%BC-%EB%84%98%EA%B2%A8%EC%A4%80%EB%8B%A4%EB%A9%B4#entry67comment</comments>
      <pubDate>Tue, 27 Aug 2024 02:41:27 +0900</pubDate>
    </item>
    <item>
      <title>React에서 비동기 작업을 할 때 Race Condition을 방지하는 효과적인 해결 방법들</title>
      <link>https://www.ohnimdev.com/entry/React%EC%97%90%EC%84%9C-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9E%91%EC%97%85%EC%9D%84-%ED%95%A0-%EB%95%8C-Race-Condition%EC%9D%84-%EB%B0%A9%EC%A7%80%ED%95%98%EB%8A%94-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Race Condition이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Race Condition이란 두 개 이상의 비동기 작업이 예상치 못한 순서로 실행되면서 의도하지 않은 결과를 만들어내는 상황을 의미한다. 보통 백엔드에서 멀티스레드 프로그래밍을 할 때 Race Condition을 주로 이야기하지만, React에서도 비동기 API 호출을 여러 번 할 때 이러한 상황이 발생할 수 있다. 그렇다면 구체적으로 어떤 상황에서 Race Condition이 발생하고, 이를 해결하기 위한 효과적인 해결 방법들을 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;React에서 발생할 수 있는 Race Condition&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 흔히 발생할 수 있는 Race Condition의 예로, 유저의 id를 props로 받아 해당 유저의 데이터를 fetch해 보여주는 컴포넌트를 생각해 볼 수 있다. 이러한 컴포넌트에서는 보통 useEffect 훅을 사용해 유저의 id를 의존성 배열에 넣고, id가 변경될 때마다 데이터를 fetch 해 온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 props로 넘겨 받은 유저의 id가 빠르게 바뀌면, 이전 요청이 완료되기 전에 새로운 요청이 실행되면서 Race Condition이 발생할 수 있다. 비동기 작업은 요청한 순서대로 완료되지 않을 수 있기 때문에, 가장 마지막으로 실행된 effect 보다 이전에 실행된 effect가 더 늦게 완료될 수 있다. 이 경우, 최신 props.id가 아닌 이전 props.id에 대한 잘못된 유저 데이터가 화면에 표시될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 위 상황을 간단하게 재현을 한 예제이다. 네트워크 지연 상황을 재현하기 위해 의도적으로 비동기 작업 내에 지연 코드를 포함시켰다. 빠르게 유저들의 버튼을 클릭하다 보면 마지막으로 클릭한 유저의 정보가 보이지 않는 경우가 발생하는 것을 볼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1724604799645&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const UserProfile = (props) =&amp;gt; {
  const [user, setUser] = useState(null);
  
  useEffect(() =&amp;gt; {
    const fetchUser = async () =&amp;gt; {
      const res = await fetch(`https://dummyjson.com/users/${props.id}`);
      await sleep(Math.random() * 3000);
      const json = await res.json();
      setUser(json);
    }
    
    fetchUser();
  }, [props.id]);
  
  if (!user) {
    return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;;
  }
  
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;{`props로 받은 유저 아이디: ${props.id}`}&amp;lt;/p&amp;gt;
      &amp;lt;h1&amp;gt;{`아이디: ${user.id}`}&amp;lt;/h1&amp;gt;
      &amp;lt;h2&amp;gt;{`이름: ${user.firstName} ${user.lastName}`}&amp;lt;/h2&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 356.06640625px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;356.06640625&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;RwzyXYE&quot; data-pen-title=&quot;Race Condition이 발생할 수 있는 user fetch&quot; data-user=&quot;ohnim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ohnim/pen/RwzyXYE&quot;&gt; Race Condition이 발생할 수 있는 user fetch&lt;/a&gt; by Ohnim (&lt;a href=&quot;https://codepen.io/ohnim&quot;&gt;@ohnim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;현재 effect가 가장 최신 effect인지 확인할 수 있는 플래그를 이용하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 Race Condition을 해결하기 위한 한 가지 방법은, useEffect 내부에서 현재 실행 중인 effect가 최신 effect인지 확인할 수 있는 boolean 플래그를 사용하는 것이다. 먼저, effect가 실행될 때 isCurrent 변수를 true로 초기화한다. 그리고 useEffect의 clean-up 함수에서 isCurrent 변수를 false로 만들어준다. 이제 이 플래그를 사용하면 현재 effect가 최신 effect인지 추적할 수 있다. 이후 fetch 함수 내에서 isCurrent가 true일 때만 유저 데이터를 업데이트하면, 유저는 항상 최신 props.id에 해당하는 유저 데이터가 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 useEffect가 실행될 때마다 매번 새로운 실행 컨텍스트가 생성되어, 완료 대기 중인 과거의 effect의 isCurrent는 false가 되고, 항상 최신의 effect에서만 isCurrent 변수가 true로 유지된다는 원리를 활용하는 방법이다.&lt;/p&gt;
&lt;pre id=&quot;code_1724605487084&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const UserProfile = (props) =&amp;gt; {
  const [user, setUser] = useState(null);

  useEffect(() =&amp;gt; {
    let isCurrent = true;

    const fetchUser = async () =&amp;gt; {
      const res = await fetch(`https://dummyjson.com/users/${props.id}`);
      await sleep(Math.random() * 3000);
      const json = await res.json();

      if (isCurrent) {
        setUser(json);
      }
    };

    fetchUser();

    return () =&amp;gt; {
      isCurrent = false;
    };
  }, [props.id]);

  if (!user) {
    return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;;
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;{`props로 받은 유저 아이디: ${props.id}`}&amp;lt;/p&amp;gt;
      &amp;lt;h1&amp;gt;{`아이디: ${user.id}`}&amp;lt;/h1&amp;gt;
      &amp;lt;h2&amp;gt;{`이름: ${user.firstName} ${user.lastName}`}&amp;lt;/h2&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;boolean 플래그를 이용해 Race Condition을 해결하는 예제는 &lt;a title=&quot;useEffect 내에서 boolean 플래그를 이용해 Race Condition 해결하는 방법&quot; href=&quot;https://codepen.io/ohnim/pen/NWZzKJv&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기에서&lt;/a&gt; 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;state에 비동기 요청 시간을 추가하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Race Condition을 해결하는 또 다른 방법은, state에 비동기 요청의 시간을 추가하여 현재 state가 언제 요청된 데이터인지 확인하는 것이다. 구체적으로, 비동기 요청을 보낼 때 그 시점의 타임스탬프를 기록한다. 이후, 비동기 작업이 완료되면 state를 업데이트하기 전에, 해당 요청의 타임스탬프와 현재 state에 저장된 타임스탬프를 비교한다. 만약 state에 저장된 타임스탬프가 해당 요청의 타임스탬프보다 더 최신이라면, 해당 요청의 데이터는 state에 저장하지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1724605817160&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const UserProfile = (props) =&amp;gt; {
  const [{ user }, setData] = useState({
    user: null,
    requestedAt: 0
  });

  useEffect(() =&amp;gt; {
    const fetchUser = async () =&amp;gt; {
      const now = Date.now();
      const res = await fetch(`https://dummyjson.com/users/${props.id}`);
      await sleep(Math.random() * 3000);
      const json = await res.json();

      setData((prev) =&amp;gt; {
        if (prev.requestedAt &amp;gt; now) {
          return prev;
        }

        return {
          user: json,
          requestedAt: now
        };
      });
    };

    fetchUser();
  }, [props.id]);

  if (!user) {
    return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;;
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;{`props로 받은 유저 아이디: ${props.id}`}&amp;lt;/p&amp;gt;
      &amp;lt;h1&amp;gt;{`아이디: ${user.id}`}&amp;lt;/h1&amp;gt;
      &amp;lt;h2&amp;gt;{`이름: ${user.firstName} ${user.lastName}`}&amp;lt;/h2&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;state에 타임스탬프를 같이 저장해 Race Condition을 해결하는 예제는 &lt;a title=&quot;state에 타임스탬프를 같이 저장해 Race Condition을 해결하는 방법&quot; href=&quot;https://codepen.io/ohnim/pen/mdZKbYE&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기에서&lt;/a&gt; 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;비동기 요청을 직접 중단하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 요청을 직접 중단할 수 있다면, 이를 활용하는 것 또한 Race Condition을 해결하는 효과적인 방법이 될 수 있다. React 프로젝트에서 대부분의 비동기 호출은 fetch나 axios를 통해 이루어지는데, 이 경우 AbortController를 사용하여 비동기 요청을 중단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법을 구현하기 위해서는, useEffect 내부에서 AbortController를 생성한 뒤, 이를 fetch나 axios 호출에 취소 신호로 전달한다. 이후, useEffect의 clean-up 함수에서 AbortController를 실행해 진행 중인 비동기 요청을 중단시킬 수 있다. 이렇게 하면, 새로운 effect가 실행돼 새로운 비동기 요청이 발생할 때, 이전에 실행 중이던 비동기 요청은 자동으로 중단되어 항상 최신 데이터만 보여줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 사용하는 비동기 요청 라이브러리에 따라 요청을 중단시킬 경우 에러가 발생할 수 있으므로, then이나 catch를 이용해 비동기 요청이 AbortController에 의해 중단된 것인지 확인하는 추가적인 코드를 작성하는 것이 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1724606131329&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const UserProfile = (props) =&amp;gt; {
  const [user, setUser] = useState(null);

  useEffect(() =&amp;gt; {
    const controller = new AbortController();

    const fetchUser = async () =&amp;gt; {
      const res = await fetch(`https://dummyjson.com/users/${props.id}`, {
        signal: controller.signal
      });
      await sleep(Math.random() * 3000);
      const json = await res.json();
      setUser(json);
    };

    fetchUser();

    return () =&amp;gt; {
      controller.abort();
    };
  }, [props.id]);

  if (!user) {
    return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;;
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;{`props로 받은 유저 아이디: ${props.id}`}&amp;lt;/p&amp;gt;
      &amp;lt;h1&amp;gt;{`아이디: ${user.id}`}&amp;lt;/h1&amp;gt;
      &amp;lt;h2&amp;gt;{`이름: ${user.firstName} ${user.lastName}`}&amp;lt;/h2&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AbortController를 이용해 Race Condition을 해결하는 예제는 &lt;a title=&quot;AbortController를 이용해 Race Condition을 해결하는 방법&quot; href=&quot;https://codepen.io/ohnim/pen/abgKogx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기에서&lt;/a&gt; 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상태 관리 라이브러리를 이용하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로, SWR이나 React Query와 같은 서버 상태 관리 라이브러리를 사용하는 방법이 있다. 이러한 라이브러리는 서버 상태를 효과적으로 관리할 수 있게 해주며, 특히 비동기 요청을 안정적으로 처리하도록 도와준다. 예를 들어, 이 라이브러리들은 쿼리 key마다 응답 상태를 독립적으로 관리하며, 항상 최신 요청의 응답만을 데이터에 저장한다. 따라서 props.id를 쿼리 key로 설정하면, Race Condition에 대한 우려를 크게 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이 글에서는 이러한 라이브러리를 사용하는 것 보다는 Race Condition이 발생하는 근본적인 이유와 이를 해결할 수 있는 기본 원리 및 방법을 이해하는 데 중점을 두고 있다. 따라서 이번 글에서는 SWR, React Query와 같은 라이브러리 사용 방법에 대한 자세한 설명은 생략하고, 추후 다른 글에서 다룰 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; React 프로젝트에서 비동기 작업을 할 때 SWR이나 React Query와 같은 라이브러리를 사용하기 때문에, Race Condition은 자주 마주하는 문제가 아니다. 하지만 자주 발생하지 않는 문제이기 때문에, 한 번 마주치게 되면 원인 파악과 해결 방법에 많은 시간을 사용하게 될 수 있다. 이 글이 앞으로 React 프로젝트에서 Race Condition 문제를 해결하는 데 도움이 되길 바라며, 이만 글을 마친다.&lt;/p&gt;</description>
      <category>개발로그/JavaScript, TypeScript</category>
      <category>Race condition</category>
      <category>react</category>
      <category>useEffect</category>
      <category>비동기 데이터 처리</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/66</guid>
      <comments>https://www.ohnimdev.com/entry/React%EC%97%90%EC%84%9C-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9E%91%EC%97%85%EC%9D%84-%ED%95%A0-%EB%95%8C-Race-Condition%EC%9D%84-%EB%B0%A9%EC%A7%80%ED%95%98%EB%8A%94-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95%EB%93%A4#entry66comment</comments>
      <pubDate>Mon, 26 Aug 2024 02:55:34 +0900</pubDate>
    </item>
    <item>
      <title>[백준 3632번] Drying</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-3632%EB%B2%88-Drying</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3632&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/3632&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(N\)개의 빨래와 각각의 빨래가 가지고 있는 초기 물의 양이 주어진다. 모든 빨래는 1분마다 물의 양이 1씩 줄어들며, 매 분마다 한 개의 빨래를 선택해 라디에이터에 올릴 수 있다. 라디에이터에 올린 빨래는 물의 양이 1분마다 \(K\)씩 줄어든다. 모든 빨래의 물의 양이 0이 되는 최소한의 시간을 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 \(X\)분 내에 모든 빨래를 말릴 수 있는 방법이 존재한다면, \(X\)분보다 더 큰 시간 내에서도 모든 빨래를 말릴 수 있다. 반대로 \(X\)분 내에 모든 빨래를 말릴 수 없다면, \(X\)분 보다 더 작은 시간 내에 모든 빨래를 말릴 방법은 존재하지 않는다. 이 특성을 이용해 최적화 문제를 결정 문제로 바꿀 수 있고, 이분 탐색을 이용해 답을 빠르게 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 \(X\)분 내에 모든 빨래를 말릴 수 있는지 확인하는 방법을 알아보자. 모든 빨래를 \(X\)분 내로 말리기 위해서는 라디에이터를 최선의 방법으로 사용해야 한다. 빨래 \(i\)가 가지고 있는 물의 양을 \(A[i]\)라고 하자. 만약 \(A[i]\)가 \(X\)보다 작거나 같다면, 이 빨래는 라디에이터를 사용하지 않아도 된다. 그러나 \(A[i]\)가 \(X\)보다 크다면, 이 빨래를 \(X\)분 내로 말리기 위해 라디에이터를 최소한의 횟수로 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, \(A[i]\)에서 \(X\)를 뺀 값은 라디에이터를 이용해 추가적으로 제거해야 하는 물의 양이다. 이 값을 \(K - 1\)로 나누고 올림 한 값이 이 빨래를 \(X\)분 내에 말리기 위해 필요한 최소 라디에이터 사용 횟수다. 모든 빨래에 대해 이 값을 계산한 뒤 더한 값이 \(X\)를 초과하면, \(X\)분 내에 모든 빨래를 말릴 수 없다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(K\)가 1인 경우는 예외 케이스인데, \(A[i]\)가 \(X\)를 초과하면 라디에이터를 사용해도 빨래를 \(X\)분 내에 말릴 수 없으므로 불가능하다고 판단하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

int n;
long long k, A[100010];

bool isPossible(long long x) {
    long long tot = 0;
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        if (A[i] &amp;gt; x) {
            if (k == 1) {
                return false;
            }
            
            long long up = A[i] - x;
            long long down = k - 1;
            
            tot += (up / down) + (up % down ? 1 : 0);
        }
    }
    
    return tot &amp;lt;= x;
}

int main() {
    scanf(&quot;%d&quot;, &amp;amp;n);
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        scanf(&quot;%lld&quot;, &amp;amp;A[i]);
    }
    
    scanf(&quot;%lld&quot;, &amp;amp;k);
    
    long long lo = 0, hi = 1e9 + 10;
    
    while (lo &amp;lt; hi) {
        long long mid = (lo + hi) / 2;
        
        if (isPossible(mid)) {
            hi = mid;
        } else {
            lo = mid + 1;
        }
    }
    
    printf(&quot;%lld\n&quot;, lo);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 3632번</category>
      <category>백준 3632번</category>
      <category>이분 탐색</category>
      <category>탐욕법</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/65</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-3632%EB%B2%88-Drying#entry65comment</comments>
      <pubDate>Sun, 25 Aug 2024 00:24:34 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] infer를 이용해 배열의 구성요소 타입 추출하기</title>
      <link>https://www.ohnimdev.com/entry/TypeScript-infer%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-%EB%B0%B0%EC%97%B4%EC%9D%98-%EA%B5%AC%EC%84%B1%EC%9A%94%EC%86%8C-%ED%83%80%EC%9E%85-%EC%B6%94%EC%B6%9C%ED%95%98%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript로 개발하다 보면 배열을 구성하는 요소의 타입을 추출해야 할 때가 종종 있다. 예를 들어, 서버에서 아래와 같은 응답 타입을 정의했다고 가정해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1724254040209&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Response {
    posts: { 
        userId: number;
        title: string;
        text: string;
    }[];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 응답을 받아 리스트 아이템을 렌더링 하는 컴포넌트를 만들 때, 해당 컴포넌트의 Prop 타입에는 보통 posts 배열을 구성하는 요소의 타입인 Post를 정의하고 싶을 것이다. 이때 가장 쉽게 생각할 수 있는 방법 중 하나는 아래와 같이 Post 타입을 별도로 분리하는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1724254428490&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Post {
    userId: number;
    title: string;
    text: string;
}

interface Response {
    posts: Post[];
}

// ListItemComponent
interface Props {
    post: Post;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, 경우에 따라 서버 응답 타입이 백엔드팀에서 제공해 주거나, 외부 라이브러리에 이미 정의되어 있어 Post와 같은 타입을 별도로 분리하기 어려울 수 있다. 이럴 때 배열의 구성요소 타입을 추출할 수 있는 방법을 아라보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;infer 키워드란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript에서 infer 키워드는 조건부 타입(Conditional Types)과 함께 사용되며, 제네릭 타입에서 특정 타입을 추론할 수 있게 해주는 키워드이다. 이 키워드는 타입 시스템 내에서 다양한 타입을 동적으로 추론할 수 있게 해주는 아주 기특한 친구다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;infer를 이용해 배열의 구성요소 타입 추출하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 배열의 구성요소의 타입을 추출하기 위해 infer 키워드를 사용하는 방법을 보여준다.&lt;/p&gt;
&lt;pre id=&quot;code_1724255008418&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type ElementType&amp;lt;T&amp;gt; = T extends (infer U)[] ? U : T;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 단계별로 해석해 보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;T는 제네릭 타입으로, 이 타입이 배열인지 여부를 extends 키워드를 사용해 확인한다.&lt;/li&gt;
&lt;li&gt;만약 T가 배열 타입이라면, infer U를 사용하여 배열의 구성요소 타입을 U로 추론한다.&lt;/li&gt;
&lt;li&gt;T가 배열이 아닌 경우에는 그냥 T 자체를 반환한다. 조금 더 엄밀하게 타입을 지정하고 싶다면 unknown과 같은 값을 사용해도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 사용하면, 배열의 구성요소의 타입을 매우 쉽게 추출할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다양한 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 예제들을 활용해 ElementType이 어떻게 동작을 하는지 확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1724255341681&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type ElementType&amp;lt;T&amp;gt; = T extends (infer U)[] ? U : T;

type StringArray = string[];
type NumberArray = number[];
type UnionArray = (string | number)[];
type ObjectArray = { x: string }[];
type NoneArray = Map&amp;lt;string, string&amp;gt;;

type StringArrayElement = ElementType&amp;lt;StringArray&amp;gt;; // string
type NumberArrayElement = ElementType&amp;lt;NumberArray&amp;gt;; // number
type UnionArrayElement = ElementType&amp;lt;UnionArray&amp;gt;; // string | number
type ObjectArrayElement = ElementType&amp;lt;ObjectArray&amp;gt;; // { x: string }
type NoneArrayElement = ElementType&amp;lt;NoneArray&amp;gt;; // Map&amp;lt;string, string&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 볼 수 있듯이, ElementType은 기본 타입(Primitive Type)뿐만 아니라 유니온 타입(Union Type)과 객체 타입으로 이루어진 배열의 구성요소 타입을 정확하게 추출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript에서 제공하는 infer 키워드와 조건부 타입을 활용하면 배열의 구성요소 타입을 손쉽게 추출할 수 있다. 이를 활용하면 처음에 고민하던 것을 다음과 같이 해결할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1724255596417&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type ElementType&amp;lt;T&amp;gt; = T extends (infer U)[] ? U : T;

interface Response {
    posts: { 
        userId: number;
        title: string;
        text: string;
    }[];
}

type Post = ElementType&amp;lt;Response[&quot;posts&quot;]&amp;gt;; // { userId: number; title: string; text: string; }

// ListItemComponent
interface Props {
    post: Post;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 infer를 활용하면 Promise로 감싸진 타입을 추출하거나, 함수의 파라미터와 반환 타입을 추출하는 등 다양한 상황에서 활용할 수 있다.&lt;/p&gt;</description>
      <category>개발로그/JavaScript, TypeScript</category>
      <category>infer 키워드</category>
      <category>typescript</category>
      <category>배열의 구성요소 타입 추출</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/64</guid>
      <comments>https://www.ohnimdev.com/entry/TypeScript-infer%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-%EB%B0%B0%EC%97%B4%EC%9D%98-%EA%B5%AC%EC%84%B1%EC%9A%94%EC%86%8C-%ED%83%80%EC%9E%85-%EC%B6%94%EC%B6%9C%ED%95%98%EA%B8%B0#entry64comment</comments>
      <pubDate>Thu, 22 Aug 2024 01:26:42 +0900</pubDate>
    </item>
    <item>
      <title>[백준 17610번] 양팔저울</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17610%EB%B2%88-%EC%96%91%ED%8C%94%EC%A0%80%EC%9A%B8</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17610&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/17610&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 서로 다른 무게의 추 \(k\) 개가 주어졌을 때, 양팔저울을 한 번만 사용하여 1부터 주어진 추들의 무게의 합인 \(S\) 사이의 모든 무게 중 측정이 불가능한 무게의 개수를 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해서는 \(k\)개의 추를 가지고 만들 수 있는 모든 가능한 무게 조합을 고려하는 완전 탐색을 이용할 수 있다. 각 추에 대해 우리는 세 가지 선택을 할 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;추를 사용하지 않는 경우&lt;/li&gt;
&lt;li&gt;양팔저울의 왼쪽에 올려놓는 경우&lt;/li&gt;
&lt;li&gt;양팔저울의 오른쪽에 올려놓는 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 추를 위와 같은 세 가지 선택지 중 하나로 결정한 후, 양팔저울의 왼쪽과 오른쪽에 놓인 추들의 무게 차이를 계산한다. 이렇게 계산된 모든 가능한 무게를 boolean 배열에 기록한다. 이 배열은 특정 무게가 측정 가능한지 여부를 나타내는 배열이다. 마지막으로, 1부터 주어진 추들의 무게의 합인 \(S\)까지의 모든 무게를 순회하면서 boolean 배열에 기록되지 않은 무게의 수를 세면, 그 값이 바로 측정 불가능한 무게의 개수가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 모든 경우의 수를 탐색하기 때문에 \(\mathcal {O}(3^k)\)의 시간복잡도를 가진다. 하지만 \(k\)가 최대 13밖에 되지 않기 때문에, 전체 경우의 수가 크지 않아 충분히 계산이 가능하다. 또한, 주어진 추들의 무게 합 \(S\)도 크지 않기 때문에 boolean 배열로 관리하기에 무리가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int n, total, A[20];
bool isPossible[13 * 200010];

void func(int pos, int lsum, int rsum) {
    if (pos == n) {
        isPossible[abs(lsum - rsum)] = true;
        
        return;
    }
    
    func(pos + 1, lsum, rsum);
    func(pos + 1, lsum + A[pos], rsum);
    func(pos + 1, lsum, rsum + A[pos]);
}

int main() {
    scanf(&quot;%d&quot;, &amp;amp;n);
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        scanf(&quot;%d&quot;, &amp;amp;A[i]);
        
        total += A[i];
    }
    
    func(0, 0, 0);
    
    int ans = 0;
    
    for (int i = 1 ; i &amp;lt;= total ; ++i) {
        if (!isPossible[i]) {
            ans += 1;
        }
    }
    
    printf(&quot;%d\n&quot;, ans);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 17610번</category>
      <category>koi 2019 1차대회 중등부 1번</category>
      <category>백준 17610번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/63</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17610%EB%B2%88-%EC%96%91%ED%8C%94%EC%A0%80%EC%9A%B8#entry63comment</comments>
      <pubDate>Sat, 10 Aug 2024 19:03:39 +0900</pubDate>
    </item>
    <item>
      <title>[백준 17608번] 막대기</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17608%EB%B2%88-%EB%A7%89%EB%8C%80%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17608&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/17608&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일렬로 세워진 막대기를 오른쪽에서 바라봤을 때, 보이는 막대기의 수를 구하는 문제이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 막대기들을&amp;nbsp;오른쪽에서 왼쪽으로 하나씩  살펴보는 방법으로 문제를 해결할 수 있다. 오른쪽 끝의 막대기부터 시작해서, 현재까지 본 막대기들 중 가장 높은 것과 현재 막대기의 높이를 비교한다. 만약 현재 막대기의 높이가 지금까지 본 막대기들 중 가장 높은 막대기의 높이보다 크다면, 그 막대기는 오른쪽에서 봤을 때 보이는 막대기이다. 이 과정을 가장 왼쪽에 있는 막대기까지 반복하면, 오른쪽에서 봤을 때 보이는 막대기의 수를 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

int n, A[100010];

int main() {
    scanf(&quot;%d&quot;, &amp;amp;n);
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        scanf(&quot;%d&quot;, &amp;amp;A[i]);
    }
    
    int ans = 0, prev = -1;
    
    for (int i = n - 1 ; i &amp;gt;= 0 ; --i) {
        if (A[i] &amp;gt; prev) {
            ans += 1;
            prev = A[i];
        }
    }
    
    printf(&quot;%d\n&quot;, ans);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 17608번</category>
      <category>koi 2019 1차대회 초등부 1번</category>
      <category>백준 17608번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/62</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17608%EB%B2%88-%EB%A7%89%EB%8C%80%EA%B8%B0#entry62comment</comments>
      <pubDate>Sat, 10 Aug 2024 17:33:36 +0900</pubDate>
    </item>
    <item>
      <title>[백준 32072번] 트리 뽑아내기</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-32072%EB%B2%88-%ED%8A%B8%EB%A6%AC-%EB%BD%91%EC%95%84%EB%82%B4%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/32072&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/32072&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 정의한 &lt;b&gt;뽑아내기&lt;/b&gt; 연산을 수행할 때마다 루트에 있는 가중치의 값을 출력하는 문제이다. 뽑아내기 연산을 수행할 때는 &lt;b&gt;특별한 경로&lt;/b&gt;를 찾아야 하는데, 한 정점에서 이동할 자식을 결정할 때는 직계 자식들만 비교하면 된다는 것에 유의하자. &lt;s&gt;(한 정점의 자손들 중 가장 작은 가중치를 가지는 정점으로 이동하는 경로를 찾아야 하는 것으로 문제를 잘못 이해해 시간을 너무 많이 날렸다... &amp;zwj;♂️)&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뽑아내기 연산을 수행한다고 하지 말고, 루트 정점에 있어야 하는 정점들을 순회한다고 접근하면 문제를 조금 더 쉽게 풀 수 있다. 그렇다면 어떤 순서대로 정점을 방문해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뽑아내기 연산을 할 때, 특별한 경로에 있는 정점들 중 하나인 정점 \(V\)와 자식 정점 \(K\)가 있다고 가정해 보자. 그리고 \(V\)와 같은 레벨에 있는 정점 \(U\)도 있다. 뽑아내기 연산 후 \(V\)의 가중치는 \(K\)의 가중치가 되기 때문에, 다음 뽑아내기 연산에 \(U\)가 포함되려면 \(U\)의 가중치는 \(K\)의 가중치보다 작아야 한다. 만약 \(U\)의 가중치가 \(K\)의 가중치보다 크다면, 다음 뽑아내기 연산에 포함될 정점은 \(K\)가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사실을 확장하면, 트리의 순서를 유지하면서 가중치가 가장 작은 정점들부터 방문하는 것이 뽑아내기 연산을 \(N\)번 수행했을 때의 결과와 같다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;functional&amp;gt;

using namespace std;

int n, p, A[300010], H[300010];
vector&amp;lt;int&amp;gt; node[300010];
priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; pq;

int main() {
    scanf(&quot;%d&quot;, &amp;amp;n);
    
    for (int i = 2 ; i &amp;lt;= n ; ++i) {
        scanf(&quot;%d&quot;, &amp;amp;p);
        node[p].push_back(i);
    }
    
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        scanf(&quot;%d&quot;, &amp;amp;A[i]);
        H[A[i]] = i;
    }
    
    pq.push(A[1]);
    
    while (!pq.empty()) {
        int val = pq.top();
        int here = H[val];
        
        pq.pop();
        
        printf(&quot;%d\n&quot;, val);
        
        for (int there: node[here]) {
            pq.push(A[there]);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 32072번</category>
      <category>koi 2024 2차대회 고등부 3번</category>
      <category>koi 2024 2차대회 중등부 4번</category>
      <category>백준 32072번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/61</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-32072%EB%B2%88-%ED%8A%B8%EB%A6%AC-%EB%BD%91%EC%95%84%EB%82%B4%EA%B8%B0#entry61comment</comments>
      <pubDate>Thu, 8 Aug 2024 03:01:21 +0900</pubDate>
    </item>
    <item>
      <title>[MobX] &amp;quot;There are multiple, different versions of MobX active&amp;quot; 에러 원인과 해결 방법</title>
      <link>https://www.ohnimdev.com/entry/MobX-There-are-multiple-different-versions-of-MobX-active-%EC%97%90%EB%9F%AC-%EC%9B%90%EC%9D%B8%EA%B3%BC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;MobX 다중 버전 에러&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MobX를 이용해 개발하다 보면 다음과 같은 에러 메시지를 마주칠 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. [MobX] minified error nr: 35.&lt;br /&gt;2. There are multiple, different versions of MobX active. Make sure MobX is loaded only once or use `configure({ isolateGlobalState: true })`&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 에러는 production 환경에서 볼 수 있는 메시지로, &lt;a title=&quot;MobX 소스코드&quot; href=&quot;https://github.com/mobxjs/mobx/blob/main/packages/mobx/src/errors.ts#L69&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MobX 소스코드&lt;/a&gt;를 보면 2번과 똑같은 에러라는 것을 알 수 있다. 이 에러는 무엇이고, 어떻게 해결할 수 있는지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러 발생 원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 에러는 버전이 서로 다른 버전의 MobX들을 동시에 사용할 때 발생한다. 여기서 이야기하는 버전은 MobX 내부 상태를 관리하기 위해 사용하는 저장소의 버전을 의미하며, &lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;MobX 패키지의 버전과는 관련이&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;a title=&quot;MobX의 내부 상태 저장소를 초기화 하는 코드&quot; href=&quot;https://github.com/mobxjs/mobx/blob/main/packages/mobx/src/core/globalstate.ts#L163&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MobX의 내부 상태 저장소를 초기화하는 코드&lt;/a&gt;를 살펴보면, 이미 초기화된 MobX의 내부 상태 저장소가 있으면 현재 초기화하려는&amp;nbsp; `MobXGlobals` 객체의 버전과 비교하는 로직이 있다. &lt;a title=&quot;MobX의 내부 상태 저장소의 버전&quot; href=&quot;https://github.com/mobxjs/mobx/blob/main/packages/mobx/src/core/globalstate.ts#L32&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이때의 버전은 클래스 안에 선언된 버전&lt;/a&gt;으로, 패키지의 버전과는 관련이 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 내부 상태 저장소를 초기화 하기 전에, 버전이 다른 내부 상태 저장소가 있다는 것이 확인되면 MobX는 &lt;a title=&quot;여러개의 MobX 버전이 초기화 됐으니 확인 하라는 에러&quot; href=&quot;https://github.com/mobxjs/mobx/blob/main/packages/mobx/src/core/globalstate.ts#L167&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여러 개의 MobX 버전이 초기화 됐으니 확인하라는 에러&lt;/a&gt;를 던진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방법 1. MobX 버전 통일하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 MobX 버전을 사용해서 발생하는 문제이므로, 사용하고 있는 MobX 버전을 하나로 통일하면 된다. 정확히는 MobXGlobals의 버전이 같은 패키지 버전들로 맞춰야 하는 것이지만, 패키지 버전을 선택할 수 있다면 안전성을 위해 패키지 버전을 하나로 통일하는 것을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방법 2. isolateGlobalState 옵션 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부득이하게 사용하고 있는 MobX 패키지들의 버전을 통일할 수 없는 경우를 위해, MobX는 내부 상태 저장소를 패키지별로 격리할 수 있는 옵션을 제공한다.&lt;/p&gt;
&lt;pre id=&quot;code_1722874682787&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { configure } from 'mobx';

// https://mobx.js.org/configuration.html#isolateglobalstate-boolean
configure({
  isolateGlobalState: true,
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 버전의 MobX 패키지를 import 하기 전에 configure 함수를 호출해야 함을 주의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발로그/JavaScript, TypeScript</category>
      <category>mobx</category>
      <category>mobx 다중 버전 에러</category>
      <category>mobx 에러</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/60</guid>
      <comments>https://www.ohnimdev.com/entry/MobX-There-are-multiple-different-versions-of-MobX-active-%EC%97%90%EB%9F%AC-%EC%9B%90%EC%9D%B8%EA%B3%BC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95#entry60comment</comments>
      <pubDate>Tue, 6 Aug 2024 01:35:14 +0900</pubDate>
    </item>
    <item>
      <title>[백준 25571번] 지그재그 부분배열</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-25571%EB%B2%88-%EC%A7%80%EA%B7%B8%EC%9E%AC%EA%B7%B8-%EB%B6%80%EB%B6%84%EB%B0%B0%EC%97%B4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/25571&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/25571&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길이가 \(N\)인 배열이 주어졌을 때, 만들 수 있는 연속된 부분배열들 중 지그재그인 부분배열의 개수를 찾는 문제이다. 배열이 지그재그라는 뜻은 배열의 원소가 감소와 증가 또는 증가와 감소를 반복하는 배열을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들 수 있는 모든 부분배열 \(A[lo, hi]\)에 대해서 지그재그인지 검사하면 답을 구할 수 있다. 하지만 이 방법은 시간복잡도가 \(\mathcal {O}(N^2)\)이라서, \(N\)이 10만 일 때 제한시간 내에 문제를 해결하지 못하므로 더 효율적인 풀이가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지그재그 부분배열의 특징을  살펴보면 \(A[lo, hi - 1]\)이 지그재그이고, \(A[lo, hi]\)가 지그재그일 때 \(A[lo ... hi - 1, hi]\) 또한 지그재그 부분배열이다. 즉 \(A[lo, hi - 1]\)이 지그재그일 때 \(A[lo, hi]\)도 지그재그이면 지그재그인 부분배열의 개수가 \(hi - lo\) 개만큼 추가되는 것이라고 볼 수 있다. 반대로 \(A[lo, hi - 1]\)이  지그재그인데 \(A[lo, hi]\)가 지그재그가 아니라면, \(A[lo ... hi - 1, hi]\)는 전부 지그재그 부분배열이 될 수 없다. 이 점을 이용하면 만들 수 있는 부분배열을 전부 순회하지 않고도, \(hi\)를 하나씩 늘려가면서 답을 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이디어는 간단하지만 구현은 조금(?) 까다롭다. \(A[lo, hi - 1]\)이 마지막이 감소하는 지그재그인지, 증가하는 지그재그인지에 따라 \(A[hi - 1]\)과 \(A [hi]\)의 증가 또는 감소를 체크해야 한다. 또한 \(A[lo, hi - 1]\)이 지그재그이지만 \(A[lo, hi]\)가 지그재그가 아닐 때, \(A[hi - 1, hi]\)가 또 다른 지그재그 부분배열의 시작이 될 수 있는지도 함께 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

enum LastState {
    NONE, // 초기 OR 지그재그가 아닌 상태
    DESC, // 마지막이 감소 상태
    INC, // 마지막이 증가 상태
};

int n, t, A[100010];

int main() {
    scanf(&quot;%d&quot;, &amp;amp;t);
    
    while (t--) {
        scanf(&quot;%d&quot;, &amp;amp;n);
        
        for (int i = 0 ; i &amp;lt; n ; ++i) {
            scanf(&quot;%d&quot;, &amp;amp;A[i]);
        }
        
        int lo = 0;
        long long ans = 0;
        LastState lastState = NONE;
        
        for (int hi = 1 ; hi &amp;lt; n ; ++hi) {
            // 초기 상태이거나 지그재그 부분수열이 아닌 경우
            if (lastState == NONE) {
                lo = hi - 1;
                
                // 새로운 지그재그 부분배열이 시작되는지 체크
                if (A[hi] &amp;gt; A[hi - 1]) {
                    lastState = INC;
                } else if (A[hi] &amp;lt; A[hi - 1]) {
                    lastState = DESC;
                }
            } 
            // 이전 지그재그 부분배열이 감소로 끝난경우
            else if (lastState == DESC) {
                // 증가가 된다면 지그재그 부분배열의 길이가 증가
                if (A[hi] &amp;gt; A[hi - 1]) {
                    lastState = INC;
                }
                // 감소가 된다면 새로운 지그재그 부분배열이 시작
                else if (A[hi] &amp;lt; A[hi - 1]){
                    lastState = DESC;
                    lo = hi - 1;
                }
                // 지그재그 부분배열이 아닌 경우
                else {
                    lastState = NONE;
                }
            }
            // 이전 지그재그 부분배열이 증가로 끝난 경우
            else if (lastState == INC) {
                // 감소가 된다면 지그재그 부분배열의 길이가 증가
                if (A[hi] &amp;lt; A[hi - 1]) {
                    lastState = DESC;
                }
                // 증가가 된다면 새로운 지그재그 부분배열이 시작
                else if (A[hi] &amp;gt; A[hi - 1]) {
                    lastState = INC;
                    lo = hi - 1;
                }
                // 지그재그 부분배열이 아닌 경우
                else {
                    lastState = NONE;
                }
            }
            
            // 지그재그 부분배열이라면 만들 수 있는 모든 부분 배열을 답에 누적
            if (lastState != NONE) {
                ans += hi - lo;
            }
        }
        
        printf(&quot;%lld\n&quot;, ans);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 25571번</category>
      <category>백준 25571번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/59</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-25571%EB%B2%88-%EC%A7%80%EA%B7%B8%EC%9E%AC%EA%B7%B8-%EB%B6%80%EB%B6%84%EB%B0%B0%EC%97%B4#entry59comment</comments>
      <pubDate>Mon, 29 Jul 2024 02:28:03 +0900</pubDate>
    </item>
    <item>
      <title>[백준 32070번] 색깔 모으기</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-32070%EB%B2%88-%EC%83%89%EA%B9%94-%EB%AA%A8%EC%9C%BC%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/32070&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/32070&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 같은 색깔의 공을 하나의 상자에 모으는 데 필요한 최소 이동 횟수를 구하는 문제이다. 각 상자는 최대 두 개의 공을 담을 수 있다. 공을 꺼낼 때는 상자의 위에 있는 공만 꺼낼 수 있으며, 공을 넣을 때는 빈 상자나 같은 색깔의 공이 있는 상자에만 넣을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 색깔의 공을 하나의 상자에 모으기 위해 공을 옮기다 보면 연쇄적으로 다른 공들도 움직여야 하는 상황이 발생한다. 이 과정에서 연쇄적으로 움직이는 공들을 추적하다 보면 하나 이상의 싸이클이 만들어지는 것을 발견할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;123123 (1).png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N6LfV/btsIRdnHs7N/kMfIuiFkUVpVw5DXdL6BL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N6LfV/btsIRdnHs7N/kMfIuiFkUVpVw5DXdL6BL0/img.png&quot; data-alt=&quot;두 개의 싸이클이 생긴 예제&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N6LfV/btsIRdnHs7N/kMfIuiFkUVpVw5DXdL6BL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN6LfV%2FbtsIRdnHs7N%2FkMfIuiFkUVpVw5DXdL6BL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;두 개의 싸이클이 생긴 예제&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;267&quot; data-filename=&quot;123123 (1).png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;두 개의 싸이클이 생긴 예제&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시를 보면 1, 2, 3, 4번 공의 싸이클과 5, 6, 7번 공의 사이클은 답을 구하는 과정에서 서로 영향을 끼치지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싸이클사이클 내에 있는 공들을 같은 색끼리 모으기 위해 필요한 최소 이동 횟수는 정해져 있다. 먼저 사이클에 있는 공 한 개를 빈 상자로 옮겨 빈틈(?)을 만들어야 한다. 그런 다음, 그 빈틈을 이용해 나머지 공들을 하나씩 옮기며 색을 맞춘다. 이 과정에서 필요한 이동 횟수를 계산해 보면, 사이클에 속한 상자의 개수가 \(x\) 개일 때, \(x + 1\) 번이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 모든 싸이클에서 이 방법이 적용 가능한 것은 아니다. 사이클 내에서 상자 위에 올라와 있는 색의 쌍이 두 쌍 이상이면 공을 연쇄적으로 움직이지 못해 답을 구할 수 없다. 이러한 경우에는 문제에서 요구한 대로 -1을 출력하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 공을 옮기는 시뮬레이션이 필요한 것이 아니라 싸이클사이클 내에 속한 상자의 개수만 알면 되기 때문에 사이클을 관리하는 것은 유니온 파인드 자료구조를 사용하는 것이 좋다. 다음은 이 문제를 풀기 위한 전체 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

int n, top_pairs[200010];
bool solved[200010];
vector&amp;lt;int&amp;gt; ball_idx[200010];

// 유니온 파인드 클래스 정의
class UnionFind {
    vector&amp;lt;int&amp;gt; parent, rank, cnt;
    
public:
    UnionFind(int sz): parent(sz), rank(sz, 1), cnt(sz, 1) {
        for (int i = 0 ; i &amp;lt; sz ; ++i) {
            parent[i] = i;
        }
    }
    
    int find(int u) {
        if (u == parent[u]) {
            return u;
        }
        
        return parent[u] = find(parent[u]);
    }
    
    void merge(int u, int v) {
        u = find(u);
        v = find(v);
        
        if (u == v) {
            return;
        }
        
        if (rank[u] &amp;gt; rank[v]) {
            swap(u, v);
        }
        
        parent[u] = v;
        cnt[v] += cnt[u];
        
        if (rank[u] == rank[v]) {
            ++rank[v];
        }
    }
    
    int getCnt(int u) {
        u = find(u);
        
        return cnt[u];
    }
};

int main() {
    scanf(&quot;%d&quot;, &amp;amp;n);
    
    // 각 공의 위치를 저장
    // 짝수는 상자의 윗부분, 홀수는 상자의 아랫부분에 있다는 것을 의미
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        int a, b;
        scanf(&quot;%d%d&quot;, &amp;amp;a, &amp;amp;b);
        ball_idx[a].push_back(i * 2);
        ball_idx[b].push_back(i * 2 + 1);
    }
    
    UnionFind uf(n + 1);
    
    // 같은 색 공이 있는 상자들끼리 싸이클을 만든다
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        uf.merge(ball_idx[i][0] / 2, ball_idx[i][1] / 2);
    }
    
    // 싸이클 내에서 상자 위에 올라와 있는 색의 쌍이 두 쌍 이상인지 확인
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        if (ball_idx[i][0] % 2 == 0 &amp;amp;&amp;amp; ball_idx[i][1] % 2 == 0) {
            int rootIdx = uf.find(ball_idx[i][0] / 2);
            
            top_pairs[rootIdx] += 1;
        }
    }
    
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        int rootIdx = uf.find(ball_idx[i][0] / 2);
        
        if (top_pairs[rootIdx] &amp;gt;= 2) {
            puts(&quot;-1&quot;);
            
            return 0;
        }
    }
    
    int ans = 0;
    
    // 각 싸이클에 대해 최적해를 계산
    // 싸이클에 속한 상자의 갯수가 한 개라면 이미 같은 색이라는 것을 의미하므로 계산하지 않는다
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        int rootIdx = uf.find(ball_idx[i][0] / 2);
        
        if (solved[rootIdx]) {
            continue;
        }
        
        solved[rootIdx] = true;
        
        int x = uf.getCnt(rootIdx);
        
        if (x &amp;gt;= 2) {
            ans += x + 1;
        }
    }
    
    printf(&quot;%d\n&quot;, ans);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 32070번</category>
      <category>koi 2024 2차대회 중등부 2번</category>
      <category>koi 2024 2차대회 초등부 3번</category>
      <category>백준 32070번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/58</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-32070%EB%B2%88-%EC%83%89%EA%B9%94-%EB%AA%A8%EC%9C%BC%EA%B8%B0#entry58comment</comments>
      <pubDate>Sun, 28 Jul 2024 03:28:11 +0900</pubDate>
    </item>
    <item>
      <title>[백준 32069번] 가로등</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-32069%EB%B2%88-%EA%B0%80%EB%A1%9C%EB%93%B1</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/32069&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/32069&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수직선 도로 위해 \(N\) 개의 가로등이 위치해 있다. 각 위치에서 가장 가까운 가로등까지의 거리를 계산하고, 그 거리들을 오름차순으로 정렬한 뒤 앞에서부터 \(K\) 개 출력하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서는 어두운 정도를 각 위치로부터 가장 가까운 가로등까지의 거리로 정의하고 있다. 그래서 처음에는 각 위치별로 가장 가까운 가로등까지의 거리를 모두 계산해야 한다고 생각할 수 있다. 하지만 도로의 길이 \(L\)이 최대 \(10^{18}\)까지 될 수 있기 때문에, 모든 위치를 전부 탐색하는 완전탐색은 사실상 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해서는 가로등을 기준으로 생각할 필요가 있다. 각 가로등들을 중심으로 가까운 위치들을 하나씩 방문해 나가면서 어두운 정도를 계산한다면, 모든 위치를 탐색하지 않고도 가로등으로부터 가장 가까운 \(K\) 개의 위치를 탐색과 동시에 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 아이디어를 구현하기 위해서는 BFS(너비 우선 탐색)이 적합하다. 일반적인 BFS에서는 시작 지점을 하나만 두고 탐색을 시작하지만, 이 문제에서는 여러 개의 가로등이 있으므로 큐에 모든 가로등의 위치를 넣고 시작한다. 또한, 방문한 지점을 저장하기 위해 배열을 사용할 수 도 있지만, 도로의 길이가 매우 길 수 있기 때문에 메모리를 생각해 set과 같은 자료구조를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;set&amp;gt;

using namespace std;

long long l, a;
int n, k;
const long long dx[] = {-1, 1}; // 왼쪽과 오른쪽으로 이동하기 위한 배열
queue&amp;lt;pair&amp;lt;long long, long long&amp;gt;&amp;gt; q; // BFS를 위한 큐, (위치, 거리)
set&amp;lt;long long&amp;gt; visited; // 방문한 위치를 저장하는 집합

// 위치가 도로 범위 내에 있는지 확인하는 함수
bool isPossible(long long x) {
    return 0 &amp;lt;= x &amp;amp;&amp;amp; x &amp;lt;= l;
}

int main() {
    // 도로 길이, 가로등 개수, 출력할 위치 개수를 입력 받는다
    scanf(&quot;%lld%d%d&quot;, &amp;amp;l, &amp;amp;n, &amp;amp;k);
    
    // 각 가로등의 위치를 입력 받고 큐에 추가, 방문 표시
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        scanf(&quot;%lld&quot;, &amp;amp;a);
        q.push({ a, 0 });
        visited.insert(a);
    }
    
    // BFS 시작
    while (!q.empty()) {
        long long here = q.front().first;
        long long dist = q.front().second;
        q.pop();
        
        // 현재 위치의 어두운 정도 (가장 가까운 가로등까지의 거리)를 출력
        printf(&quot;%lld\n&quot;, dist);
        --k;
        
        // K개의 위치를 모두 출력했으면 프로그램 종료
        if (k == 0) {
            return 0;
        }
        
        // 현재 위치의 왼쪽과 오른쪽 이웃을 탐색
        for (int i = 0 ; i &amp;lt; 2 ; ++i) {
            long long there = here + dx[i];
            
            // 이웃 위치가 도로 범위 내에 있고 아직 방문하지 않았다면 큐에 추가
            if (isPossible(there) &amp;amp;&amp;amp; !visited.count(there)) {
                q.push({ there, dist + 1 });
                visited.insert(there);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 32069번</category>
      <category>koi 2024 2차대회 고등부 1번</category>
      <category>koi 2024 2차대회 중등부 1번</category>
      <category>koi 2024 2차대회 초등부 2번</category>
      <category>백준 32069번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/57</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-32069%EB%B2%88-%EA%B0%80%EB%A1%9C%EB%93%B1#entry57comment</comments>
      <pubDate>Fri, 19 Jul 2024 02:26:43 +0900</pubDate>
    </item>
    <item>
      <title>[백준 32068번] 보물 찾기</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-32068%EB%B2%88-%EB%B3%B4%EB%AC%BC-%EC%B0%BE%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/32068&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/32068&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 시작점 \(S\)에서 출발하여 보물 위치 \(L\) 또는 \(R\)에 방문하는데 필요한 이동 횟수를 계산하는 것이다. 이동 패턴은 한 단계식 오른쪽과 왼쪽을 번갈아가며 한 칸씩 점점 더 멀리 이동하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브태스크 4번까지는 입력 제한이 크지 않아 시뮬레이션으로도 해결할 수 있다. 그러나 서브태스크 5번을 해결하려면 더 효율적인 접근 방법이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, \(S\)보다 왼쪽에 있는 \(L\)을 방문하기 위해서는 \(S\)와 \(L\)의 거리만큼 떨어진 오른쪽에 있는 점들을 모두 방문해야 한다. 따라서 \(L\)을 방문하기 위한 이동 횟수는 \(2 \times (S - L)\)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로, \(S\)보다 오른쪽에 있는 \(R\)을 방문하기 위해서는 \(S\)와 \(R\)의 거리보다 1만큼 작은 거리에 떨어진 왼쪽의 모든 점들을 방문해야 한다. 따라서 \(R\)을 방문하기 위한 이동 횟수는 \(2 \times (R - S - 1)\)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 놀이는 \(L\)또는 \(R\)을 방문하면 끝난다. 따라서 두 값 중 작은 값이 정답이 된다. 아무것도 하지 않은 초기 상태도 이동 횟수에 포함되므로 최종적으로 1을 더해 출력해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

int t;

int main() {
    freopen(&quot;input.txt&quot;, &quot;r&quot;, stdin);
    scanf(&quot;%d&quot;, &amp;amp;t);
    
    while (t--) {
        int l, r, s;
        scanf(&quot;%d%d%d&quot;, &amp;amp;l, &amp;amp;r, &amp;amp;s);
        
        int left = (s - l) * 2;
        int right = (r - s - 1) * 2 + 1;
        int ans = left &amp;lt; right ? left : right;
        
        printf(&quot;%d\n&quot;, ans + 1);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/56</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-32068%EB%B2%88-%EB%B3%B4%EB%AC%BC-%EC%B0%BE%EA%B8%B0#entry56comment</comments>
      <pubDate>Thu, 18 Jul 2024 02:15:05 +0900</pubDate>
    </item>
    <item>
      <title>[백준 32036번] 수열과 쿼리 45</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-32036%EB%B2%88-%EC%88%98%EC%97%B4%EA%B3%BC-%EC%BF%BC%EB%A6%AC-45</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/32036&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/32036&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제를 바꿔서 생각하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열에 연산을 직접 하는것이 아니라 수학적인 방법으로 접근하면 조금 더 쉽게 문제를 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 쿼리에서 주어지는 a, b를 아래와 같이 함수로 표현될 수 있다.(문제에서는 x, y라고 했는데, 좌표 평면에서의 x, y와 겹치기 때문에 헷갈리지 않기 위해 a, b로 바꿔서 적었다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$f_i(x) = |x - a_i| + b_i$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 여러 개의 1번 쿼리를 통해 &lt;b&gt;업데이트된 배열의 값은 각 1번 쿼리의 함수들을 더한 것&lt;/b&gt;으로 표현될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$F(x) = \sum_{i=1}^{n} f_i(x) = \sum_{i=1}^{n}|x - a_i| + \sum_{i=1}^{n} b_i$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 쿼리에서는 배열의 최솟값이 가장 처음 등장하는 지점과 그 값을 찾아야 한다. 배열 \(A\)를 함수 \(F(x)\)로 바꾸었기 때문에, &lt;b&gt;2번 쿼리는 함수 \(F(x)\)에서 최솟값을 찾는 문제&lt;/b&gt;로 변환할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수에서 최솟값을 만드는 지점 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 \(F(x)\)의 값이 \(x\)에 따라 어떻게 변하는지 알기 위해 x의 값이 변함에 따라 \(F(x)\)의 기울기가 어떻게 변하는지 생각해 보자. 함수 \(F(x)\)에 있는 \(a_i\)들을 작은 순서대로 \(a_1, a_2, \dots, a_n\)이라고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, \(x\)가 \(a_1\)보다 작을 때 \(F(x)\)의 기울기는 음수이다. 즉, \(x\)가 커짐에 따라 \(F(x)\)의 값은 작아진다. \(x\)가 증가하면서 각 \(a_i\)를 만날 때마다 \(F(x)\)의 기울기는 조금씩 증가하다 \(x\)가 \(a_i\)들의 중앙값에 도달하는 순간 기울기는 0이 된다. 중앙값을 지나게 되면 \(F(x)\)의 기울기는 양수로 변하게 되며, \(x\)가 증가할수록 \(F(x)\)의 값은 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 \(x\)가 \(a_i\)들의 중앙값일 때 \(F(x)\)의 값이 최소가 된다. \(a_i\)들의 개수가 짝수면 중앙값이 두 개가 되는데, 이때 \(F(x)\)의 값이 동일하므로 문제에서 요구한 대로 더 작은 값을 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수의 최솟값 계산하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(F(x)\)의 최솟값과 그 때의 \(x\)를 출력하는 2번 쿼리를 계산하기 위해서는 \(a_i\)들의 중앙값을 찾고, \(F(x)\)를 계산하는 두 가지로 나눠서 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;변하는 수열에서 중앙값을 빠르게 찾기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(F(x)\)에 존재하는 \(a_i\)들을 정렬한 뒤 중앙값을 찾을 수 있지만, 1번 쿼리가 들어올 때마다 \(a_i\)들의 순서가 바뀔 수 있어 정렬을 다시 해야 하기 때문에 효율적이지 못하다. 균형 잡힌 이진 탐색 트리를 직접 구현한다면 중앙값을 빠르게 찾을 수 있지만, 이는 구현에 공수가 많이 들기 때문에 제한된 시간 내에 문제를 풀어야 하는 알고리즘 대회에서는 적합하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변하는 수열에서 중앙값을 빠르게 찾는 효율적인 방법 중 하나는 수열을 정렬한 뒤 절반은 최대 힙, 절반은 최소 힙으로 관리하는 것이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;최대 힙의 크기는 최소 힙의 크기와 같거나 하나 더 크다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최대 힙의 최댓값은 최소 힙의 최솟값보다 작거나 같다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 가지 원칙을 지키며 최대 힙과 최소 힙을 관리한다면, 변하는 수열에서 중앙값은 항상 최대 힙의 루트가 된다. 1번 쿼리가 들어왔을 때, 즉 수열에 새로운 값이 추가될 때에는 1번 원칙을 만족하게 최대 힙 또는 최소 힙에 수를 추가한 뒤 2번 원칙이 지켜지는지 확인하면 된다. 만약 2번 원칙이 지켜지지 않는다면 최대 힙과 최소 힙의 루트를 서로 바꿔준다.&lt;/p&gt;
&lt;pre id=&quot;code_1720956795302&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;priority_queue&amp;lt;long long, vector&amp;lt;long long&amp;gt;, less&amp;lt;long long&amp;gt;&amp;gt; maxHeap;
priority_queue&amp;lt;long long, vector&amp;lt;long long&amp;gt;, greater&amp;lt;long long&amp;gt;&amp;gt; minHeap;

void addElementToSequence(long long a) {
    if (maxHeap.size() == minHeap.size()) {
        maxHeap.push(a);
    } else {
        minHeap.push(a);
    }
    
    if (!maxHeap.empty() &amp;amp;&amp;amp; !minHeap.empty() &amp;amp;&amp;amp; maxHeap.top() &amp;gt; minHeap.top()) {
        long long rootOfMaxHeap = maxHeap.top();
        long long rootOfMinHeap = minHeap.top();
        
        maxHeap.pop();
        maxHeap.push(rootOfMinHeap);
        
        minHeap.pop();
        minHeap.push(rootOfMaxHeap);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;F(x) 계산하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(F(x)\)를 최소로 만드는 \(x\)를 찾았으니 실제로 \(F(x)\)를 계산할 차례이다. 여기서 \(x\)에 대입할 중앙값을 \(a_m\)이라고 하자. 중앙값 \(a_m\)을 기준으로 \(F(a_m)\)의 계산을 두 부분으로 나눌 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;\(a_m\) 보다 작거나 같은 \(a_i\)들&lt;/li&gt;
&lt;li&gt;\(a_m\) 보다 큰 \(a_i\)들&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(a_m\) 보다 작거나 같은 원소들은 절댓값을 풀면 \(a_m - a_i\)이 된다. 이 값은 최대 힙의 원소 개수와 최대 힙의 원소들의 합을 이용하여 계산할 수 있다. \(a_m\) 보다 큰 원소들은 절댓값을 풀면 \(a_i - a_m\)이 된다. 이 값은 최소 힙의 원소 개수와 최소 힙의 원소들의 합을 이용하여 계산할 수 있다. 최대 힙과 최소 힙을 사용하여 중앙값을 관리할 때, 각 힙의 합을 계산하면 매번 모든 원소들의 합을 계산하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(\sum_{i=1}^{n} b_i\)는 이제까지 1번 쿼리로 들어온 b의 값들을 합이므로 별도 변수로 저장하여 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 2번 쿼리를 \(O(1)\)에 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1720958453013&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

priority_queue&amp;lt;long long, vector&amp;lt;long long&amp;gt;, less&amp;lt;long long&amp;gt;&amp;gt; maxHeap;
priority_queue&amp;lt;long long, vector&amp;lt;long long&amp;gt;, greater&amp;lt;long long&amp;gt;&amp;gt; minHeap;
int q, t;
long long sumOfMaxHeap, sumOfMinHeap, sumOfB;

void addElementToSequence(long long a) {
    if (maxHeap.size() == minHeap.size()) {
        maxHeap.push(a);
        sumOfMaxHeap += a;
    } else {
        minHeap.push(a);
        sumOfMinHeap += a;
    }
    
    if (!maxHeap.empty() &amp;amp;&amp;amp; !minHeap.empty() &amp;amp;&amp;amp; maxHeap.top() &amp;gt; minHeap.top()) {
        long long rootOfMaxHeap = maxHeap.top();
        long long rootOfMinHeap = minHeap.top();
        
        maxHeap.pop();
        maxHeap.push(rootOfMinHeap);
        sumOfMaxHeap = sumOfMaxHeap - rootOfMaxHeap + rootOfMinHeap;
        
        minHeap.pop();
        minHeap.push(rootOfMaxHeap);
        sumOfMinHeap = sumOfMinHeap - rootOfMinHeap + rootOfMaxHeap;
    }
}

int main() {
    scanf(&quot;%d&quot;, &amp;amp;q);
    
    while (q--) {
        scanf(&quot;%d&quot;, &amp;amp;t);
        
        if (t == 1) {
            long long a, b;
            scanf(&quot;%lld%lld&quot;, &amp;amp;a, &amp;amp;b);
            
            sumOfB += b;
            addElementToSequence(a);
        } else {
            long long minXValue = maxHeap.top();
            long long minYValue = (minXValue * (long long)maxHeap.size() - sumOfMaxHeap) + (sumOfMinHeap - minXValue * (long long)minHeap.size()) + sumOfB;
            
            printf(&quot;%lld %lld\n&quot;, minXValue, minYValue);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 32036</category>
      <category>백준 32036</category>
      <category>수열과 쿼리 45</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/55</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-32036%EB%B2%88-%EC%88%98%EC%97%B4%EA%B3%BC-%EC%BF%BC%EB%A6%AC-45#entry55comment</comments>
      <pubDate>Sun, 14 Jul 2024 21:17:37 +0900</pubDate>
    </item>
    <item>
      <title>한 번쯤 들어봤을 Reset CSS와 Normalize CSS, 무슨 차이가 있을까?</title>
      <link>https://www.ohnimdev.com/entry/%ED%95%9C-%EB%B2%88%EC%AF%A4-%EB%93%A4%EC%96%B4%EB%B4%A4%EC%9D%84-Reset-CSS%EC%99%80-Normalize-CSS-%EB%AC%B4%EC%8A%A8-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EC%9E%88%EC%9D%84%EA%B9%8C</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 Reset CSS와 Normalize CSS가 필요한가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 개발을 하다 보면 HTML에 아무런 스타일을 작성하지 않았는데도 스타일이 적용된 것을 본 적이 있을 것이다. 이는 브라우저가 HTML 태그들에 대해 기본적으로 적용하는 마진, 패딩, 폰트 크기 등이 있기 때문이다. 문제는 각 브라우저마다 기본적으로 적용하는 스타일이 다를 수 있다는 점이다. 이로 인해 동일한 HTML 코드가 브라우저마다 다르게 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 등장한 개념이 바로 &lt;b&gt;Reset CSS&lt;/b&gt;와 &lt;b&gt;Normalize CSS&lt;/b&gt;이다. 이 두 개념은 모두 스타일의 일관성을 유지하는 것을 목표로 하지만, 이를 달성하기 위한 방식에 차이가 있다. 이제 Reset CSS와 Normalize CSS의 차이점과 각각의 장단점을 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Reset CSS의 목적과 기능&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reset CSS는 브라우저가 HTML 태그들에 기본적으로 적용하는 모든 스타일을 똑같은 값으로&amp;nbsp;&lt;b&gt;'초기화'&lt;/b&gt; 한다. 이렇게 함으로써 브라우저간의 스타일 불일치를 최소화하고, 모든 요소들이 동일한 출발점에서 스타일을 쌓아가도록 만든다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;Reset CSS 적용 전&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;Reset CSS 적용 후&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2PzUj/btsIt98ugn3/RwrDsLFrPJaVmYGC0iZoE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2PzUj/btsIt98ugn3/RwrDsLFrPJaVmYGC0iZoE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2PzUj/btsIt98ugn3/RwrDsLFrPJaVmYGC0iZoE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2PzUj%2FbtsIt98ugn3%2FRwrDsLFrPJaVmYGC0iZoE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Reset CSS를 적용하기 전&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;300&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCtiDf/btsItWuMzq1/XLIQ2U4zYtAD60YAFKsbp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCtiDf/btsItWuMzq1/XLIQ2U4zYtAD60YAFKsbp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCtiDf/btsItWuMzq1/XLIQ2U4zYtAD60YAFKsbp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCtiDf%2FbtsItWuMzq1%2FXLIQ2U4zYtAD60YAFKsbp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Reset CSS를 적용한 후&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;300&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reset CSS 적용 전과 후의 차이를 이해하기 위해, 가장 유명한 Reset CSS 중 하나인 &lt;span style=&quot;background-color: #fdfdfb; color: #261b0d; text-align: start;&quot;&gt;&lt;a title=&quot;Eric A. Meyer의 Reset CSS&quot; href=&quot;https://meyerweb.com/eric/tools/css/reset/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Eric A. Meyer의 Reset CSS&lt;/a&gt;를 예로 든다. Reset CSS를 적용하지 않았을 때는 각 태그마다 기본 스타일을 가지고 있다. 그러나 Reset CSS를 적용하면 모든 HTML 태그에 대해 기본적으로 적용되는 스타일(여백, 패딩, 글꼴 크기 등)이 같은 값으로 초기화되어 똑같이 보이는 것을 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Normalize CSS의 목적과 기능&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Normalize CSS는 브라우저의 기본 스타일을 초기화 하는 대신, &lt;b&gt;브라우저마다 발생하는 스타일 차이를 보정&lt;/b&gt;하여 일관된 스타일을 유지하는 것을 목표로 한다. 이를 통해 기본 스타일을 완전히 제거하지 않고도 브라우저간의 스타일 불일치를 최소화할 수 있다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 36px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;Normalize CSS 적용 전&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px; text-align: center;&quot;&gt;&lt;b&gt;Normalize CSS 적용 후&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJyRds/btsIvNJKvW7/h5FmUZFPCbShRKysDV4Ff1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJyRds/btsIvNJKvW7/h5FmUZFPCbShRKysDV4Ff1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJyRds/btsIvNJKvW7/h5FmUZFPCbShRKysDV4Ff1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJyRds%2FbtsIvNJKvW7%2Fh5FmUZFPCbShRKysDV4Ff1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Normalize CSS 적용 전&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;300&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.html.png&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E2OA8/btsIu5Yye4k/73pAanNE3giEJVQqPZ37r0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E2OA8/btsIu5Yye4k/73pAanNE3giEJVQqPZ37r0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E2OA8/btsIu5Yye4k/73pAanNE3giEJVQqPZ37r0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE2OA8%2FbtsIu5Yye4k%2F73pAanNE3giEJVQqPZ37r0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;Normalize CSS 적용 후&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;300&quot; data-filename=&quot;3.html.png&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Normalize CSS 적용 전과 후의 차이를 이해하기 위해, 가장 유명한 Normalize CSS 중 하나인 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a title=&quot;Nicolas Gallagher의 normalize.css&quot; href=&quot;https://necolas.github.io/normalize.css/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Nicolas Gallagher의 normalize.css&lt;/a&gt;를 예로 든다. Reset CSS와는 다르게 Normalize CSS를 적용하기 전과 후의 모습이 크게 다르지 않다는 점을 확인할 수 있다. 여기서 보여주지는 않았지만 Normalize CSS는&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;디바이스가 가지고 있는 버그들이나 구형 브라우저가 지원하지 않는 속성들을 수정해 모든 기기에서 일관성 있게 보여주는 기능들도 가지고 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reset CSS를 사용할 때 장단점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reset CSS는 브라우저가 HTML 태그에 기본적으로 적용하는 스타일을 제거하기 때문에, 페이지가 어떻게 보일지 예측하기 쉬우며, 처음부터 자신만의 스타일을 쌓아가고 싶을 때 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reset CSS는 모든 스타일을 제거하기 때문에 스타일을 처음부터 쌓아갈 여유가 없다면 Reset CSS를 사용하는 것은 큰 부담이 될 수 있다. 또한, Reset CSS는 거의 모든 HTML 태그를 일괄적으로 초기화하기 때문에, 인스펙터 도구에 계속해서 Reset CSS의 내용이 표시되어 디버깅이 어려워질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Normalize CSS를 사용할 때 장단점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Normalize CSS는 HTML 태그가 가지고 있는 기본적인 스타일들을 최대한 보존하기 때문에, 스타일을 일일이 다시 선언할 필요가 없어 스타일링 셋업에 대한 부담이 덜하다. 또한, 디바이스 및 브라우저가 가지고 있는 일반적인 버그들을 수정하여 더 나은 사용자 경험을 제공할 수 있다. 더불어, 스타일을 오버라이드해야 하는 태그를 정확하게 타깃으로 잡기 때문에 인스펙터 도구에 많은 내용이 표시되지 않아 디버깅에 방해를 주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Normalize CSS는 HTML 태그가 기본 스타일을 최대한 보존하기 때문에, 완전한 커스텀 스타일링을 위해서는 많은 스타일 오버라이드가 필요할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그래서 어떤 것을 사용하는 게 좋을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reset CSS와 Normalize CSS는 각각의 장단점이 있으며, 프로젝트의 요구 사항과 개발자의 스타일링 선호도에 따라 선택을 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굳이 하나를 선택해야 한다면 완전한 커스텀이 필요한 경우에는 Reset CSS를, 기본 스타일을 어느 정도 사용하고자 한다면 Normalize CSS를 선택하면 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;(그런데 1인 개발을 하는데 완전한 커스텀이 필요하다면 &lt;a title=&quot;Ant Design&quot; href=&quot;https://ant.design/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Ant Design&lt;/a&gt;, &lt;a title=&quot;Chakra UI&quot; href=&quot;https://v2.chakra-ui.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Chakra UI&lt;/a&gt; 등 잘 만들어져 있는 디자인 시스템을 사용하는 게 좋지 않을까 싶다.)&lt;/s&gt;&lt;/p&gt;</description>
      <category>개발로그/HTML, CSS</category>
      <category>normalize css</category>
      <category>Reset CSS</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/54</guid>
      <comments>https://www.ohnimdev.com/entry/%ED%95%9C-%EB%B2%88%EC%AF%A4-%EB%93%A4%EC%96%B4%EB%B4%A4%EC%9D%84-Reset-CSS%EC%99%80-Normalize-CSS-%EB%AC%B4%EC%8A%A8-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EC%9E%88%EC%9D%84%EA%B9%8C#entry54comment</comments>
      <pubDate>Thu, 11 Jul 2024 03:14:28 +0900</pubDate>
    </item>
    <item>
      <title>[백준 31997번] 즐거운 회의</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-31997%EB%B2%88-%EC%A6%90%EA%B1%B0%EC%9A%B4-%ED%9A%8C%EC%9D%98</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/31997&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/31997&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N명의 사람들이 T시간 동안 진행되는 회의에 참석하고 떠나는 시간이 주어진다. 그리고 각 사람들은 서로 친구 관계를 맺고 있다. 회의가 진행되는 동안 특정 시각 t에서 회의에 참석한 사람들 중 친구인 쌍이 몇 쌍인지 계산하는 문제이다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;입력 데이터 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N명의 사람들이 언제 도착하고 떠나는지, 그리고 누구와 친구 관계를 맺고 있는지 저장해야 한다. 각 시간마다 어떤 사람이 회의에 참석하고 떠나는지를 기록하면, 회의가 진행되는 모든 시간 동안 현재 참석하고 있는 사람들을 쉽게 파악할 수 있다. 또한, 친구 관계는 인접 리스트를 이용해 저장한다.&lt;/p&gt;
&lt;pre id=&quot;code_1720540980630&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 각 시간대별로 회의에 참석하는 사람을 저장할 벡터
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; in(t + 1);

// 각 시간대별로 회의에서 떠나는 사람을 저장할 벡터
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; out(t + 1);

// 각 사람의 친구 관계를 저장할 인접 리스트
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; friends(n + 1);

// 각 사람의 도착 시간과 떠나는 시간을 입력받아 저장
for (int i = 1; i &amp;lt;= n; ++i) {
    int a, b;
    scanf(&quot;%d%d&quot;, &amp;amp;a, &amp;amp;b);
    in[a].push_back(i);  // 도착 시간에 사람 추가
    out[b].push_back(i); // 떠나는 시간에 사람 추가
}

// 친구 관계를 입력받아 인접 리스트에 저장
while (m--) {
    int u, v;
    scanf(&quot;%d%d&quot;, &amp;amp;u, &amp;amp;v);
    friends[u].push_back(v); // u의 친구 목록에 v 추가
    friends[v].push_back(u); // v의 친구 목록에 u 추가
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;회의 시작부터 종료까지 시뮬레이션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회의에서 떠나고 참석하는 사람들의 정보가 시간별로 저장되어 있어, 회의가 진행되는 상황을 시뮬레이션할 수 있다. 떠나는 사람과 참석하는 사람들의 리스트를 이용해 현재 어떤 사람들이 회의에 참석하고 있는지 시뮬레이션하는 것은 어렵지 않지만, 몇 쌍의 친구가 있는지 관리하는 것은 조금 신경을 써야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(u\)라는 사람이 회의에서 떠날 때를 생각해보자. \(u\)의 친구 목록에서 현재 회의에 참석하고 있는 친구가 있다면 \(u\)가 떠남으로써 친구 쌍의 수는 그 수만큼 감소하게 된다. 반대로 \(u\)라는 사람이 회의에 참석하게 되면, \(u\)의 친구 목록에서 현재 회의에 참석하고 있는 친구의 수만큼 친구 쌍의 수는 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점을 이용하면 현재 몇 쌍의 친구들이 회의에 참석하고 있는지 효율적으로 계산할 수 있다. 현재 회의에 참석하고 있는 사람들은 boolean 배열을 이용해 쉽게 관리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1720541903668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 현재 회의에 참석 중인 사람을 표시하는 배열
vector&amp;lt;bool&amp;gt; joined(n + 1, false);

// 현재 회의에 존재하는 친구 쌍의 수
int total_relations = 0;

for (int i = 0; i &amp;lt; t; ++i) {
    // 회의에서 떠나는 사람들을 시뮬레이션
    for (auto u : out[i]) {
        joined[u] = false;
        
        // u의 친구 목록들 중 현재 회의에 참석 중인 친구가 있다면 친구 쌍의 수를 감소
        for (auto v : friends[u]) {
            if (joined[v]) {
                --total_relations;
            }
        }
    }
    
    // 회의에 참석하는 사람들을 시뮬레이션
    for (auto u : in[i]) {
        joined[u] = true;
        
        // u의 친구 목록들 중 현재 회의에 참석 중인 친구가 있다면 친구 쌍의 수를 증가
        for (auto v : friends[u]) {
            if (joined[v]) {
                ++total_relations;
            }
        }
    }
    
    printf(&quot;%d\n&quot;, total_relations);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

int n, m, t;

int main() {
    scanf(&quot;%d%d%d&quot;, &amp;amp;n, &amp;amp;m, &amp;amp;t);
    
    // 각 시간대별로 회의에 참석하는 사람을 저장할 벡터
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; in(t + 1);
    
    // 각 시간대별로 회의에서 떠나는 사람을 저장할 벡터
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; out(t + 1);
    
    // 각 사람의 친구 관계를 저장할 인접 리스트
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; friends(n + 1);
    
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        int a, b;
        scanf(&quot;%d%d&quot;, &amp;amp;a, &amp;amp;b);
        in[a].push_back(i);
        out[b].push_back(i);
    }
    
    while (m--) {
        int u, v;
        scanf(&quot;%d%d&quot;, &amp;amp;u, &amp;amp;v);
        friends[u].push_back(v);
        friends[v].push_back(u);
    }
    
    // 현재 회의에 참석 중인 사람을 표시하는 배열
    vector&amp;lt;bool&amp;gt; joined(n + 1, false);

    // 현재 친구 관계 쌍의 총 수
    int total_relations = 0;

    for (int i = 0; i &amp;lt; t; ++i) {
        // 현재 시간에 회의에서 떠나는 사람들 처리
        for (auto u : out[i]) {
            joined[u] = false; // 사람이 회의에서 떠남
            
            // 친구 목록을 순회하며 현재 회의에 참석 중인 친구를 찾음
            for (auto v : friends[u]) {
                if (joined[v]) {
                    --total_relations; // 친구 쌍의 수 감소
                }
            }
        }
        
        // 현재 시간에 회의에 참석하는 사람들 처리
        for (auto u : in[i]) {
            joined[u] = true; // 사람이 회의에 참석
            
            // 친구 목록을 순회하며 현재 회의에 참석 중인 친구를 찾음
            for (auto v : friends[u]) {
                if (joined[v]) {
                    ++total_relations; // 친구 쌍의 수 증가
                }
            }
        }
        
        // 현재 시간에 친구 관계 쌍의 총 수를 출력
        printf(&quot;%d\n&quot;, total_relations);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 31997</category>
      <category>백준 31997</category>
      <category>제2회 청소년 it 경시대회 고등부 1번</category>
      <category>제2회 청소년 it 경시대회 초등부 2번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/53</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-31997%EB%B2%88-%EC%A6%90%EA%B1%B0%EC%9A%B4-%ED%9A%8C%EC%9D%98#entry53comment</comments>
      <pubDate>Wed, 10 Jul 2024 01:55:06 +0900</pubDate>
    </item>
    <item>
      <title>[백준 31965번] 회의 장소</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-31965%EB%B2%88-%ED%9A%8C%EC%9D%98-%EC%9E%A5%EC%86%8C</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/31965&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/31965&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;피로도를 최소로 하는 회의 순서 정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회의 세트에 참여하는 K개의 집의 좌표를 오름차순으로 \(x_1, x_2, \cdots, x_k\)라고 하고, 각 집에서 회의를 개최했을 때 드는 비용을 \(c_1, c_2, \cdots, c_k\)라고 정의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 집에서&amp;nbsp;회의를 개최했을 때 드는 비용을 계산한 뒤 유심히 관찰해 보면, 회의 비용이 감소하거나 증가하는 방향으로 회의를 개최할 때 피로도가 최소가 된다는 것을 발견할 수 있다. 그 이유는 회의 비용이 정렬되어 있으면 인접한 회의의 비용 차이를 최소화할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회의 비용이 정렬되어 있을 때 피로도는 \(c_1, c_2, \cdots, c_k\)중에서 최댓값과 최솟값의 차이와 같고, 이때가 바로 만들 수 있는 최소의 피로도가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최대 회의 비용과 최소 회의 비용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회의 장소를 \(x_i\)에서 \(x_{i+1}\)로 옮길 때 비용이 얼마나 변하는지 계산해 보자. 비용 변화는 두 부분으로 나눠서 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 집부터 \(i\)번째 집까지는 이동 거리가 \((x_{i+1} - x_i)\)만큼 멀어지므로, 회의 비용은 \(i \times (x_{i+1} - x_i)\)만큼 증가한다. \(i+1\)번째 집부터 K번째 집까지는 이동 거리가 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;\((x_{i+1} - x_i)\)만큼&lt;span&gt; 줄어드므로, 회의 비용은 \((k - i) \times (x_{i+1} - x_i)\)만큼 감소한다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;회의 비용의 총변화량은 증가량과 감소량의 합으로 계산할 수 있으며 아래와 같이 정리할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$\displaylines{\begin{align} \triangle C&amp;amp; = i \times (x_{i+1} - x_i) - (k - i) \times (x_{i+1} - x_i) \\ &amp;amp; = (2 \times i - k) \times (x_{i+1} - x_i)\end{align}}$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해, 회의 장소를 첫 번째 집부터 K번째 집으로 옮길 때의 비용 변화를 분석해 보자. 비용은 첫 번째 집에서 시작하여 점점 줄어들다가 \(\lfloor{\frac{k + 1}{2}}\rfloor\)번째 집에서 최소가 되었다가, 다시 증가하기 시작한다. 즉, 첫 번째 집 혹은 K번째 집에서 회의를 열 때 비용이 최대가 되고 \(\lfloor{\frac{k + 1}{2}}\rfloor\)번째 집에서 회의를 열 때 비용이 최소가 되는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;회의 비용 빠르게 계산하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(i\)번째 집에서 회의를 열었을 때 비용은 \(\sum_{j=1}^k |x_i - x_j|\)로 계산할 수 있다. 절댓값 기호를 없애기 위해서 \(i\)번째 집보다 작은 좌표에 있는 왼쪽 집들과 큰 좌표에 있는 오른쪽 집들로 나눠서 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽 집들은 전부 좌표가 \(x_i\)보다 작으므로 \(\sum_{j=1}^i(x_i - x_j)\)으로 계산할 수 있고 \(i \times x_i - \sum_{j=1}^i x_j\)으로 정리할 수 있다. 오른쪽 집들은 전부 좌표가 \(x_i\)보다 크므로 \(\sum_{j=i}^k(x_j - x_i)\)으로 계산할 수 있고 \(\sum_{j=i}^k x_j - (k - i + 1) \times x_i\)으로 정리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회의 세트에 포함된 집들은 전부 연속된 집들이기 때문에 &lt;a title=&quot;빠르게 배열의 부분 합을 구하는 Prefix Sum 알고리즘&quot; href=&quot;https://www.ohnimdev.com/entry/%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%B0%B0%EC%97%B4%EC%9D%98-%EB%B6%80%EB%B6%84-%ED%95%A9%EC%9D%84-%EA%B5%AC%ED%95%98%EB%8A%94-Prefix-Sum-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Prefix Sum 알고리즘&lt;/a&gt;을 이용하면 회의 비용을 빠르게 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;회의 세트에 포함된 집들을 빠르게 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색을 사용하면 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;각 질의마다 주어진 \(L\)과 \(R\)에 포함되는 집들을 &lt;/span&gt;로그 시간 안에 빠르게 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int n, q, l, r;
long long X[200010], P[200010];

/**
 * 특정 집에서 회의를 개최할 때의 비용을 계산하는 함수
 * @param i - 회의 세트에 포함된 집들의 시작 인덱스
 * @param j - 회의 세트에 포함된 집들의 끝 인덱스
 * @param k - 회의를 개최하는 집의 인덱스
 * @return - 집 k에서 모일 때의 총 비용
 */
long long getCost(int i, int j, int k) {
    long long leftCost = (long long)(k - i + 1) * X[k] - (P[k] - P[i - 1]);
    long long rightCost = (P[j] - P[k - 1]) - (long long)(j - k + 1) * X[k];
    
    return leftCost + rightCost;
}

int main() {
    // n: 집의 개수, q: 질의의 개수
    scanf(&quot;%d%d&quot;, &amp;amp;n, &amp;amp;q);
    
    // 집의 좌표와 누적 합 계산
    for (int i = 1 ; i &amp;lt;= n ; ++i) {
        scanf(&quot;%lld&quot;, &amp;amp;X[i]);
        P[i] = P[i - 1] + X[i];
    }
    
    // 각 질의에 대해 처리
    while (q--) {
        scanf(&quot;%d%d&quot;, &amp;amp;l, &amp;amp;r);
        // 주어진 범위 [l, r]에 해당하는 집의 인덱스 범위를 찾기 위해 이분 탐색 사용
        int lo = lower_bound(X + 1, X + n + 1, l) - X;
        int hi = upper_bound(X + 1, X + n + 1, r) - X - 1;
        
        // 범위 내에 집이 없을 경우
        if (lo &amp;gt;= hi) {
            puts(&quot;0&quot;);
        } else {
            // 최대 회의 비용과 최소 회의 비용 계산
            long long maxStress = max(getCost(lo, hi, lo), getCost(lo, hi, hi));
            long long minStress = getCost(lo, hi, (lo + hi) / 2);
            
            // 최소 피로도를 출력
            printf(&quot;%lld\n&quot;, maxStress - minStress);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 31965</category>
      <category>koi 2024 1차대회 중등부 2번</category>
      <category>백준 31965</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/52</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-31965%EB%B2%88-%ED%9A%8C%EC%9D%98-%EC%9E%A5%EC%86%8C#entry52comment</comments>
      <pubDate>Tue, 9 Jul 2024 03:00:06 +0900</pubDate>
    </item>
    <item>
      <title>HTML에서 DOCTYPE을 꼭 사용해야 하는 이유</title>
      <link>https://www.ohnimdev.com/entry/HTML%EC%97%90%EC%84%9C-DOCTYPE%EC%9D%84-%EA%BC%AD-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;HTML에서 DOCTYPE의 역할과 중요성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML을 작성할 때 첫 번째 줄에 &amp;lt;!DOCTYPE html&amp;gt; 코드를 꼭 작성해야 한다. 이 코드는 브라우저에게 HTML을 최신 스펙에 맞게 표시해야 한다고 알려준다. 또한 검색엔진들이 문서를 수집할 때 DOCTYPE이 있는 문서에 더 높은 점수를 준다고 비공식적으로 알려져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올바르게 UI 표시와 더 높은 SEO 점수를 고려할 때, HTML 첫 번째 줄에 &amp;lt;!DOCTYPE html&amp;gt;을 넣지 않을 이유가 없다. 그런데 우리는 HTML 문서를 작성하는 것이 명확한데도 불구하고 왜 브라우저에게 이 문서가 HTML로 만들어졌다고 알려줘야 하는 것일까? DOCTYPE이 무엇이고, 그리고 왜 등장했는지에 대해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DOCTYPE이 등장한 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML과 브라우저가 처음 등장했을 당시에는 브라우저마다 HTML을 해석하는 방법이 제각각이었다. 똑같은 HTML 파일을 브라우저마다 다르게 출력하는 일이 빈번했고, 이에 대응하고자 개발자들은 각 브라우저별로 HTML을 따로 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;W3C(월드 와이드 웹 컨소시엄)은 이 문제를 해결하기 위해 표준 HTML 스펙을 만들었다. 하지만 기존에 브라우저마다 제각각 해석하던 HTML들과 표준 HTML 스펙은 차이가 있었고, 표준을 지켜 개발을 하면 오히려 UI가 깨지는 아이러니한 상황이 발생했다. 브라우저들은 표준이 아닌 HTML 문서들과 표준 HTML 문서를 구분해서 보여줘야 할 필요성을 느꼈고, 그렇게 등장한 것이 DOCTYPE이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DOCTYPE에 따른 브라우저 렌더링 모드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOCTYPE에 따라 브라우저는 아래 세 가지의 모드로 문서를 표시한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼크 모드(quirks mode): 표준이 아닌 HTML 문서를 지원&lt;/li&gt;
&lt;li&gt;쿼크가 아닌 모드(no-quirks mode): W3C에서 만든 표준 HTML 스펙 문서를 지원&lt;/li&gt;
&lt;li&gt;제한된 쿼크 모드(limited-quirks mode): 일부 비표준 스펙을 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;과거에는 쿼크가 아닌 모드(no-quirks mod)를 전체 표준 모드(full standards mode)로, 제한된 쿼크 모드(limited-quirks mode)를 거의 표준 모드(almost-standards mode)로 불렀지만 현재는 브라우저의 동작이 표준화되었고 일관성을 위해 모드 이름을 변경했다고 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML5가 발표된지 10년이 다 되어가는 현재 거의 모든 브라우저가 표준 HTML 스펙을 지원하기 때문에 쿼크가 아닌 모드 말고 다른 모드를 사용할 일은 없어 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;브라우저의 렌더링 모드를 확인할 수 있는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 document.compatMode 값을 통해 현재 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;브라우저가&amp;nbsp;&lt;/span&gt;어떤 모드로 렌더링 되고 있는지 확인할 수 있다. document.compatMode의 값이 &quot;BackCompat&quot;이면 쿼크 모드로 렌더링을 하고 있는 것이고 &quot;CSS1Compat&quot;이면 쿼크가 아닌 모드 혹은 제한된 쿼크 모드로 렌더링을 하고 있다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DOCTYPE을 선언하지 않은 HTML 문서&lt;/h3&gt;
&lt;pre id=&quot;code_1720032462591&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;script&amp;gt;
            // BackCompat 출력
            console.log(document.compatMode);
        &amp;lt;/script&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;DOCTYPE을 선언하지 않은 HTML 문서&amp;lt;/div&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DOCTYPE을 선언한 HTML 문서&lt;/h3&gt;
&lt;pre id=&quot;code_1720032502046&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;script&amp;gt;
            // CSS1Compat 출력
            console.log(document.compatMode);
        &amp;lt;/script&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;DOCTYPE을 선언한 HTML 문서&amp;lt;/div&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;브라우저가 지원하는 DOCTYPE 종류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt; 선언은 현재 HTML 문서가 HTML5 버전에 맞춰 작성됐다는 것을 의미한다. HTML5 이전 버전의 DOCTYPE은 &lt;a title=&quot;DOCTYPE 위키피디아&quot; href=&quot;https://en.wikipedia.org/wiki/Document_type_declaration&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;위키피디아&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;</description>
      <category>개발로그/HTML, CSS</category>
      <category>DOCTYPE</category>
      <category>쿼크모드</category>
      <category>표준모드</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/51</guid>
      <comments>https://www.ohnimdev.com/entry/HTML%EC%97%90%EC%84%9C-DOCTYPE%EC%9D%84-%EA%BC%AD-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0#entry51comment</comments>
      <pubDate>Thu, 4 Jul 2024 04:23:43 +0900</pubDate>
    </item>
    <item>
      <title>[백준 31964번] 반품 회수</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-31964%EB%B2%88-%EB%B0%98%ED%92%88-%ED%9A%8C%EC%88%98</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/31964&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/31964&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(KOI 2024 1차대회 초등부 3번, 고등부 1번)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트럭이 직선 도로상의 각 집을 특정 시각 이후에 방문에 물건을 회수한 후 다시 출발지로 돌아올 수 있는 최단 시간을 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그리디로 접근하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출발지점에서 마지막 집까지 왕복하는 거리인 \(2 \times X_n\)은 트럭이 필수적으로 사용해야 하는 시간이다. 따라서 이 시간을 제외하고 각 집에서 기다려야 하는 시간을 최소화하는 것이 중요하다. 오른쪽 끝 집부터 방문해 물건을 회수하면, 트럭이 출발지점으로 돌아오는 시간에 각 집에서 기다려야 하는 시간이 포함될 수 있다. 그래서 트럭이 가장 오른쪽 집부터 방문에 물건을 회수하는 것이 가장 최선의 선택지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시뮬레이션으로 문제 풀기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리디로 접근해야 한다는 것을 알았으니 시뮬레이션으로 문제를 풀 수 있다. 먼저 \(X_n\)의 시간을 사용해 트럭을 마지막 집으로 이동시키자. 그다음 가장 오른쪽 집부터 왼쪽 집으로 이동하면서 물건을 회수하는 것을 시뮬레이션할 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;현재 사용한 시간에 \(X_{i+1}\)에서 \(X_{i}\)으로 이동한 시간을 더해준다.&lt;/li&gt;
&lt;li&gt;만약 현재 사용한 시간이 \(T_{i}\)보다 크거나 같다면 물건을 회수할 수 있다.&lt;/li&gt;
&lt;li&gt;만약 현재 사용한 시간이 \(T_{i}\)보다 작다면 \(T_{i}\)까지 기다린다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 집에서 물건을 회수한 뒤 다시 출발지점으로 돌아오는 것도 시뮬레이션해야 한다는 것을 잊지 말자.&lt;/p&gt;
&lt;pre id=&quot;code_1719333241687&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int simulate() {
    int ret = X[n - 1];
    
    for (int i = n - 1 ; i &amp;gt;= 0 ; --i) {
        if (i != n - 1) {
            ret += X[i + 1] - X[i];
        }
        
        if (ret &amp;lt; T[i]) {
            ret = T[i];
        }
    }
    
    ret += X[0];
    
    return ret;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수식으로 문제 풀기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시뮬레이션으로도 문제를 풀 수 있지만, 수식을 사용하면 조금 더 우아(?)하게 문제를 풀 수 있다. 트럭이 가장 오른쪽 집으로 이동한 뒤, 다시 출발지점으로 쉬지 않고 돌아오는 동안 각 집 \(i\)에 방문하는 시간을 계산해 보자. 이 시간은 \(X_n + (X_n - X_i)\)이다. 이 값이 각 집 \(i\)에서 물건을 내놓는 시간 \(T_i\)보다 작을 수 있어서 각 집에서는 최소 \(Y_i\)만큼의 시간을 기다려야 한다. 이를 수식으로 정리하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$X_n + (X_n - X_i) + Y_i \geq T_i$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구해야 하는 값인 \(Y_i\)를 제외하고 전부 오른쪽 항으로 이동해 수식을 정리해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$Y_i \geq T_i - 2 \times X_n + X_i$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 집에 대해 위 수식을 만족하는 가장 작은 \(Y_i\)를 구하고 그 값들 중 가장 큰 \(Y\)를 구한다. 그다음 트럭이 왕복하는 시간 \(2 \times X_n\)에 더해주면 답을 구할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1719334414176&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int solve() {
    int Y = 0;
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        Y = max(Y, T[i] - 2 * X[n - 1] + X[i]);
    }
    
    return 2 * X[n - 1] + Y;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 31964번</category>
      <category>koi 2024 1차대회 고등부 1번</category>
      <category>koi 2024 1차대회 초등부 3번</category>
      <category>백준 31964번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/50</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-31964%EB%B2%88-%EB%B0%98%ED%92%88-%ED%9A%8C%EC%88%98#entry50comment</comments>
      <pubDate>Wed, 26 Jun 2024 01:55:05 +0900</pubDate>
    </item>
    <item>
      <title>[백준 31963번] 두 배</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-31963%EB%B2%88-%EB%91%90-%EB%B0%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/31963&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/31963&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2024 KOI 1차대회 초등부 2번, 중등부 1번)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길이가 N인 수열이 주어졌을 때, 수열의 한 원소에 숫자 2를 곱하는 연산을 할 수 있다. 이때 주어진 수열을 오름차순으로 만들 수 있는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;최소한의 연산 횟수를 구하는 문제이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시뮬레이션으로 문제 풀기(부분점수)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수열의 두 번째 원소부터 마지막 원소까지 순회하면서, 각 원소가 이전 원소보다 커질 때까지 2를 곱하는 과정을 반복하고, 그 횟수를 직접 계산하는 방법으로 문제를 풀 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1719244747247&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int simulate(const vector&amp;lt;int&amp;gt;&amp;amp; vi) {
    vector&amp;lt;int&amp;gt; incresing_order(vi.size());
    incresing_order[0] = vi[0];
    
    int ret = 0;
    
    for (int i = 1 ; i &amp;lt; vi.size() ; ++i) {
        incresing_order[i] = vi[i];
        
        while (incresing_order[i] &amp;lt; incresing_order[i - 1]) {
            incresing_order[i] *= 2;
            ++ret;
        }
    }
    
    return ret;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 방법으로는 만점을 받을 수 없고 부분점수만 받을 수 있다. 예를 들어, 1,000,000부터 시작해 1씩 감소하는 수열이 주어졌을 때를 생각해 보자. 이 경우가 최악의 사례인데, 이때 시간복잡도는 \(\mathcal{O}(N^2)\)이므로 N이 최대일 때는 주어진 시간 내에 문제를 해결할 수 없다. 따라서 만점을 받기 위해서는 다른 방법이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수식으로 정리해서 문제 풀기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(i-1\)번째 원소 \(A_{i-1}\)에 2가 곱해진 횟수를 \(x\)라고 하고, \(i\)번째 원소 \(A_{i}\)에 2가 곱해진 횟수를 \(y\)라고 하자. 이때 수열이 오름차순이 되려면 다음과 같은 수식을 만족해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$A_{i-1} \times 2^x \leq A_{i} \times 2^y$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 \(y\)를 제외한 나머지 모든 항을 왼쪽으로 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$\frac{A_{i-1}}{A_{i}} \times 2^x \leq 2^y$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 양쪽 항 모두에 \(log_2\)를 적용하고, 로그의 곱셈 및 나눗셈 법칙을 이용해 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$log_2 \frac{A_{i-1}}{A_{i}} + x \leq y$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 \(i - 1\)번째 원소 \(A_{i-1}\)에 2를 곱한 횟수 \(x\)를 알고 있다면, 앞서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;정리한 수식을 이용해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;마지막 원소까지 2를 곱해야 하는 횟수를 모두 구할 수 있다. 첫 번째 원소에는 2를 곱하는 않는 것이 유리하므로, \(i - 1\)이 1일 때 \(x\)는 0으로 정해져 있다. 이 사실을 활용하면 문제의 답을 구할 수 있다. 답이 int 범위가 넘어갈 수 있으므로 long long 자료형을 사용해야 한다는 것에 유의하자.&lt;/p&gt;
&lt;pre id=&quot;code_1719249987240&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long long solve(const vector&amp;lt;int&amp;gt;&amp;amp; vi) {
    long long ret = 0;
    
    vector&amp;lt;int&amp;gt; multiply_cnt(vi.size(), 0);
    
    for (int i = 1 ; i &amp;lt; vi.size() ; ++i) {
        multiply_cnt[i] = max((int)ceil(log2((double)vi[i - 1]/ (double)vi[i]) + (double)multiply_cnt[i - 1]), 0);
        
        ret += multiply_cnt[i];
    }
    
    return ret;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;사실 위 코드는 부동소수점 연산의 정확도 문제 때문에 잠재적인 위험이 있다. 정확한 로그 값이 필요한 것이 아니라 올림(ceil) 연산을 한 값이 필요하기 때문에 vi[i - 1]과 v[i]에 직접 log2 연산을 하지 않아도 된다. 대신 vi[i - 1]에 2를 몇 번 곱해야 vi[i]보다 커지는지 혹은 vi[i - 1]에 2를 몇 번 나눠야 v[i]보다 작아지는지를 계산해 둔다면 ceil과 똑같은 기능을 기대할 수 있다. 이렇게 한다면 부동소수점 연산이 가지고 있는 부정확함에 벌벌 떨지 않아도 될 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cmath&amp;gt;

using namespace std;

long long solve(const vector&amp;lt;int&amp;gt;&amp;amp; vi) {
    long long ret = 0;
    
    vector&amp;lt;int&amp;gt; multiply_cnt(vi.size(), 0);
    
    for (int i = 1 ; i &amp;lt; vi.size() ; ++i) {
        multiply_cnt[i] = max((int)ceil(log2((double)vi[i - 1]/ (double)vi[i]) + (double)multiply_cnt[i - 1]), 0);
        
        ret += multiply_cnt[i];
    }
    
    return ret;
}

int main() {
    int n;
    
    cin &amp;gt;&amp;gt; n;
    
    vector&amp;lt;int&amp;gt; vi(n);
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        cin &amp;gt;&amp;gt; vi[i];
    }
    
    long long ans = solve(vi);
    
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; endl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 31963번</category>
      <category>koi 2024 1차대회 초등부 2번</category>
      <category>백준 31963번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/49</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-31963%EB%B2%88-%EB%91%90-%EB%B0%B0#entry49comment</comments>
      <pubDate>Tue, 25 Jun 2024 01:13:01 +0900</pubDate>
    </item>
    <item>
      <title>[백준 16937번] 두 스티커</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-16937%EB%B2%88-%EB%91%90-%EC%8A%A4%ED%8B%B0%EC%BB%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16937&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/16937&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모눈종이 안에  두 개의 스티커를 겹치지 않게 붙일 때, 그 넓이의 합의 최댓값을 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N의 크기가 작기 때문에 문제를 해결하기 위해 완전탐색을 사용할 수 있다. 따라서, 두 개의 스티커를 골라 모눈종이 안에 붙일 수 있는지 판단하는 함수를 작성하는 것이 이 문제를 해결하기 위한 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 첫 번째 스티커 붙이기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 한 개의 스티커를 모눈종이 안에 붙일 수 있는지 판단하는 함수인 &lt;b&gt;&lt;i&gt;isInside&lt;/i&gt;&lt;/b&gt; 함수를 작성한다. 이 함수는 왼쪽 사각형이 오른쪽 사각형을 포함할 수 있는지 판단한다. 왼쪽 사각형의 가로와 세로가 오른쪽 사각형의 가로와 세로보다 같거나 큰지 확인하는 것으로 구현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1718290582436&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bool isInside(int outer_width, int outer_height, int inner_width, int innder_height) {
    return outer_width &amp;gt;= inner_width &amp;amp;&amp;amp; outer_height &amp;gt;= innder_height;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 두 번째 스티커 붙이기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 스티커를 모눈종이 안에 붙일 수 있다면 이제 두 번째 스티커를 붙일 차례이다. 첫 번째 스티커를 붙일 때 왼쪽 위 꼭짓점에 맞춰서 붙이는 것이 두 번째 스티커를 붙일 공간을 최대한 확보하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드.webp&quot; data-origin-width=&quot;2210&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKsNX9/btsHXQBvsWn/1ueUZShK7tJ03S20Kb8PMK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKsNX9/btsHXQBvsWn/1ueUZShK7tJ03S20Kb8PMK/img.webp&quot; data-alt=&quot;두 번째 스티커를 붙일 후보 공간들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKsNX9/btsHXQBvsWn/1ueUZShK7tJ03S20Kb8PMK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKsNX9%2FbtsHXQBvsWn%2F1ueUZShK7tJ03S20Kb8PMK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;두 번째 스티커를 붙일 후보 공간들&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;190&quot; data-filename=&quot;다운로드.webp&quot; data-origin-width=&quot;2210&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;두 번째 스티커를 붙일 후보 공간들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 스티커를 왼쪽 위에 붙이고 나면 두 번째 스티커를 붙일 수 있는 후보 공간들은 위 그림처럼 남은 아래 사각형 또는 오른쪽 사각형이 된다. 1번에서 구현한 isInside 함수를 이용하면 이를 구현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1718291639991&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bool canPlaceStickers(int s1_width, int s1_height, int s2_width, int s2_height) {
    // 첫 번째 스티커를 붙일 수 없는 경우
    if (!isInside(w, h, s1_width, s1_height)) {
        return false;
    }
    
    // 두 번째 스티커를 남은 아래 사각형에 붙일 수 있는 경우
    if (isInside(w, h - s1_height, s2_width, s2_height)) {
        return true;
    }
    
    // 두 번째 스티커를 남은 오른쪽 사각형에 붙일 수 있는 경우
    if (isInside(w - s1_width, h, s2_width, s2_height)) {
        return true;
    }
    
    // 두 번째 사각형을 붙이지 못하는 경우
    return false;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 스티커 회전 고려하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 스티커를 90도 회전시키는 것도 고려해야 한다. 이는 스티커의 가로와 세로를 서로 바꾸는 방식으로 구현할 수 있다. 두 개의 스티커를 회전시키는 경우의 수 조합을 만들어 고려하면 선택한 두 개의 스티커를 붙일 수 있는지 판단할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1718291882431&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int i = 0 ; i &amp;lt; n ; ++i) {
    for (int j = i + 1 ; j &amp;lt; n ; ++j) {
        int R1[] = {R[i], C[i]};
        int C1[] = {C[i], R[i]};
        int R2[] = {R[j], C[j]};
        int C2[] = {C[j], R[j]};
        
        for (int k = 0 ; k &amp;lt; 2 ; ++k) {
            for (int l = 0 ; l &amp;lt; 2 ; ++l) {
                int r1 = R1[k], c1 = C1[k], r2 = R2[l], c2 = C2[l];
                
                if (canPlaceStickers(r1, c1, r2, c2)) {
                    ans = max(ans, r1 * c1 + r2 * c2);
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int h, w, n, R[110], C[110];

bool isInside(int outer_width, int outer_height, int inner_width, int innder_height) {
    return outer_width &amp;gt;= inner_width &amp;amp;&amp;amp; outer_height &amp;gt;= innder_height;
}

bool canPlaceStickers(int s1_width, int s1_height, int s2_width, int s2_height) {
    // 첫 번째 스티커를 붙일 수 없는 경우
    if (!isInside(w, h, s1_width, s1_height)) {
        return false;
    }
    
    // 두 번째 스티커를 남은 아래 사각형에 붙일 수 있는 경우
    if (isInside(w, h - s1_height, s2_width, s2_height)) {
        return true;
    }
    
    // 두 번째 스티커를 남은 오른쪽 사각형에 붙일 수 있는 경우 =
    if (isInside(w - s1_width, h, s2_width, s2_height)) {
        return true;
    }
    
    // 두 번째 사각형을 붙이지 못하는 경우
    return false;
}

int main() {
    freopen(&quot;input.txt&quot;, &quot;r&quot;, stdin);
    scanf(&quot;%d%d%d&quot;, &amp;amp;h, &amp;amp;w, &amp;amp;n);
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        scanf(&quot;%d%d&quot;, &amp;amp;R[i], &amp;amp;C[i]);
    }
    
    int ans = 0;
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        for (int j = i + 1 ; j &amp;lt; n ; ++j) {
            int R1[] = {R[i], C[i]};
            int C1[] = {C[i], R[i]};
            int R2[] = {R[j], C[j]};
            int C2[] = {C[j], R[j]};

            for (int k = 0 ; k &amp;lt; 2 ; ++k) {
                for (int l = 0 ; l &amp;lt; 2 ; ++l) {
                    int r1 = R1[k], c1 = C1[k], r2 = R2[l], c2 = C2[l];

                    if (canPlaceStickers(r1, c1, r2, c2)) {
                        ans = max(ans, r1 * c1 + r2 * c2);
                    }
                }
            }
        }
    }
    
    printf(&quot;%d\n&quot;, ans);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 16937</category>
      <category>백준 16937</category>
      <category>완전탐색</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/48</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-16937%EB%B2%88-%EB%91%90-%EC%8A%A4%ED%8B%B0%EC%BB%A4#entry48comment</comments>
      <pubDate>Fri, 14 Jun 2024 00:30:03 +0900</pubDate>
    </item>
    <item>
      <title>[백준 30804번] 과일 탕후루</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-30804%EB%B2%88-%EA%B3%BC%EC%9D%BC-%ED%83%95%ED%9B%84%EB%A3%A8</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/30804&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/30804&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 1부터 9로 이루어진 수열이 주어진다. 이때, 연속된 부분 수열 중에서 숫자가 최대 두 개의 숫자로만 이루어진 부분 수열의 최대 길이를 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 푸는 가장 효율적인 방법은 &lt;b&gt;슬라이딩 윈도우 기법&lt;/b&gt;을 이용하는 것이다. 하지만 문제에서 주어진 제약 조건들을 잘 활용하면 조금은 더 간단한 방법으로 문제를 해결할 수 있다. 이 문제가 Division 1, Division 2에 동시에 출제된 것을 보면 다양한 접근 방법을 생각할 수 있게 의도된 문제로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 제약 조건을 활용하면 더욱 효과적이고 빠른 해결 방법을 찾을 수 있기 때문에, 문제를 풀기 전에&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;제약 조건을 먼저 확인하는 것이 중요하다는 이유를 잘 설명해 주는 모법적인(?) 문제라고 생각한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;조금은 거친 방법으로 문제를 풀기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서 주어진 두 개의 제약조건을 생각해보자. 첫 번째는 &lt;b&gt;수열을 이루고 있는 숫자는 1부터 9까지&lt;/b&gt;라는 것이다. 두 번째는 &lt;b&gt;최대 두 개의 숫자로만 이루어진 연속된 부분 수열의 최대 길이&lt;/b&gt;를 구해야 한다는 것이다. 이 두 가지 조건을 이용하면 우리는 조금은 거친 방법을 생각해 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 우리가 연속된 부분 수열을 이루고 있는 숫자 두 개를 미리 정하고, 그 조건을 만족하는 연속된 부분 수열의 최대 길이를 구하는 문제를 푼다고 생각해 보자. 주어진 수열에서 숫자 \(P_1\) 또는 \(P_2\)로 이루어진 연속된 부분 수열들 중 최대 길이를 구하는 코드는 아래와 같이 작성할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1718039159123&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int func(int p1, int p2) {
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        P[i] = (A[i] == p1 || A[i] == p2);
    }
    
    int ret = 0, cnt = 0;
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        if (P[i]) {
            cnt += 1;
        } else {
            cnt = 0;
        }
        
        ret = max(ret, cnt);
    }
    
    return ret;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 우리가 생각해봐야 하는 \(P_1\), \(P_2\)의 쌍의 개수는 수열을 이루고 있는 숫자가 최대 9개이므로 \({}_9 \mathrm{ C }_2\)이다. 함수 func의 시간 복잡도가 \(\mathcal{O}(N)\)이기 때문에 모든 경우에 대해 func 함수를 계산해도 주어진 제한시간 내에 충분히 들어올 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1718039582221&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int ans = 0;
    
for (int i = 1 ; i &amp;lt;= 9 ; ++i) {
    for (int j = i + 1 ; j &amp;lt;= 9 ; ++j) {
        ans = max(ans, func(i, j));
    }
}

printf(&quot;%d\n&quot;, ans);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;슬라이딩 윈도우 기법으로 문제 풀기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 이 문제를 효율적인 방법으로 풀 수 있는 &lt;b&gt;슬라이딩 윈도우 기법&lt;/b&gt;을 이용한 풀이를 알아보자. 어떠한 연속된 구간 [lo, hi]가 있을 때 이 구간을 이루고 있는 숫자의 개수가 두 개보다 많다면, hi를 증가시켜도 그 구간을 이루고 있는 숫자의 개수는 두 개보다 같거나 작아질 수 없다. 하지만 lo를 증가시키면 구간의 길이가 작아지므로 그 구간을 이루고 있는 숫자의 개수는 적어도 지금보다 같거나 혹은 더 적어질 것이다. 이 아이디어를 이용하면 lo와 hi를 조정해 \(\mathcal{O}(N)\)의 시간복잡도를 이용해 문제를 해결할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1718040096745&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int solve() {
    int lo = 0, ans = 0, dNums = 0;
    
    for (int hi = 0 ; hi &amp;lt; n ; ++hi) {
        ++F[A[hi]];
        
        if (F[A[hi]] == 1) {
            ++dNums;
        }
        
        while (dNums &amp;gt; 2) {
            --F[A[lo]];
            
            if (F[A[lo]] == 0) {
                --dNums;
            }
            
            ++lo;
        }
        
        ans = max(ans, hi - lo + 1);
    }
    
    return ans;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;만약 수열을 이루고 있는 수의 종류가 많아지거나, 수열의 길이가 지금보다 더 커진다면 위에서 소개한 완전 탐색과 비슷한 방법으로는 문제를 풀기 어렵지만 슬라이딩 기법을 이용하면 해결할 수 있다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 30804</category>
      <category>백준 30804</category>
      <category>슬라이딩 윈도우</category>
      <category>투 포인터</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/47</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-30804%EB%B2%88-%EA%B3%BC%EC%9D%BC-%ED%83%95%ED%9B%84%EB%A3%A8#entry47comment</comments>
      <pubDate>Tue, 11 Jun 2024 02:27:11 +0900</pubDate>
    </item>
    <item>
      <title>[백준 17845] 수강 과목</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17845-%EC%88%98%EA%B0%95-%EA%B3%BC%EB%AA%A9</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17845&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/17845&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 공부시간 \(N\)과 과목 수 \(K\)가 주어진다. 이어서 각 과목 \(i\)의 중요도 \(I_i\)와 필요한 공부시간 \(T_i\)가 주어진다. 최대 공부시간 \(N\)을 초과하지 않으면서 중요도 합이 최대가 되도록 과목을 선택할 때, 최대 중요도를 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 전형적인 배낭 문제(Knapsack Problem)로 동적 계획법(Dynamic Programming)을 이용해 풀 수 있다. 동적 계획법은 Top Down 방식과 Bottom Up 방식으로 풀 수 있는데, 여기서 두 가지 방법 모두를 이용해 문제를 푸는 방법을 소개한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Top Down 방식으로 문제 풀기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 계획법을 Top Down 방식으로 풀 때는 보통 재귀적인 방법을 사용한다. i번째 과목까지 고려했을 때, 남은 공부 시간 left를 이용해서 얻을 수 있는 최대 중요도를 얻을 수 있는 함수 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;func(idx, left)이 있다고 생각해 보자. 그렇다면 func(idx, left)는 아래와 같은 정의를 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SCR-20240611-btlz.png&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6I4tS/btsHTiLSvrm/z84hrS0ZmLYktZBJaxMc80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6I4tS/btsHTiLSvrm/z84hrS0ZmLYktZBJaxMc80/img.png&quot; data-alt=&quot;func(idx, left)의 점화식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6I4tS/btsHTiLSvrm/z84hrS0ZmLYktZBJaxMc80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6I4tS%2FbtsHTiLSvrm%2Fz84hrS0ZmLYktZBJaxMc80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;func(idx, left)의 점화식&quot; loading=&quot;lazy&quot; width=&quot;1696&quot; height=&quot;156&quot; data-filename=&quot;SCR-20240611-btlz.png&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;func(idx, left)의 점화식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 남은 공부 시간이 현재 과목의 공부 시간보다 크거나 같다면 현재 과목을 선택하는 것을 고려해 볼 수 있다. 하지만 남은 공부 시간이 현재 과목의 공부 시간보다 작다면 현재 과목은 선택하면 안 된다. 이 점화식을 메모이제이션 기법을 이용해 구현하면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1718035224020&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int func(int idx, int left) {
    if (idx == -1) {
        return 0;
    }
    
    int&amp;amp; ret = dp[idx][left];
    
    if (ret != -1) {
        return ret;
    }
    
    ret = func(idx - 1, left);
    
    if (left &amp;gt;= T[idx]) {
        ret = max(ret, func(idx - 1, left - T[idx]) + I[idx]);
    }
    
    return ret;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bottom Up 방식으로 문제 풀기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부 시간 k를 사용하여 얻을 수 있는 최대 중요도를 나타내는 DP 테이블 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;dp[k]가 있다고 생각해 보자. 각 과목에 대해, 현재 과목을 포함하는 경우와 포함하지 않는 경우를 고려하여 DP 테이블을 업데이트할 수 있다. 업데이트를 할 때는 N부터 \(T_i\)까지 역순으로 업데이트를 해줘야 한다는 것을 유의해야 한다. 만약 \(T_i\)부터 N까지 순차적으로 업데이트를 해준다면 해당 과목을 여러 번 선택할 수 있기 때문이다. 이를 코드로 구현한다면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1718035865266&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int solve() {
    memset(dp, -1, sizeof(dp));
    
    dp[0] = 0;
    
    for (int i = 0 ; i &amp;lt; k ; ++i) {
        for (int j = n ; j &amp;gt;= T[i] ; --j) {
            if (dp[j - T[i]] != -1) {
                dp[j] = max(dp[j], dp[j - T[i]] + I[i]);
            }
        }
    }
    
    int ans = 0;
    
    for (int i = 0 ; i &amp;lt;= n ; ++i) {
        ans = max(ans, dp[i]);
    }
    
    return ans;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bottom Up 방식은 기저사례부터 차근차근 쌓아나가기 때문에 메모리 사용량을 절약할 수 있는 장점이 있지만 Top Down 방식이 가지는 직관적인 구현이란 장점도 있으니 상황에 맞게 잘 선택하면 될 것 같다.&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 17845</category>
      <category>DP</category>
      <category>동적 계획법</category>
      <category>백준 17845</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/46</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17845-%EC%88%98%EA%B0%95-%EA%B3%BC%EB%AA%A9#entry46comment</comments>
      <pubDate>Tue, 11 Jun 2024 01:22:06 +0900</pubDate>
    </item>
    <item>
      <title>빠르게 배열의 부분 합을 구하는 Prefix Sum 알고리즘</title>
      <link>https://www.ohnimdev.com/entry/%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%B0%B0%EC%97%B4%EC%9D%98-%EB%B6%80%EB%B6%84-%ED%95%A9%EC%9D%84-%EA%B5%AC%ED%95%98%EB%8A%94-Prefix-Sum-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Prefix Sum 알고리즘이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 문제를 풀다 보면 배열에서 특정 구간의 합을 구해야 하는 문제들을 종종 만나곤 한다. 한 번만 계산해야 한다면 반복문을 통해 \(O(N)\)의 시간 복잡도로 해결할 수 있지만, 여러 번 반복해서 계산해야 한다면 조금 더 효율적인 알고리즘이 필요하다. 이때 사용할 수 있는 알고리즘이 &lt;b&gt;Prefix Sum&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prefix Sum 알고리즘은 배열의 첫 번째 원소부터 각 원소들까지의 합을 미리 계산해두는 알고리즘이다. 예를 들어, 배열 [1, 2, 3, 4, 5]가 있을 때 첫 번째 원소의 Prefix Sum은 1, 두 번째 원소의 Prefix Sum은 1 + 2 = 3, 세 번째 원소의 Prefix Sum은 1 + 2 + 3 = 6 이 된다. 이렇게 미리 배열의 모든 원소들의 Prefix Sum을 구해 놓으면 배열의 특정 범위 내 합을 빠르게 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1차원 배열에서 구간 합 빠르게 구하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Prefix Sum 배열 계산하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차원 배열에서 Prefix Sum은 \(psum[i] = psum[i - 1] + arr[i]\) 공식을 이용해 계산할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717430969285&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; getPartialSumVector(const vector&amp;lt;int&amp;gt;&amp;amp; arr) {
    vector&amp;lt;int&amp;gt; psum(arr.size());
    
    psum[0] = arr[0];
    
    for (int i = 1 ; i &amp;lt; arr.size() ; ++i) {
        psum[i] = psum[i - 1] + arr[i];
    }
    
    return psum;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구간 합 구하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Prefix Sum 배열을 만들고 나면 원본 배열의 특정 범위 \([l, r]\)의 합을 다음과 같이 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$sum(l, r) = psum[r] - psum[l - 1]$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(psum[r]\)은 배열의 첫 번째 원소부터 \(r\)번째 원소까지의 합을 나타낸다. \(psum[l - 1]\)은 배열의 첫 번째 원소부터 \(l - 1\)번째 원소까지의 합을 나타낸다. 따라서 \(psum[r]\)에서 \(psum[l - 1]\)을 빼면, 배열의 \(l\)번째 원소부터 \(r\)번째 원소의 합만 남게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1717431503291&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int getSumOf(const vector&amp;lt;int&amp;gt;&amp;amp; psum, int l, int r) {
    if (l == 0) {
        return psum[r];
    }
    
    return psum[r] - psum[l - 1];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(O(N)\)의 시간 복잡도를 이용해 Prefix Sum 배열을 만들고 나면 이후 배열의 구간 합을 구하는 연산은 \(O(1)\)에 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2차원 배열에서 구간 합 빠르게 구하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Prefix Sum 배열 계산하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 배열의 Prefix Sum의 정의는 1차원 배열의 Prefix Sum 정의와는 조금 다르다. 2차원 배열에서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;\(y_{ij}\)의 Prefix Sum은&amp;nbsp;&lt;/span&gt;(0, 0)에서 (i, j)까지의 합이라고 정의한다. 쉽게 말해서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2차원 배열의 (0, 0)부터 (i, j)까지 사각형을 그린 뒤 사각형 안에 포함되는 원소들을 다 합친것을 의미한다. 2차원 배열의 Prefix Sum 배열은 \(psum[i][j] = arr[i][j] + psum[i - 1][j] + psum[i][j - 1] - psum[i - 1][j - 1]\) 공식을 통해 계산할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;1204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n9xX5/btsHNbFj9ZL/PWKCll5E9GYEV0mK8aJvlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n9xX5/btsHNbFj9ZL/PWKCll5E9GYEV0mK8aJvlK/img.png&quot; data-alt=&quot;2차원 배열의 Prefix Sum 배열&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n9xX5/btsHNbFj9ZL/PWKCll5E9GYEV0mK8aJvlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn9xX5%2FbtsHNbFj9ZL%2FPWKCll5E9GYEV0mK8aJvlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;2차원 배열의 Prefix Sum 배열&quot; loading=&quot;lazy&quot; width=&quot;270&quot; height=&quot;271&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;1204&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2차원 배열의 Prefix Sum 배열&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수식으로만 보면 다소 헷갈릴 수 있어 그림과 함께 설명을 다시 한다. (i, j)의 Prefix Sum을 계산할 때 이전에 계산한 Prefix Sum 값을 재활용한다. 다음과 같은 영역을 생각해보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분홍색 영역: (0, 0)부터 (i, j - 1)까지의 합&lt;/li&gt;
&lt;li&gt;초록색 영역: (0, 0)부터 (i - 1, j)까지의 합&lt;/li&gt;
&lt;li&gt;검은색 영역: 분홍색 영역과 초록색 영역이 겹치는 부분&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(i, j)의 Prefix Sum을 계산하려면 분홍색 영역과 초록색 영역을 더한 뒤, 중복해서 더해진 검은색 영역을 빼야 한다. 마지막으로 (i, j)의 값을 더해주면 (0, 0)부터 (i, j)까지의 Prefix Sum을 계산할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1717434351391&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; getPartialSumVector(const vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; arr) {
    int rows = arr.size();
    int cols = arr[0].size();
    
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; psum(rows, vector&amp;lt;int&amp;gt;(cols));
    
    for (int i = 0 ; i &amp;lt; rows ; ++i) {
        for (int j = 0 ; j &amp;lt; cols ; ++j) {
            psum[i][j] = arr[i][j];
            
            if (i &amp;gt; 0) {
                psum[i][j] += psum[i - 1][j];
            }
            
            if (j &amp;gt; 0) {
                psum[i][j] += psum[i][j - 1];
            }
            
            if (i &amp;gt; 0 &amp;amp;&amp;amp; j &amp;gt; 0) {
                psum[i][j] -= psum[i - 1][j - 1];
            }
        }
    }
    
    return psum;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구간 합 구하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prefix Sum 배열을 계산할 때와 비슷한 방법을 이용해 2차원 배열에서 (l1, r1) ~ (l2, r2)의 구간 합을 구할 수 있다. 이를 수식으로 표현하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$sum(l1, r1, l2, r2) = psum[l2][r2] - psum[l1 - 1][r2] - psum[l2][r1 - 1] + psum[l1 - 1][r1 - 1]$$&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;1582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWKvf9/btsHNudALoS/bz2WZMWkEzWzbPZYDPFkLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWKvf9/btsHNudALoS/bz2WZMWkEzWzbPZYDPFkLk/img.png&quot; data-alt=&quot;2차원 배열의 구간 합&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWKvf9/btsHNudALoS/bz2WZMWkEzWzbPZYDPFkLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWKvf9%2FbtsHNudALoS%2Fbz2WZMWkEzWzbPZYDPFkLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;2차원 배열의 구간 합&quot; loading=&quot;lazy&quot; width=&quot;271&quot; height=&quot;272&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;1582&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2차원 배열의 구간 합&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구간 합을 구하는 과정 역시 수식으로만 보면 헷갈릴 수 있어 그림과 함께 다시 봐보자. (l1, r1) ~ (l2, r2)의 구간 합을 구하는 과정에 사용할 Prefix Sum을 다음과 같이 생각해 보자&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빨간색 영역: (0, 0)에서 (l2, r2)까지의 합&lt;/li&gt;
&lt;li&gt;초록색 영역: (0, 0)에서 (l2, r1 - 1)까지의 합&lt;/li&gt;
&lt;li&gt;파란색 영역: (0, 0)에서 (l1 - 1, r2)까지의 합&lt;/li&gt;
&lt;li&gt;검은색 영역: 초록색 영역과 파란색 영역이 겹치는 부분&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구간 (l1, r1) ~ (l2, r2)의 합을 구하기 위해서는 빨간색 영역에서 초록색 영역과 파란색 영역을 뺀 뒤, 두 번  빠진 검은색 영역을 한 번 더해줘야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1717437016756&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int getSumOf(const vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; psum, int l1, int r1, int l2, int r2) {
    int ret = psum[l2][r2];
    
    if (l1 &amp;gt; 0) {
        ret -= psum[l1 - 1][r2];
    }
    
    if (r1 &amp;gt; 0) {
        ret -= psum[l2][r1 - 1];
    }
    
    if (l1 &amp;gt; 0 &amp;amp;&amp;amp; r1 &amp;gt; 0) {
        ret += psum[l1 - 1][r1 - 1];
    }
    
    return ret;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(O(NM)\)의 시간 복잡도를 이용해 2차원 배열의 Prefix Sum 배열을 만들고 나면 그다음 특정 구간의 합을 구하는 연산은 \(O(1)\)에 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Prefix Sum을 이용해 풀 수 있는 문제들&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: left;&quot; title=&quot;[백준 2167번] 2차원 배열의 합&quot; href=&quot;https://www.acmicpc.net/problem/2167&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[백준 2167번] 2차원 배열의 합&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title=&quot;[백준 11659번] 구간 합 구하기 4&quot; href=&quot;https://www.acmicpc.net/problem/11659&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[백준 11659번] 구간 합 구하기 4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title=&quot;[백준 2559번] 수열&quot; href=&quot;https://www.acmicpc.net/problem/2559&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[백준 2559번] 수열&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>알고리즘/etc.</category>
      <category>partial sum</category>
      <category>Prefix Sum</category>
      <category>구간 합</category>
      <category>누적 합</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/45</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EB%B0%B0%EC%97%B4%EC%9D%98-%EB%B6%80%EB%B6%84-%ED%95%A9%EC%9D%84-%EA%B5%AC%ED%95%98%EB%8A%94-Prefix-Sum-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98#entry45comment</comments>
      <pubDate>Tue, 4 Jun 2024 03:19:07 +0900</pubDate>
    </item>
    <item>
      <title>[백준 2417번] 정수 제곱근</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-2417%EB%B2%88-%EC%A0%95%EC%88%98-%EC%A0%9C%EA%B3%B1%EA%B7%BC</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2417&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2417&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 주어진 N의 제곱근을 소수점에서 올림 한 값을 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함정이 가득한 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 자체는 상당히 간단해 이제 막 PS에 입문한 사람들이 연습용으로 풀기 좋은 문제로 보인다. 그리고 문제 번호도 2000번 대여서 뭔가 연습문제 같은 느낌도 상당히 든다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;???: 제곱근을 수하고 소수점에서 올림을 하라니?!  sqrt 함수를 이용하면 그냥 풀 수 있는 문제잖아!&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 생각의 흐름을 거치고 나면 아래와 같은 코드를 작성하고 제출을 누르게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1716996371264&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;cmath&amp;gt;

using namespace std;

int main() {
    unsigned long long n;
    
    cin &amp;gt;&amp;gt; n;
    
    unsigned long long ans = sqrt(n);
    
    if (ans * ans == n) {
        cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; endl;
    } else {
        cout &amp;lt;&amp;lt; ans + 1 &amp;lt;&amp;lt; endl;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 나에게 주어지는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;틀렸습니다&lt;/b&gt;&lt;/span&gt; 목걸이...!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다... 이 문제는 누구나 풀 수 있는 문제인 것처럼 사람들을 유혹하지만 사실은 함정이 가득한 악마의 문제인 것이다. 이제 이 문제를 어떻게 풀면 좋을지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sqrt 함수는&amp;nbsp; 내부적으로 float, double, long double 자료형을 이용한다.  이런 자료형들을 부동 소수점을 사용하고 약 15~19자리까지 정확하다고 알려져 있다. 그런데 입력으로 주어지는 N의 최댓값은 \(2^{63} - 1\)이고 이는 20자리이다. 즉 정확하게 계산할 수 있는 범위를 넘어서기 때문에 정답을 구할 수 없는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 정수 제곱근을 구하기 위해서는 직접 계산하는 방법이 필요하다. 여기서는 이분 탐색을 이용한 방법을 설명한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;lo를 0, hi를 적당히 큰 값으로 설정한다.&lt;/li&gt;
&lt;li&gt;lo와 hi의 중앙값인 mid를 계산한다.&lt;/li&gt;
&lt;li&gt;mid의 제곱이 N보다 작으면 lo를 mid + 1로 설정한다.&lt;/li&gt;
&lt;li&gt;mid의 제곱이 N보다 크거나 같으면 hi를 mid로 설정한다.&lt;/li&gt;
&lt;li&gt;lo가 hi보다 커질 때까지 2, 3, 4를 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 이분 탐색을 이용해 sqrt 함수의 한계를 극복하고 정확한 정수 제곱근을 구하면 &lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;맞았습니다!!&lt;/b&gt;&lt;/span&gt; 목걸이를 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

unsigned long long n;

int main() {
    scanf(&quot;%llu&quot;, &amp;amp;n);
    
    unsigned long long lo = 0, hi = 4294967296;
    
    while (lo &amp;lt; hi) {
        long long mid = (lo + hi) / 2;
        
        if (mid * mid &amp;gt;= n) {
            hi = mid;
        } else {
            lo = mid + 1;
        }
    }
    
    printf(&quot;%llu\n&quot;, lo);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj</category>
      <category>boj 2417번</category>
      <category>백준</category>
      <category>백준 2417번</category>
      <category>이분탐색</category>
      <category>정수 제곱근</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/43</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-2417%EB%B2%88-%EC%A0%95%EC%88%98-%EC%A0%9C%EA%B3%B1%EA%B7%BC#entry43comment</comments>
      <pubDate>Thu, 30 May 2024 01:10:01 +0900</pubDate>
    </item>
    <item>
      <title>[백준 15989번] 1, 2, 3, 더하기 4</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-15989%EB%B2%88-1-2-3-%EB%8D%94%ED%95%98%EA%B8%B0-4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15989&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/15989&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정수 N이 주어졌을 때, 1, 2, 3의 합으로 N을 나타내는 경우의 수를 찾는 문제이다. 순서만 다른 것은 같은 경우라는 것을 주의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 전형적인 동적 계획법(dynamic programming, 이하 DP)로 해결할 수 있다. DP는 상향식과 하향식 두 가지 방법으로 구현할 수 있는데 보통은 하향식 방법이 구현이 쉽고 직관적이다. 하지만 슬라이딩 윈도우, 컨벡스 헐 트릭등 다양한 DP 최적화 전략을 이용하기 위해서라면 상향식 방법도 이용할 줄 알아야 하기 때문에 두 가지 방법으로 모두 구현을 해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;하향식으로 문제 풀기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상향식으로 문제를 풀 때 가장 먼저 해야 할 일은 재귀적으로 문제를 해결할 수 있는 완전 탐색 코드를 작성하는 것이다. 다음과 같은 함수를 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(\text{calc}(x, last) =\) \(x\)를 \(last\)이상 3 이하의 숫자의 합으로 로 표현할 수 있는 경우의 수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서만 다른 경우는 같은 경우의 수로 계산해야 할 때는 고르는 순서를 한쪽 방향으로 강제하면 쉽게 해결할 수 있기 때문에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;함수의 두 번째 인자로 \(last\)를 넣어줬다. 이제 이 재귀 함수는 다시 또 다른 재귀함수들로 표현할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(\text{calc}(x, \text{last}) = \displaystyle\sum_{i=\text{last}}^{3} \text{calc}(x-i, i)\) \(\text{where}\) \(x - i \geq 0\)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의한 재귀함수를 코드로 옮기면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1716823742131&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int calc(int x, int last) {
    if (x == 0) {
        return 1;
    }
    
    int ret = 0;
    
    for (int i = last ; i &amp;lt;= 3 ; ++i) {
        if (x - i &amp;gt;= 0) {
            ret += calc(x - i , i);
        }
    }
    
    return ret;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현한 재귀함수에 메모이제이션 패턴을 적용하면 상향식 DP로 문제를 풀게 된다. 전체 코드는 아래 더보기를 클릭하면 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1716823877647&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

int t, n, cache[10010][4];

int calc(int x, int last) {
    if (x == 0) {
        return 1;
    }
    
    int&amp;amp; ret = cache[x][last];
    
    if (ret != -1) {
        return ret;
    }
    
    ret = 0;
    
    for (int i = last ; i &amp;lt;= 3 ; ++i) {
        if (x - i &amp;gt;= 0) {
            ret += calc(x - i , i);
        }
    }
    
    return ret;
}

int main() {
    scanf(&quot;%d&quot;, &amp;amp;t);
    
    memset(cache, -1, sizeof(cache));
    
    while (t--) {
        scanf(&quot;%d&quot;, &amp;amp;n);
        
        printf(&quot;%d\n&quot;, calc(n, 1));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상향식으로 문제 풀기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상향식으로 문제를 풀 때는 점화식을 먼저 세워야 한다. 그런데 이 문제 같은 경우 하향식으로 문제를 풀 때 만들었던 재귀함수의 정의를 상향식으로 문제를 풀 때에도 그대로 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716824470558&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dp[0][1] = 1;

for (int i = 1 ; i &amp;lt; 10010 ; ++i) {
    for (int j = 1 ; j &amp;lt;= 3 ; ++j) {
        for (int k = j ; k &amp;lt;= 3 ; ++k) {
            if (i - k &amp;gt;= 0) {
                dp[i][k] += dp[i - k][j];
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이대로 끝내기에는 뭔가 좀 아쉬운 느낌이 든다. 상향식으로 문제를 풀 경우에는 DP 최적화를 할 수 있다고 했는데, 이 문제는 어떻게 DP 최적화를 할 수 있을까? 하향식으로 문제를 풀 때 \(last\) 변수를 이용해 사용하는 숫자의 순서를 강제하고 있었다. 상향식으로 문제를 풀 때 이 순서 강제를 다른 방법으로 할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상향식으로 접근을 하게 되면, 각 숫자 1, 2, 3을 사용 순서를 반복문 내에서 강제할 수 있기 때문에 \(last\) 변수가 없어도 자연스럽게 중복을 제거할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1716824975831&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dp[0] = 1;

for (int i = 1 ; i &amp;lt;= 3 ; ++i) {
    for (int j = i ; j &amp;lt; 10010 ; ++j) {
        dp[j] += dp[j - i];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 \(last\) 변수가 없기 때문에 2차원 배열로 관리하던 dp 테이블을 1차원으로 줄일 수 있어 메모리 사용 또한 효율적으로 할 수 있다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;전체 코드는 아래 더보기를 클릭하면 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1716825105872&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

int t, n, dp[10010];

int main() {
    scanf(&quot;%d&quot;, &amp;amp;t);
    
    dp[0] = 1;
    
    for (int i = 1 ; i &amp;lt;= 3 ; ++i) {
        for (int j = i ; j &amp;lt; 10010 ; ++j) {
            dp[j] += dp[j - i];
        }
    }
    
    while (t--) {
        scanf(&quot;%d&quot;, &amp;amp;n);
        
        printf(&quot;%d\n&quot;, dp[n]);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj</category>
      <category>boj 15989번</category>
      <category>dynamic programming</category>
      <category>동적 계획법</category>
      <category>백준</category>
      <category>백준 15989번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/42</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-15989%EB%B2%88-1-2-3-%EB%8D%94%ED%95%98%EA%B8%B0-4#entry42comment</comments>
      <pubDate>Tue, 28 May 2024 00:57:59 +0900</pubDate>
    </item>
    <item>
      <title>[백준 17086번] 아기 상어 2</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17086%EB%B2%88-%EC%95%84%EA%B8%B0-%EC%83%81%EC%96%B4-2</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17086&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/17086&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임의의 점 \((i, j)\)의 안전거리는 \((i, j)\)에서 시작해서 아기 상어가 있는 곳까지의 최소 거리를 의미한다. 여기서 거리는 인접한 8방향(상하좌우 및 대각선)으로 이동한 칸 수로 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;격자판 위의 모든 점들 중에서 안전거리가 최대인 값을 찾아 출력하는 것이 이 문제의 목표이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; 기본적인 풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너비 우선 탐색(이하 BFS)을 이용하면 임의의 점 \((i, j)\)에서 가장 가까운 아기 상어까지의 거리를 계산할 수 있다. 그리고 N과 M의 크기가 작으므로 모든 점들에 대해서 BFS를 한 번씩 돌려도 제한시간 내에 충분히 답을 구할 수 있다. 아래 더보기 버튼을 누르면 이 방법으로 푼 코드를 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1716654837549&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

const int dx[] = {-1, -1, -1, 0, 1, 1, 1, 0};
const int dy[] = {-1, 0, 1, 1, 1, 0, -1, -1};
int n, m, A[51][51], D[51][51];

bool isPossible(int x, int y) {
    if (x == -1 || x == n || y == -1 || y == m) {
        return false;
    }
    
    return true;
}

int bfs(pair&amp;lt;int, int&amp;gt; src) {
    queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q;
    memset(D, -1, sizeof(D));
    
    q.push(src);
    D[src.first][src.second] = 0;
    
    while (!q.empty()) {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();
        
        if (A[x][y] == 1) {
            return D[x][y];
        }
        
        for (int i = 0 ; i &amp;lt; 8 ; ++i) {
            int nx = x + dx[i], ny = y + dy[i];
            
            if (isPossible(nx, ny) &amp;amp;&amp;amp; D[nx][ny] == -1) {
                q.push({ nx, ny });
                D[nx][ny] = D[x][y] + 1;
            }
        }
    }
    
    return 0;
}

int main() {
    scanf(&quot;%d%d&quot;, &amp;amp;n, &amp;amp;m);
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        for (int j = 0 ; j &amp;lt; m ; ++j) {
            scanf(&quot;%d&quot;, &amp;amp;A[i][j]);
        }
    }
    
    int ans = 0;
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        for (int j = 0 ; j &amp;lt; m ; ++j) {
            if (A[i][j] == 0) {
                ans = max(ans, bfs({ i, j }));
            }
        }
    }
    
    printf(&quot;%d\n&quot;, ans);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;조금 더 빠르게 풀어보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;격자판 위의 모든 점에 대해서 각각 BFS를 돌리는 것이 비효율적이라고 생각할 수 있다. 만약 문제에서 주어지는 N과 M의 최대 크기가 지금보다 더 컸다면 위 방법으로는 이 문제를 풀 수 없었을 것이다. 조금 더 빠르게 풀 수 있는 방법은 없을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS를 돌리기 위해서 암시적으로 모델링 한 그래프가 무향 그래프라는 점을 생각해 보자. 그러면 각 점에서 아기 상어까지의 최단 거리와 아기 상어에서 각 점까지의 최단 거리가 같다는 것을 알 수 있다. 따라서 아기 상어가 있는 모든 지점들을 시작점으로 잡고 BFS를 돌리면 BFS 한 번에 모든 지점들의 안전거리를 구할 수 있다. 아래 더보기 버튼을 누르면 더 빠르게 문제를 해결하는 코드를 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1716655569281&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

const int dx[] = {-1, -1, -1, 0, 1, 1, 1, 0};
const int dy[] = {-1, 0, 1, 1, 1, 0, -1, -1};
int n, m, A[51][51], D[51][51];

bool isPossible(int x, int y) {
    if (x == -1 || x == n || y == -1 || y == m) {
        return false;
    }
    
    return true;
}

int main() {
    scanf(&quot;%d%d&quot;, &amp;amp;n, &amp;amp;m);
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        for (int j = 0 ; j &amp;lt; m ; ++j) {
            scanf(&quot;%d&quot;, &amp;amp;A[i][j]);
        }
    }
    
    memset(D, -1, sizeof(D));
    queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q;
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        for (int j = 0 ; j &amp;lt; m ; ++j) {
            if (A[i][j] == 1) {
                D[i][j] = 0;
                q.push({ i, j });
            }
        }
    }
    
    while (!q.empty()) {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();
        
        for (int i = 0 ; i &amp;lt; 8 ; ++i) {
            int nx = x + dx[i], ny = y + dy[i];
            
            if (isPossible(nx, ny) &amp;amp;&amp;amp; D[nx][ny] == -1) {
                D[nx][ny] = D[x][y] + 1;
                q.push({ nx, ny });
            }
        }
    }
    
    int ans = 0;
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        for (int j = 0 ; j &amp;lt; m ; ++j) {
            ans = max(ans, D[i][j]);
        }
    }
    
    printf(&quot;%d\n&quot;, ans);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>BFS</category>
      <category>boj</category>
      <category>boj 17086번</category>
      <category>그래프</category>
      <category>백준</category>
      <category>백준 17086번</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/40</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17086%EB%B2%88-%EC%95%84%EA%B8%B0-%EC%83%81%EC%96%B4-2#entry40comment</comments>
      <pubDate>Sun, 26 May 2024 02:46:08 +0900</pubDate>
    </item>
    <item>
      <title>[백준 9376번] 탈옥</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-9376%EB%B2%88-%ED%83%88%EC%98%A5</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9376&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/9376&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 죄수 A, B가 최소한의 문을 열어 감옥에서 탈출하려고 한다. 한 죄수가 문을 열면, 그 문은 계속 열려 있어서 다른 죄수도 그 문을 이용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(원래 문제에서는 감옥 밖에 있는 상근이가 특별한 기술을 이용해 문을 열어준다고 되어 있지만, 문제를 더 쉽게 이해할 수 있게 내용을 살짝 바꿨다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 떠오르는 생각은 두 죄수의 상태를 동시에 관리하는 것이다. 하지만 죄수가 있을 수 있는 장소는 1만 곳이고, 이를 두 죄수의 상태로 표현하면 1억 가지의 경우의 수가 생긴다. 여기에 탈출을 위해 이용한 문의 개수까지 고려한다면 이 방법은 문제를 제한 시간 내에 풀 수 있는 방법이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;경우의 수 나누기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 풀기 위해서는 두 죄수가 어떤 전략을 가지고 움직여야 최적해를 찾을 수 있을지 경우의 수를 나눠야 한다. 죄수 A와 B는 최적해를 위해 아래 두 가지 전략 중에 하나를 선택할 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;죄수 A와 B가 따로 탈출한다.&lt;/li&gt;
&lt;li&gt;죄수 A와 B는 어느 한 점에 만나 함께 탈출한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 전략을 선택했을 경우, 최적해가 될 수 있다는 것은 직관적으로 이해할 수 있다. 하지만 2번 전략의 경우, 반드시 한 지점에서 만나서 같이 움직여야 최적해가 된다는 점이 와닿지 않을 수 있다. 그러면 A와 B가 만난 뒤 따로 움직이는 경우가 최적해라고 가정을 해보자. A와 B가  만난 다음, A가  사용한 문들을 B가 사용하지 않고 따로 움직였을 때  최적해가 된다면, 그냥 A가 B를 따라가면 더 좋은 최적해가 되지 않을까? 즉 이 가설 자체가 모순이기 때문에 A와 B가 만나면 항상 같이 다니는 것이 최적해라는 것을  알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 최적해는 두 가지 경우 중 하나에서 나온다는 것을 알았으니, 이제 각각의 경우에서 최적해를 구한 다음에 그중 더 작은 값을 출력하면 문제를 풀 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그래프 모델링 하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최적해를 구하기 전에 문제에서 주어진 감옥을 그래프로 모델링 해보자. 2차원 행렬을 그래프로 모델링할 때 1차원으로 압축시키면 문제를 더 쉽게 풀 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1716562420539&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int getIdx(int i, int j) {
    return i * w + j;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 각 노드들 간의 관계를 가중치가 있는 그래프로 표현할 수 있다. 빈칸에서 문으로 이동할 때는 가중치를 1로 두고, 빈칸에서 빈칸, 문에서 빈칸으로 이동할 때는 가중치를 0으로 모델링하면 탈출할 때 최소한의 문을 사용하는 조건을 최단거리를 구하는 문제로 바꿔 풀 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 A와 B가 탈출했다는 것을 어떻게 탐지할 수 있을까? 벽이 아닌 모서리에 도착했을 때 A와 B가 탈출했다고 판단을 할 수 있다. 그러나 탈출 지점을 의미하는 가상의 노드를 하나 만들어 벽이 아닌 모서리들과 연결하면, 하나의 노드만 보고도 A와 B가 탈출했다는 것을 알 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다익스트라를 이용해 답을 구하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다익스트라 알고리즘을 이용해 최단거리를 구한 다음 각각의 경우에 수에 대해 최적해를 계산해 준다. 먼저 A와 B가 따로 탈출하는 경우를 계산해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1716563626515&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int case1_ans = dijkstra(A_IDX, EXIT_IDX) + dijkstra(B_IDX, EXIT_IDX);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음은 A와 B가 한 지점에 만나서 같이 탈출하는 경우의 최적해를 구해야 한다. A와 B가 어느 곳에서 만나야 최적의 경우인지 모르니 만날 수 있는 모든 곳에 대해서 최적해를 계산해 준다. 만약 A와 B가 만나는 곳이 문이라면 한 명만 문을 열면 되니 1을 빼준다.&lt;/p&gt;
&lt;pre id=&quot;code_1716564050082&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int case2_ans = INF;

for (int i = 0 ; i &amp;lt; h ; ++i) {
    for (int j = 0 ; j &amp;lt; w ; ++j) {
        if (A[i][j] == '*') {
            continue;
        }
        
        int MEETING_IDX = getIdx(i, j);
        
        int dist = dijkstra(A_IDX, MEETING_IDX) + dijkstra(B_IDX, MEETING_IDX) + dijkstra(MEETING_IDX, EXIT_IDX);
        
        if (A[i][j] == '#') {
            dist -= 1;
        }
        
        case2_ans = min(case2_ans, dist);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 끝나면 좋겠지만, 이대로 제출하면 시간초과가 발생한다. 만날 수 있는 지점이 1만 개나 되기 때문에 다익스트라를 1만 번이나 돌려야 하며, 거기다 테스트 케이스까지 있으니 시간초과가 나는 것은 당연한 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다익스트라 결과 배열을 이용해 답을 구하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A와 B가 만날 수 있는 지점마다 다익스트라를 매번 새롭게 돌려야 해서 시간초과가 발생한다. A와 B가 만날 수 있는 모든 지점들에 대해서 탈출 지점까지 가는 최단 거리를 빠르게 구할 수 있는 방법이 없을까? 우리가 모델링한 그래프가 무향 그래프이기 때문에 출발 지점과 도착 지점을 반대로 생각해도 최단거리가 똑같다는 점을 이용할 수 있다. 탈출 지점에서 다익스트라를 한 번 돌리면 탈출 지점에서 시작해 도달할 수 있는 모든 지점들에 대한 최단거리를 구할 수 있고, 결국 이 값들이 각 지점에서 탈출 지점으로 가는 최단 거리가 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1716565617303&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; startFromA = dijkstra2(A_IDX);
vector&amp;lt;int&amp;gt; startFromB = dijkstra2(B_IDX);
vector&amp;lt;int&amp;gt; startFromExit = dijkstra2(EXIT_IDX);

int ans = startFromA[n - 1] + startFromB[n - 1];

for (int i = 0 ; i &amp;lt; h ; ++i) {
    for (int j = 0 ; j &amp;lt; w ; ++j) {
        if (A[i][j] == '*') {
            continue;
        }
        
        int MEETING_IDX = getIdx(i, j);
        
        int dist = startFromA[MEETING_IDX] + startFromB[MEETING_IDX] + startFromExit[MEETING_IDX];
        
        if (A[i][j] == '#') {
            dist -= 2;
        }
        
        ans = min(ans, dist);
    }
}

printf(&quot;%d\n&quot;, ans);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 가지 주의할 점은 A와 B가 한 지점에서 만나서 같이 움직일 때의 최적해를 구할 때, 만나는 곳이 문이라면 1이 아니라 2를 빼줘야 한다는 것이다. 왜냐면 이 전과 다르게 탈출 지점에서 만나는 곳까지 오는 것으로 계산했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어렵다고 생각할 수 있는 지점을 최대한 쉽게  설명하려고 하다보니 생각했던 것보다 글이 너무 길어졌다. 설명이 길어진 만큼 누군가에게는 쉽게 전달이 됐으면 좋겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;

using namespace std;

const int INF = 10000000;
const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, -1, 0, 1};
int t, h, w, n;
char A[110][110];
vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; nodes;

int getIdx(int i, int j) {
    return i * w + j;
}

void buildGraph() {
    nodes.assign(n, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;());
    
    for (int i = 0 ; i &amp;lt; h ; ++i) {
        for (int j = 0 ; j &amp;lt; w ; ++j) {
            if (A[i][j] == '*') {
                continue;
            }
            
            int here = getIdx(i, j);
            
            for (int k = 0 ; k &amp;lt; 4 ; ++k) {
                int ni = i + dx[k], nj = j + dy[k];
                
                if (ni == -1 || ni == h || nj == -1 || nj == w) {
                    nodes[here].push_back({ n - 1, 0 });
                    nodes[n - 1].push_back({ here, A[i][j] == '#' ? 1 : 0 });
                } else if (A[ni][nj] != '*') {
                    int there = getIdx(ni, nj);
                    nodes[here].push_back({ there, A[ni][nj] == '#' ? 1 : 0 });
                }
            }
        }
    }
}

vector&amp;lt;int&amp;gt; dijkstra2(int src) {
    vector&amp;lt;int&amp;gt; dist(n, INF);
    priority_queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; pq;
    
    dist[src] = 0;
    pq.push({ 0, src });
    
    while (!pq.empty()) {
        int cost = -pq.top().first;
        int here = pq.top().second;
        pq.pop();
        
        if (dist[here] &amp;lt; cost) {
            continue;
        }
        
        for (const auto&amp;amp; node: nodes[here]) {
            int there = node.first;
            int nextDist = cost + node.second;
            
            if (dist[there] &amp;gt; nextDist) {
                dist[there] = nextDist;
                pq.push({ -nextDist, there });
            }
        }
    }
    
    return dist;
}

int main() {
    freopen(&quot;input.txt&quot;, &quot;r&quot;, stdin);
    scanf(&quot;%d&quot;, &amp;amp;t);
    
    while (t--) {
        scanf(&quot;%d%d&quot;, &amp;amp;h, &amp;amp;w);
        
        n = h * w + 1;

        int A_IDX = -1, B_IDX = -1, EXIT_IDX = n - 1;
        
        for (int i = 0 ; i &amp;lt; h ; ++i) {
            scanf(&quot;%s&quot;, A[i]);
            
            for (int j = 0 ; j &amp;lt; w ; ++j) {
                if (A[i][j] == '$') {
                    if (A_IDX == -1) {
                        A_IDX = getIdx(i, j);
                    } else {
                        B_IDX = getIdx(i, j);
                    }
                }
            }
        }
        
        buildGraph();
        
        vector&amp;lt;int&amp;gt; startFromA = dijkstra2(A_IDX);
        vector&amp;lt;int&amp;gt; startFromB = dijkstra2(B_IDX);
        vector&amp;lt;int&amp;gt; startFromExit = dijkstra2(EXIT_IDX);
        
        int ans = startFromA[n - 1] + startFromB[n - 1];
        
        for (int i = 0 ; i &amp;lt; h ; ++i) {
            for (int j = 0 ; j &amp;lt; w ; ++j) {
                if (A[i][j] == '*') {
                    continue;
                }
                
                int MEETING_IDX = getIdx(i, j);
                
                int dist = startFromA[MEETING_IDX] + startFromB[MEETING_IDX] + startFromExit[MEETING_IDX];
                
                if (A[i][j] == '#') {
                    dist -= 2;
                }
                
                ans = min(ans, dist);
            }
        }

        printf(&quot;%d\n&quot;, ans);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj</category>
      <category>boj 9376번</category>
      <category>그래프</category>
      <category>다익스트라</category>
      <category>백준</category>
      <category>백준 9376번</category>
      <category>최단거리</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/39</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-9376%EB%B2%88-%ED%83%88%EC%98%A5#entry39comment</comments>
      <pubDate>Thu, 23 May 2024 02:51:44 +0900</pubDate>
    </item>
    <item>
      <title>[백준 10021번] Watering the Fields</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-10021%EB%B2%88-watering-the-fields</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/10021&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/10021&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;농부 John은 가뭄 때문에 N개의 농지에 물을 공급하기 위한 수로를 설치하려고 한다. 각 농지는 2차원 평면 위의 점 \((x_i, y_i)\)로 나타내며, 서로 다른 두 농지 \(i\)와 \(j\)를 연결하는 수로를 건설하는 비용은 \((x_i - x_j)^2 + (y_i - y_j)^2\)으로 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &lt;b&gt;모든 농지가 서로 물을 공급받을 수 있도록 수로를 설치하는 최소 비용을 계산하는것이 목표&lt;/b&gt;다. 제약 사항이 하나 있다면 수로를 건설하는 업자가 수로 건설 비용이 C 이상이 되는 것만 설치한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서 농지는 그래프의 노드로, 수로는 간선으로 모델링 할 수 있다. 이렇게 그래프로 모델링하고 나면 우리가 구해야 하는 것은 비용이 C 이상인 간선들을 이용해 모든 노드들을 연결하는 미니멈 스패닝 트리를 구하는 문제로 바뀐다.(만약 미니멈 스패닝 트리를 모른다면 이 문제를 푸는 것은 어려울 것이라 생각한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미니멈 스패닝 트리를 구하는 알고리즘은 크게 크루스칼 알고리즘과 프림 알고리즘 두 개가 있는데, 크루스칼 알고리즘을 이용해 미니멈 스패닝 트리를 구했다.(보통 크루스칼 알고리즘을 이용하는데 왠지모르게 디스조인트 셋을 구현하는 것이 기분이 좋다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;답을 출력할 때에는 실제로 스패닝 트리가 만들어졌는지 확인할 필요가 있다. 0번 노드의 집합 번호를 찾은 뒤 나머지 노드가 전부 같은 집합에 속하는지 확인하는 방법으로 간단하게 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;소스 코드&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

struct Point {
    int x, y;
    
    Point(int _x, int _y): x(_x), y(_y) {}
};

struct Edge {
    int u, v, w;
    
    Edge(int _u, int _v, int _w): u(_u), v(_v), w(_w) {}
    
    bool operator &amp;lt; (const Edge&amp;amp; r) const {
        return w &amp;lt; r.w;
    }
};

struct DisjointSet {
    vector&amp;lt;int&amp;gt; parents, ranks;
    
    DisjointSet(int n): parents(n), ranks(n, 1) {
        for (int i = 0 ; i &amp;lt; n ; ++i) {
            parents[i] = i;
        }
    }
    
    int find(int u) {
        if (u == parents[u]) {
            return u;
        }
        
        return parents[u] = find(parents[u]);
    }
    
    void merge(int u, int v) {
        u = find(u);
        v = find(v);
        
        if (u == v) {
            return;
        }
        
        if (ranks[u] &amp;gt; ranks[v]) {
            swap(u, v);
        }
        
        parents[u] = v;
        
        if (ranks[u] == ranks[v]) {
            ++ranks[v];
        }
    }
};

int getDist(const Point&amp;amp; a, const Point&amp;amp; b) {
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}

int n, c;
vector&amp;lt;Point&amp;gt; points;
vector&amp;lt;Edge&amp;gt; edges;

int main() {
    scanf(&quot;%d%d&quot;, &amp;amp;n, &amp;amp;c);

    for (int i = 0 ; i &amp;lt; n ; ++i) {
        int x, y;
        scanf(&quot;%d%d&quot;, &amp;amp;x, &amp;amp;y);
        points.push_back(Point(x, y));
    }
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        for (int j = i + 1 ; j &amp;lt; n ; ++j) {
            int dist = getDist(points[i], points[j]);
            
            if (dist &amp;gt;= c) {
                edges.push_back(Edge(i, j, dist));
            }
        }
    }
    
    sort(edges.begin(), edges.end());
    
    DisjointSet disjointSet(n);
    
    long long ans = 0;
    
    for (const auto&amp;amp; edge: edges) {
        if (disjointSet.find(edge.u) == disjointSet.find(edge.v)) {
            continue;
        }
        
        disjointSet.merge(edge.u, edge.v);
        ans += edge.w;
    }
    
    int root = disjointSet.find(0);
    
    for (int i = 1 ; i &amp;lt; n ; ++i) {
        if (root != disjointSet.find(i)) {
            puts(&quot;-1&quot;);
            
            return 0;
        }
    }
    
    printf(&quot;%lld\n&quot;, ans);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj</category>
      <category>boj 10021번</category>
      <category>백준</category>
      <category>백준 10021번</category>
      <category>알고리즘</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/38</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-10021%EB%B2%88-watering-the-fields#entry38comment</comments>
      <pubDate>Mon, 20 May 2024 23:30:58 +0900</pubDate>
    </item>
    <item>
      <title>[백준 17404번] RGB거리 2</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17404%EB%B2%88-RGB%EA%B1%B0%EB%A6%AC-2</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17404&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/17404&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N개의 집을 다음 두 개의 규칙에 맞게 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;빨강, 초록, 파랑 중 하나로 칠해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인접한 두 집의 색이 달라야 한다.&lt;/li&gt;
&lt;li&gt;1번 집과 N번 집의 색이 달라야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 집을 특정 색으로 칠하는 비용이 주어졌을 때, 모든 집을 규칙에 맞게 칠할 때 드는 최소 비용을 구하는 문제이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;원래 문제에는 규칙이 세 개 있지만, 이해를 돕기 위해 두 개로 정리했다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 이 문제는 동적 계획법(dynamic programming)을 이용해 해결할 수 있다. 먼저, 부분 문제를 해결할 수 있는 함수를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(findMinCost(idx, prevColor) =\) idx - 1번째 집을 prevColor로 칠했을 때, idx번째 집부터 마지막 집까지 규칙에 맞게 칠할 수 있는 최소 비용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 함수를 바탕으로 다음과 같은 점화식을 세울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(findMinCost(idx, prevColor)\)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(= \min_{\rm \text{color} \in \text{{R,G,B}} \setminus \text{{prevColor}}}\)\(findMinCost(idx + 1, color) + cost[idx][color]\)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 점화식을 함수로 구현한 다음에 메모이제이션 기법을 이용해 문제를 풀 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;근데... 이러면 1번 집과 N번 집의 색이 달라야 한다는 조건을 고려하지 않은 거 아닌가요?&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞다. 이 점화식은 1번 집과 N번 집의 색이 달라야 한다는 조건을 만족하지 않는 점화식이다. 이를 해결하기 위해 약간의 꼼수가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 1번 집의 색을 하나 결정한다. 그다음 마지막 집의 색을 고를 때 이전 집의 색뿐만 아니라 1번 집의 색도 고려할 수 있도록 점화식을 수정한다. 바꾼 점화식을 이용해 1번 집을 빨강, 초록, 파랑으로 각각 색칠한 경우에 대해 전체 집을 색칠하는 최소 비용을 계산하고, 그중 가장 작은 값을 선택한다. 이렇게 하면 기존 점화식의 큰 변화 없이 문제의 조건을 만족하면서 모든 집을 칠하는데 드는 최소 비용을 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스 코드&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

const int INF = 987654321;
int n, cost[1010][3], dp[1010][3];

void init() {
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        for (int j = 0 ; j &amp;lt; 3 ; ++j) {
            dp[i][j] = INF;
        }
    }
}

int findMinCost(int pos, int prevColor, int firstColor) {
    if (pos == n) {
        return 0;
    }
    
    int&amp;amp; ret = dp[pos][prevColor];
    
    if (ret != INF) {
        return ret;
    }
    
    for (int currentColor = 0 ; currentColor &amp;lt; 3 ; ++currentColor) {
        if (currentColor == prevColor || (pos == n - 1 &amp;amp;&amp;amp; currentColor == firstColor)) {
            continue;
        }
        
        ret = min(ret, findMinCost(pos + 1, currentColor, firstColor) + cost[pos][currentColor]);
    }
    
    return ret;
}

int main() {
    scanf(&quot;%d&quot;, &amp;amp;n);
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        for (int j = 0 ; j &amp;lt; 3 ; ++j) {
            scanf(&quot;%d&quot;, &amp;amp;cost[i][j]);
        }
    }
    
    int ans = INF;
    
    for (int i = 0 ; i &amp;lt; 3 ; ++i) {
        init();
        ans = min(ans, findMinCost(1, i, i) + cost[0][i]);
    }
    
    printf(&quot;%d\n&quot;, ans);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj</category>
      <category>boj 17404번</category>
      <category>PS</category>
      <category>백준</category>
      <category>백준 17404번</category>
      <category>알고리즘</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/37</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-17404%EB%B2%88-RGB%EA%B1%B0%EB%A6%AC-2#entry37comment</comments>
      <pubDate>Mon, 20 May 2024 10:02:34 +0900</pubDate>
    </item>
    <item>
      <title>[백준 1022번] 소용돌이 예쁘게 출력하기</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-1022%EB%B2%88-%EC%86%8C%EC%9A%A9%EB%8F%8C%EC%9D%B4-%EC%98%88%EC%81%98%EA%B2%8C-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 링크&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1022&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1022&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반시계 방향을 따라 소용돌이 모양으로 숫자를 채운 뒤, 주어진 범위의 숫자를 포맷에 맞게 출력하는 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 푸는 방법에는 실제로 소용돌이를 만들어보는 시뮬레이션 방법과 특정 좌표의 숫자를 구할 수 있는 규칙을 찾는 방법 두 가지가 있다. 이 문제를 풀기 위해 접근할 때 보통 두 번째 방법을 많이 생각하는 것 같다. 하지만 입력으로 주어지는 수의 제한 범위와 현대 컴퓨터의 연산속도를 이용하면 직접 시뮬레이션을 돌려 푸는 방법도 있다는 것을 소개하고 싶다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;풀이 1. 시뮬레이션&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모눈종이에 소용돌이 모양으로 숫자를 채우는 시물레이션을 돌려보는 상상을 해보자. 입력으로 주어지는 좌표의 최솟값은 -5,000이고 최댓값은 5,000이다. 입력으로 주어지는 좌표의 최솟값은 -5,000이고 최댓값은 5000이다. 따라서 우리가 시뮬레이션을 돌려봐야 하는 모눈종이 한 변의 길이는 최대 10,000이 된다. 한 변의 길이가 10,000인 모눈종이에 소용돌이 모양으로 숫자를 채우기 위해 반복문을 돌아야 하는 횟수는 10,000의 제곱인 1억 번이다. 이 정도 횟수는 반복문 안에 무거운 작업만 들어가지 않는다면 2초 내에 충분히  수행할 수 있는 연산량이다. 시뮬레이션으로 풀 수 있다는 것을 알았으니 실제 구현을 어떻게 할지 생각을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(0, 0)에서 시작하여 소용돌이 모양으로 좌표를 움직이기 위해 방향 배열 \(dr\), \(dc\)를 준비한다. 이 방향 배열은 현재 좌표 \((r, c\))가 다음에 어디로 움직여야 할지 알려주는 이정표 역할을 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1715966451295&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const int dr[] = {0, -1, 0, 1};
const int dc[] = {1, 0, -1, 0};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방향 배열을 사용하여 현재 좌표가 움직일 수 있는 방향은 총 네 가지다. 이 방향은 특정한 좌표에 도달하면 다음 인덱스 방향으로 바뀌게 된다. 그 순간은 직접 소용돌이를 그려보면 쉽게 찾아볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k6Uzp/btsHtL1i0kN/Ku6eFSUiKTYB3LMFH4B4Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k6Uzp/btsHtL1i0kN/Ku6eFSUiKTYB3LMFH4B4Kk/img.png&quot; data-alt=&quot;방향을 바꿔야 하는 순간들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k6Uzp/btsHtL1i0kN/Ku6eFSUiKTYB3LMFH4B4Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk6Uzp%2FbtsHtL1i0kN%2FKu6eFSUiKTYB3LMFH4B4Kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;방향을 바꿔야 하는 순간들&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;254&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;방향을 바꿔야 하는 순간들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소용돌이를 그리면서 방향을 바꿔야 하는 좌표에 네모 표시를 해줬다. 같은 색의 네모들의 좌표는 열과 행의 절댓값 중 더 큰 값이 모두 같다. 이 특징을 이용하면 현재 좌표가 방향을 바꿔야 하는 좌표인지 알 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1715967160716&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;r += dr[k];
c += dc[k];

int mmax = max(abs(r), abs(c));
        
if ((mmax - 1 == r &amp;amp;&amp;amp; mmax== c)
    || (-mmax == r &amp;amp;&amp;amp; mmax == c)
    || (-mmax == r &amp;amp;&amp;amp; -mmax == c)
    || (mmax == r &amp;amp;&amp;amp; -mmax == c)) {
        // k는 방향 배열에서 어떤 인덱스를 선택할지 결정하는 변수
        k = (k + 1) % 4;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소용돌이 모양으로 채운 숫자를 모두 저장하기에는 메모리가 부족하기 때문에 출력에 필요한 좌표를 map에 저장하는 방법을 사용한다. 현재 좌표가 내가 출력해야 하는 좌표에 포함되면 map에 저장!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력할 때는 가장 큰 숫자의 자릿수를 계산해 주고 C++의 setw 기능을 이용해 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예쁘게 출력해야 하는 조건을 맞춰주자. 이렇게 구현한 최종 코드는 아래와 같다.&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715967683235&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;cstdlib&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;iomanip&amp;gt;

using namespace std;

map&amp;lt;pair&amp;lt;int, int&amp;gt;, int&amp;gt; ans;

int r1, c1, r2, c2, maxLen;
const int dr[] = {0, -1, 0, 1};
const int dc[] = {1, 0, -1, 0};

int main() {
    cin &amp;gt;&amp;gt; r1 &amp;gt;&amp;gt; c1 &amp;gt;&amp;gt; r2 &amp;gt;&amp;gt; c2;
    
    int r = 0, c = 0, cur = 1, k = 0;
    while (ans.size() != (r2 - r1 + 1) * (c2 - c1 + 1)) {
        if (r1 &amp;lt;= r &amp;amp;&amp;amp; r &amp;lt;= r2 &amp;amp;&amp;amp; c1 &amp;lt;= c &amp;amp;&amp;amp; c &amp;lt;= c2) {
            ans[{ r, c }] = cur;
            maxLen = floor(log10(cur)) + 1;
        }
        
        r += dr[k];
        c += dc[k];
        
        int mmax = max(abs(r), abs(c));
        
        if ((mmax - 1 == r &amp;amp;&amp;amp; mmax== c)
            || (-mmax == r &amp;amp;&amp;amp; mmax == c)
            || (-mmax == r &amp;amp;&amp;amp; -mmax == c)
            || (mmax == r &amp;amp;&amp;amp; -mmax == c)) {
            k = (k + 1) % 4;
        }
    
        ++cur;
    }
    
    for (int r = r1 ; r &amp;lt;= r2 ; ++r) {
        for (int c = c1 ; c &amp;lt;= c2 ; ++c) {
            cout &amp;lt;&amp;lt; setw(maxLen) &amp;lt;&amp;lt; ans[{ r, c }];
            
            if (c &amp;lt; c2) {
                cout &amp;lt;&amp;lt; &quot; &quot;;
            }
        }
        
        cout &amp;lt;&amp;lt; endl;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;풀이 2. 규칙 찾기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소용돌이 모양의 숫자들은 특정 기준에 따라 그룹 지을 수 있다. 각 숫자의 열과 행의 절댓값 중 더 큰 값이 똑같은 숫자들끼리 그룹을 지어보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blpOgk/btsHtuMiwfW/tKCqBxqH1RjB2G2aeq5Ie0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blpOgk/btsHtuMiwfW/tKCqBxqH1RjB2G2aeq5Ie0/img.png&quot; data-alt=&quot;그룹지을 수 있는 숫자들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blpOgk/btsHtuMiwfW/tKCqBxqH1RjB2G2aeq5Ie0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblpOgk%2FbtsHtuMiwfW%2FtKCqBxqH1RjB2G2aeq5Ie0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;그룹지을 수 있는 숫자들&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;254&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그룹지을 수 있는 숫자들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 그룹의 숫자들에게는 같은 레벨을 부여할 수 있다. 각 레벨은 \((0, 0)\)에서의 거리로 정의할 수 있으며, \((0, 0)\)에서 멀어질수록 레벨은 1씩 증가한다. 편의상 레벨은 1부터 시작하며, \((0, 0)\)은 그룹에 포함시키지 않고 예외 처리를 할 예정이다.&lt;/p&gt;
&lt;pre id=&quot;code_1715969242307&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int getLevel(int r, int c) {
    return max(abs(r), abs(c));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 그룹에 레벨을 부여하면, 각 레벨까지 등장한 숫자의 개수를 계산할 수 있다. 각 그룹은 한 변의 길이가 \((level + 1) * 2 - 1\)인 정사각형 이며, 이 정사각형의 면적을 구하면 해당 레벨까지의 숫자 갯수를 알 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1715970132021&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int getBiggestValueOf(int level) {
    int len = (level + 1) * 2 - 1;
    
    return len * len;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 한 그룹을 조금 다 자세히 들여다보자.  각각의 그룹들은 다시 네 개의 서브 그룹(코드상에서는 step으로 부르려고 한다.)으로 나눌 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MctK8/btsHt1W1SxV/mt8t5EWxaKC0CrwJrRQqE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MctK8/btsHt1W1SxV/mt8t5EWxaKC0CrwJrRQqE1/img.png&quot; data-alt=&quot;한 그룹을 다시 네 개의 그룹으로 나눌 수 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MctK8/btsHt1W1SxV/mt8t5EWxaKC0CrwJrRQqE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMctK8%2FbtsHt1W1SxV%2Fmt8t5EWxaKC0CrwJrRQqE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;한 그룹을 다시 네 개의 그룹으로 나눌 수 있다&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;254&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;한 그룹을 다시 네 개의 그룹으로 나눌 수 있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 개의 그룹은 총 \(level * 8\)개의 숫자로 이루어져 있으며, 이를 \(level * 2\)개의 숫자씩 총 네 개의 서브 그룹으로 다시 나눠줄 수 있다. 그룹 내의 어떤 숫자가 몇 번째 서브그룹에 속하는지는 조금 복잡하지만 행, 열, 레벨을 이용해 계산할 수 있다. 그리고 속한 서브그룹 내에서 몇 번째 순서인지도 같이 계산해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1715971556858&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; pair&amp;lt;int, int&amp;gt; getStepOfLevel(int r, int c, int level) {
    if (c == level &amp;amp;&amp;amp; r &amp;gt;= -level &amp;amp;&amp;amp; r &amp;lt;= level - 1) {
        return {1, abs(r - level)};
    } else if (r == -level &amp;amp;&amp;amp; c &amp;gt;= -level &amp;amp;&amp;amp; c &amp;lt;= level) {
        return {2, abs(level - c)};
    } else if (c == -level &amp;amp;&amp;amp; r &amp;gt;= -level + 1 &amp;amp;&amp;amp; r &amp;lt;= level) {
        return {3, abs(r - (-level))};
    } else if (r == level &amp;amp;&amp;amp; c &amp;gt;= -level + 1 &amp;amp;&amp;amp; c &amp;lt;= level) {
        return {4, abs(c - (-level))};
    }
    return {-1, -1};
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 구한 정보로 특정 좌표 \((r, c)\)가 주어졌을 때 그 좌표에 들어가야 할 소용돌이 숫자를 상수시간에 계산할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1715971631516&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int getValue(int r, int c) {
    if (r == 0 &amp;amp;&amp;amp; c == 0) {
        return 1;
    }
    
    int level = getLevel(r, c);
    pair&amp;lt;int, int&amp;gt; step = getStepOfLevel(r, c, level);
    
    int biggestValueOfPrevLevel = getBiggestValueOf(level - 1);
    int cntOfPrevSteps = (step.first - 1) * (level * 2);
    
    return biggestValueOfPrevLevel + cntOfPrevSteps + step.second;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제까지 구현한 코드들과 소용돌이를 이쁘게 출력하는 코드까지 합친 전체 코드는 다음과 같다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715971798783&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;cstdlib&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int r1, c1, r2, c2, ans[110][110], maxLength;

int getLevel(int r, int c) {
    return max(abs(r), abs(c));
}

int getBiggestValueOf(int level) {
    int len = (level + 1) * 2 - 1;
    
    return len * len;
}

pair&amp;lt;int, int&amp;gt; getStepOfLevel(int r, int c, int level) {
    if (c == level &amp;amp;&amp;amp; r &amp;gt;= -level &amp;amp;&amp;amp; r &amp;lt;= level - 1) {
        return {1, abs(r - level)};
    } else if (r == -level &amp;amp;&amp;amp; c &amp;gt;= -level &amp;amp;&amp;amp; c &amp;lt;= level) {
        return {2, abs(level - c)};
    } else if (c == -level &amp;amp;&amp;amp; r &amp;gt;= -level + 1 &amp;amp;&amp;amp; r &amp;lt;= level) {
        return {3, abs(r - (-level))};
    } else if (r == level &amp;amp;&amp;amp; c &amp;gt;= -level + 1 &amp;amp;&amp;amp; c &amp;lt;= level) {
        return {4, abs(c - (-level))};
    }
    return {-1, -1};
}

int getValue(int r, int c) {
    if (r == 0 &amp;amp;&amp;amp; c == 0) {
        return 1;
    }
    
    int level = getLevel(r, c);
    pair&amp;lt;int, int&amp;gt; step = getStepOfLevel(r, c, level);
    
    int biggestValueOfPrevLevel = getBiggestValueOf(level - 1);
    int cntOfPrevSteps = (step.first - 1) * (level * 2);
    
    return biggestValueOfPrevLevel + cntOfPrevSteps + step.second;
}

int main() {
    cin &amp;gt;&amp;gt; r1 &amp;gt;&amp;gt; c1 &amp;gt;&amp;gt; r2 &amp;gt;&amp;gt; c2;
    
    for (int r = r1 ; r &amp;lt;= r2 ; ++r) {
        for (int c = c1 ; c &amp;lt;= c2 ; ++c) {
            int val = getValue(r, c);
            int len = floor(log10(val)) + 1;
            maxLength = max(maxLength, len);
        }
    }
    
    for (int r = r1 ; r &amp;lt;= r2 ; ++r) {
        for (int c = c1 ; c &amp;lt;= c2 ; ++c) {
            int val = getValue(r, c);
            
            cout &amp;lt;&amp;lt; setw(maxLength) &amp;lt;&amp;lt; val;
            
            if (c &amp;lt; c2) {
                cout &amp;lt;&amp;lt; &quot; &quot;;
            }
        }
        cout &amp;lt;&amp;lt; endl;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떤 방법이 더 좋을까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법 1은 구현이 간단하지만, 모든 경우의 수를 계산해야 하기 때문에 시간이 오래 걸린다. 반면에 방법 2는 구현이 복잡하지만 특정 좌표의 값을 상수 시간에 알 수 있어 더 빠른 성능을 제공한다. 그렇다면 성능이 더 우수한 방법 2가 더 나은 알고리즘일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 대회에서는 문제를 해결하는 것이 가장 중요하며, 성능이 떨어지더라도 제한시간 내에 답을 계산할 수 있다면 정답으로 인정된다. 따라서 방법 1과 방법 2 중 어느 것이 더 낫다고 쉽게 단정 지을 수는 없다. 만약 이 문제에서 주어진 좌표의 제한값이 더 컷다면 방법 1로는 풀 수 없었을 것이다. 하지만 문제의 제한값이 시뮬레이션을 이용해 충분히 해결할 수 있는 범위 내에 있었기 때문에 이 문제는 방법 1도 좋은 선택지 중에 하나다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실전에는 어떤 문제가 어떤 조건으로 나올지 모른다. 그렇기 때문에 똑같은 문제더라도 가능한 많은 방법으로 풀어보는 것이 좋은 것 같다.&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj</category>
      <category>boj 1022번</category>
      <category>PS</category>
      <category>백준</category>
      <category>백준 1022번</category>
      <category>알고리즘</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/36</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-1022%EB%B2%88-%EC%86%8C%EC%9A%A9%EB%8F%8C%EC%9D%B4-%EC%98%88%EC%81%98%EA%B2%8C-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0#entry36comment</comments>
      <pubDate>Sat, 18 May 2024 13:00:49 +0900</pubDate>
    </item>
    <item>
      <title>[백준 1111번] IQ Test</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-1111%EB%B2%88-IQ-Test</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 링크&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; &lt;/span&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1111&quot;&gt;https://www.acmicpc.net/problem/1111&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점화식 \(x_{i+1} = x_i * a + b\) 으로 만들어진 길이 \(N\)의 수열이 주어진다. \(a\), \(b\)를 찾아 수열의 다음 항을 출력하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 번호도 그렇고 제목도 그렇고 뭔가 쉬워보이는 냄새가 난다. IQ Test 정도는 뭔가 쉽게 통과할 것 같은 근자감도 생긴다. 그렇게 9년 전의 나는 쉬운 마음으로 1111번에게 도전했다 참패했다. 9년이 지난 지금의 나는 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;241&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s1ZW5/btsHng9a2Zh/IPXlvJiU5uhx8U3952As7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s1ZW5/btsHng9a2Zh/IPXlvJiU5uhx8U3952As7k/img.png&quot; data-alt=&quot;9년전의 나보다 강해져서 다행이다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s1ZW5/btsHng9a2Zh/IPXlvJiU5uhx8U3952As7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs1ZW5%2FbtsHng9a2Zh%2FIPXlvJiU5uhx8U3952As7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;9년전의 나보다 강해져서 다행이다&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;241&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;241&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;9년전의 나보다 강해져서 다행이다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽진 않았지만 그래도 어찌저찌 9년 전 참패는 만회할 수 있었다. 구미가 당기는 번호, 쉬운 제목으로 사람을 현혹시키고 패배의 감정을 맛보게 해주는 악마의 문제 &lt;a href=&quot;https://www.acmicpc.net/problem/1111&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백준 1111번&lt;/a&gt; 풀이를 적어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;문제 풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 개의 연속된 항 \(x_{i}\), \(x_{i+1}\), \(x_{i+2}\)을 가지고 다음 두 선형 점화식을 세울 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;\(x_{i+2} = x_{i+1} * a + b\)&lt;/li&gt;
&lt;li&gt;\(x_{i+1} = x_{i} * a + b\)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 이 두 식의 차를 이용해 다음과 같이 \(a\)를 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(a = \frac{x_{i+2} - x_{i+1}}{x_{i+1} - x_{i}}\)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \(a\)를 찾은 후, \(b\)의 값도 자연스럽게 구할 수 있다. 이  식만 놓고 본다면 정말 쉬워보이지만, 예외 케이스를 처리하는데서 이 문제의 진가가 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 케이스 크게 두 가지가 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;다음 항이 여러개일 수 있는 경우(A 출력)&lt;/li&gt;
&lt;li&gt;다음 항을 구할 수 없는 경우 (B 출력)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 예외 케이스를 계산하기 위해서는 \(N\)에 따라 경우의 수를 나눠서 생각해야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;N이 1인 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(a\)와 \(b\)를 특정할 수 없으므로 A를 출력한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;N이 2인 경우&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 수와 두 번째 수가 같다면, \(a\)는 1이고 \(b\)는 0이다.&lt;/li&gt;
&lt;li&gt;두 수가 다를 경우, \(a\)와 \(b\)를 특정할 수 없으므로 A를 출력한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;N이 3 이상인 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 이제 위에서 구한 식을 활용하면 되는데 여기서도 다시 경우의 수가 나눠진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분자가 0이 되는 경우에는 \(a\)가 1, \(b\)는 0이다.&lt;/li&gt;
&lt;li&gt;분자와 분모가 나눠떨어지지 않는 경우에 정수인 \(a\)와 \(b\)를 찾지 못한다는 의미이니 B를 출력한다.&lt;/li&gt;
&lt;li&gt;함정들을 다 피하고 나서 세 개의 항을 임의로 선택해 \(a\)와 \(b\)를 계산한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 정말 마지막으로 식을 통해서 얻은 \(a\)와 \(b\)로 만든 수열이 맞는지 확인하고 맞다면 다음 항을 계산해서 출력하고 아니라면 B를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;소스 코드&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

int n, A[110];

using namespace std;

int main() {
    cin &amp;gt;&amp;gt; n;
    
    for (int i = 0 ; i &amp;lt; n ; ++i) {
        cin &amp;gt;&amp;gt; A[i];
    }
    
    if (n == 1) {
        cout &amp;lt;&amp;lt; &quot;A&quot; &amp;lt;&amp;lt; endl;
        
        return 0;
    }
    
    if (n == 2) {
        if (A[0] != A[1]) {
            cout &amp;lt;&amp;lt; &quot;A&quot; &amp;lt;&amp;lt; endl;
            
            return 0;
        } else {
            cout &amp;lt;&amp;lt; A[0] &amp;lt;&amp;lt; endl;
            
            return 0;
        }
    }
    
    int up = A[1] - A[2];
    int down = A[0] - A[1];
    
    if (down != 0 &amp;amp;&amp;amp; up % down != 0) {
        cout &amp;lt;&amp;lt; &quot;B&quot; &amp;lt;&amp;lt; endl;
        
        return 0;
    }
    
    int a = down == 0 ? 1 : up / down;
    int b = A[1] - A[0] * a;
    
    for (int i = 0 ; i &amp;lt; n - 1 ; ++i) {
        if (A[i] * a + b != A[i + 1]) {
            cout &amp;lt;&amp;lt; &quot;B&quot; &amp;lt;&amp;lt; endl;
            
            return 0;
        }
    }

    cout &amp;lt;&amp;lt; A[n - 1] * a + b &amp;lt;&amp;lt; endl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/35</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-1111%EB%B2%88-IQ-Test#entry35comment</comments>
      <pubDate>Tue, 14 May 2024 00:01:25 +0900</pubDate>
    </item>
    <item>
      <title>[백준 1039번] 교환</title>
      <link>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-1039%EB%B2%88-%EA%B5%90%ED%99%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;백준에서 풀만한 문제가 뭐가 있다 어슬렁거리다 과거의 내가 풀지 못했던 문제를 지금 내가 푼다면 맞출 수 있을까 궁금해졌다. 슬프게도 시도했지만 맞지 못한 문제가 많아 숫자가 작은 것부터 다시 시도해 봤다. 그래서 선택한 1039번!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2410&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kIWgp/btsHm0kbbO1/LE8p69zVogIwKDGXxK7iEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kIWgp/btsHm0kbbO1/LE8p69zVogIwKDGXxK7iEK/img.png&quot; data-alt=&quot;9년전의 나에게는 버거웠던 1039번&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kIWgp/btsHm0kbbO1/LE8p69zVogIwKDGXxK7iEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkIWgp%2FbtsHm0kbbO1%2FLE8p69zVogIwKDGXxK7iEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2410&quot; height=&quot;470&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2410&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;9년전의 나에게는 버거웠던 1039번&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무려 9년 전, 그러니까 2014년에 시도하고, 2015년에 다시 한번 시도하고 나에게 잊혔던 1039번이 다시 나에게 다가왔다. 과연 지금의 나는 과거의 나보다 더 강해졌을까 긴장되는 마음으로 문제를 다시 읽어봤는데 다행히 과거의 나보다는 더 강해진 것 같아 이렇게 풀이를 작성해 본다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 링크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1039&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;https://www.acmicpc.net/problem/1039&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 정수 \(N\)에서 서로 다른 두 자릿수의 위치 \(i\), \(j\)를 선택하여 숫자를 서로 교환하는 연산을 &lt;b&gt;정확히&lt;/b&gt; \(K\)번 수행할 때, 가능한 최댓값을 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 풀기 전에 꼭 알아야 할 세 가지 주의사항이 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;정확하게 \(K\)번을 연산한 뒤 나올 수 있는 최댓값을 찾아야 한다. 연산 횟수가&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;\(K\)번보다 적어도 안되고 \(K\)번보다 많아도 안된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;연산 과정 속에서 나오는 숫자는 0으로 시작할 수 없다.&lt;/li&gt;
&lt;li&gt;\(K\)번 연산을 수행할 수 없으면 -1을 출력해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의사항 세 가지를 알아봤으니 이제는 본격적인 풀이를 알아보자.  현재 수 \(here\)과 남은 연산 횟수 \(left\)의 쌍을 하나의 독립적인 상태로 보고 노드라고 생각하면 DFS 혹은 BFS를 이용해 쉽게 문제를 풀 수 있다. 개인적으로 BFS 보다는 재귀를 사용하는 DFS가 더 멋있어 보여서 DFS로 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 그래프의 순회 알고리즘을 구현할 때 노드의 방문 여부를 배열로 확인하곤 하지만, 이 이 문제에서는 가능한 노드의 수가 \(len(N)! * K\)로 많지 않기 때문에 메모리를 아끼기 위해 \(set\)을 사용해 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 두 자릿수를 교환하는 연산은 직접 구현해도 되고 언어에서 제공하는 라이브러리를 사용해도 좋다. 나는 C++에서 제공하는 \(\text{to_string}\)과 \(\text{stoi}\)를 이용해 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 최댓값을 찾을 때 구현 편의상 재귀함수의 기저사례에서 최댓값을 찾는 로직을 넣었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;소스 코드&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1715190365476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int n, k, ans = -1;
set&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; visited;

void dfs(int here, int depth) {
    if (depth == 0) {
        ans = max(ans, here);
        return;
    }
    
    visited.insert({ here, depth });
    
    string str = to_string(here);

    for (int i = 0 ; i &amp;lt; str.size() ; ++i) {
        for (int j = i + 1 ; j &amp;lt; str.size() ; ++j) {
            swap(str[i], str[j]);
            
            if (str[0] != '0') {
                const int there = stoi(str);
                
                if (!visited.count({ there, depth - 1 })) {
                    dfs(there, depth - 1);
                }
            }
            
            swap(str[i], str[j]);
        }
    }
}

int main() {
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
    
    dfs(n, k);
    
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; endl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/문제풀이</category>
      <category>boj 1039번</category>
      <category>boj 교환</category>
      <category>백준 1039번</category>
      <category>백준 교환</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/34</guid>
      <comments>https://www.ohnimdev.com/entry/%EB%B0%B1%EC%A4%80-1039%EB%B2%88-%EA%B5%90%ED%99%98#entry34comment</comments>
      <pubDate>Mon, 13 May 2024 03:39:40 +0900</pubDate>
    </item>
    <item>
      <title>티스토리에서 MathJax가 동작하지 않을 때 해결방법</title>
      <link>https://www.ohnimdev.com/entry/%ED%8B%B0%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%97%90%EC%84%9C-MathJax%EA%B0%80-%EB%8F%99%EC%9E%91%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%84-%EB%95%8C-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;티스토리에서 MathJax 스크립트를 삽입했는데도 MathJax가 동작하지 않을 때 사용할 수 있는 방법을 소개하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 사용하던 티스토리 기본 스킨을 &lt;a href=&quot;https://pronist.tistory.com/5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;hELLO 스킨&lt;/a&gt;으로 변경하고 난 다음에 멀쩡히 동작하던 MathJax가 동작하지 않기 시작했다. 구글링을 해도 마땅한 해결 방법을 찾지 못해, 온리 영어로 되어있어 최후의 보루로 남겨놓은 &lt;a href=&quot;https://docs.mathjax.org&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt;를 열었는데 결국 그곳에서 힌트를 발견할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;방법 1. Lazy typesetting&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Lazy typesetting&quot;은 페이지에 있는 수식들이 실제로 브라우저 뷰포트에 진입할 때까지 그 렌더링을 지연시키는 설정인데, 보는 순간 이거 된다라는 생각이 들었다. 자세한 내용은 &lt;a href=&quot;https://docs.mathjax.org/en/latest/output/lazy.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt;에서 읽을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;방법 2. MathJax 강제로 한 번 더 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 설정을 해주고 나서 MathJax자 잘 동작해서 좋아했는데...  간헐적으로 MathJax가 동작하지 않을 때가 있었다. 또 다른 방법을 찾아야 한다는 생각에 슬픔이 차올랐지만 정신을 부여잡고 공식 문서를 다시 살펴보니 동적으로 추가된 콘텐츠들 대응하기 위한 &lt;a href=&quot;https://docs.mathjax.org/en/latest/advanced/typeset.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;typesetPromise 문서&lt;/a&gt;를 찾을 수 있었다. MathJax가 동작하지 않았을 때 콘솔창에 이 메서드를 직접 실행시키니 MathJax가 적용되는 것을 확인했다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 언제 MathJax를 강제로 한 번 더 실행시키냐는 것인데... 정말 많은 고민 끝에 MathJax가 제공하는 라이프사이클 안에서 setTimeout을 이용해 다음 틱에 MathJax를 한 번 더 실행시키는 &lt;s&gt;흑마법&lt;/s&gt;을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;며칠간의 대삽질 끝에 만들어진 티스토리용 MathJax 코드는 다음과 같다. 이 코드를 HTML head 안에 살포시 넣어주자.&lt;/p&gt;
&lt;pre id=&quot;code_1715532496335&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  window.MathJax = {
    loader: {load: ['ui/lazy']},
    startup: {
      pageReady: function () {
        setTimeout(function() {
          MathJax.typesetPromise();
        }, 0);
        
        return MathJax.startup.defaultPageReady();
      },
    },
  };
&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot; id=&quot;MathJax-script&quot; async src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적용 결과는?!&lt;span style=&quot;background-color: #f5f5f5; color: #23282d; text-align: left;&quot; data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;\\[x=\\frac{-b\\pm\\sqrt{b^2&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;\\[x=\\frac {-b\\pm\\sqrt {b^2&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar1&quot; data-grammar-focus=&quot;true&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f5f5f5; color: #23282d; text-align: left;&quot;&gt;\[x=\frac{-b\pm\sqrt{b^2 -4ac}}{2a}\]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공이다!! 이후로도 테스트를 계속해봤는데 잘 되는 것 같다.(제발...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마도 티스토리 스킨을 변경하면서 블로그 콘텐츠 로딩 속도가 살짝 느려져서 MathJax가 도큐먼트를 파싱 하는 타이밍과 맞지 않아 발생한 문제로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가에게는 이 글이 도움이 되길 바란다ㅎㅎ&lt;/p&gt;</description>
      <category>개발로그/잡다한 이야기</category>
      <category>Mathjax</category>
      <category>mathjax 동작하지 않을 때</category>
      <category>mathjax 문제 해결</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/33</guid>
      <comments>https://www.ohnimdev.com/entry/%ED%8B%B0%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%97%90%EC%84%9C-MathJax%EA%B0%80-%EB%8F%99%EC%9E%91%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%84-%EB%95%8C-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95#entry33comment</comments>
      <pubDate>Mon, 13 May 2024 00:22:16 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트의 typeof 연산자</title>
      <link>https://www.ohnimdev.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-typeof-%EC%97%B0%EC%82%B0%EC%9E%90</link>
      <description>&lt;p style=&quot;clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;대부분의 프로그래밍 언어에서 데이터 타입을 확인할 수 있는 연산자를 제공한다. 자바스크립트도 데이터 타입을 확인할 수 있는 \(typeof\) 연산자를 제공한다. \(typeof\) 연산자는 피 연산자의 데이터 타입을 문자열로 리턴한다. 사용법은 어렵지 않기때문에 실습 코드로 설명을 대신한다.&lt;/p&gt;
&lt;p style=&quot;clear: none; float: none;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;소스 코드&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;/* number example */
var intNum = 10;
var floatNum = 3.14;

console.log(typeof intNum, typeof floatNum); // number number

/* string example */
var ch = 'a';
var str =  &quot;hello world&quot;;

console.log(typeof ch, typeof str) // string string

/* boolean example */
var boolVal = true;

console.log(typeof boolVal) // boolean

/* null, undefined example */
var nullVal = null;
var undefinedVal = undefined;

console.log(typeof nullVal); // object 
console.log(typeof undefinedVal); // undefined 

/* array, object example */ 
var arr = [1, 2, 3, 4];
var obj = {
  name: 'kesakiyo',
};  

console.log(typeof arr); // object
console.log(typeof obj); // object  

/* function example */ 
var func = function(x, y) { 	
  return x + y
}  

console.log(typeof func); // function&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;마무리&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 유의해야 할 점이 몇 가지 있다. 첫 번째로 숫자는 정수형, 실수형 상관없이 \(number\) 타입을 가진다. 이는 자바스크립트에서 모든 숫자를 부동 소수점으로 저장하기 때문이다. 두 번째로 자바스크립트에서는 문자열과 \(char\) 구분이 없다.&amp;nbsp;세 번째로는 기본타입인 \(null\)이 \(object\) 타입을 가진다. 이 세가지는 헷갈리지 말고 정확히 기억하고 있어야 한다. 더 자세한 설명은 &lt;a class=&quot;tx-link&quot; href=&quot;http://kesakiyo.tistory.com/30&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&quot;자바스크립트의 데이터 타입&quot;&lt;/a&gt;에서 읽을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/214AA036588CC0632B?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/214AA036588CC0632B?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/214AA036588CC0632B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F214AA036588CC0632B&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; alt=&quot;자바스크립트의 타입 요약&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;200&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>개발로그/JavaScript, TypeScript</category>
      <category>typeof</category>
      <category>데이터타입</category>
      <category>데이터타입 확인</category>
      <category>자바스크립트</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/31</guid>
      <comments>https://www.ohnimdev.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-typeof-%EC%97%B0%EC%82%B0%EC%9E%90#entry31comment</comments>
      <pubDate>Sun, 29 Jan 2017 00:03:18 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트의 데이터 타입</title>
      <link>https://www.ohnimdev.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85</link>
      <description>&lt;blockquote class=&quot;tx-quote-tistory&quot;&gt;&lt;p&gt;&lt;strike&gt;&lt;i&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;이 글은 C, C++에 익숙한 개발자의 시점으로 작성됐습니다.&lt;/span&gt;&lt;/i&gt;&lt;/strike&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 13.3333px;&quot;&gt;&lt;b&gt;Symbol과 Object는 다루지 않습니다.&lt;/b&gt; 이 두 가지는 다른 포스팅에서 자세히 다룰 예정입니다.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;자바 스크립트의 데이터 타입은 크게 두 가지로 나눠질 수 있다. 첫 번째는 &lt;b&gt;기본 타입&lt;/b&gt;, 두 번째는 &lt;b&gt;객체(Object)&lt;/b&gt;이다. 기본 타입은 숫자, 문자열, 불리언, undefined, null, Symbol(ECMAscript6)로 나눠지며 객체는 배열, 함수, 정규표현식으로 나눠진다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 580px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/2554C03A5882268A34&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F2554C03A5882268A34&quot; width=&quot;580&quot; height=&quot;338&quot; filename=&quot;11111.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;이 글에서는 기본 타입에 해당하는 데이터 타입들을 살펴볼 것이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;숫자(Number)&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;C, C++, Java는 숫자를 정수, 실수로 나눠 \(int\), &amp;nbsp;\(float\), &amp;nbsp;\(double\) 등과 같은 다양한 타입이 존재한다. 하지만 자바스크립트에서는 모든 숫자를 64비트 부동 소수점으로 저장하기 때문에 숫자 타입이 하나밖에 없다. 이는 ECMAscirpt 명세서 4.3.20 에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;그렇기 때문에 변수에 값을 초기화 할 때 정수, 실수 구분없이 바로 저장이 가능하다. 아래 예제에서 \(num\)에는&amp;nbsp;10을, \(PI\)에는 3.14를 대입한 뒤 출력하면 10과 3.14가 출력되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;brush: javascript&quot;&gt;var num = 10
var PI = 3.14

console.log(num, PI)

&amp;gt; 10 3.14
&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;어떻게 보면 상당히 편리하다고 생각할 수 있다. 하지만 모든 수들이 부동 소수점으로 저장되기 때문에 연산을 할 때 주의해야 한다. 두 가지 예제를 보면서 어떤 위험이 도사리고 있는지 확인을 해 보자.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;첫 번째 예제는 나눗셈에서 흔히 할 수 있는 실수다. 변수 \(a\)에는 정수 3을, \(\ b\)에는 정수 2를 대입한다. 그리고 \(a\ /\ b\)를 출력해 보자. C++에 익숙한 개발자라면 당연히 출력값이 1이라고 생각한다. 하지만 이는 잘못된 생각이다. 자바스크립트는 모든 숫자 타입이 부동 소수점으로 저장되기 때문에 두 숫자를 실수로 취급하고 계산을 한다. 그렇기 때문에 1.5가 출력된다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;원하는 값인 1을 얻으려면 내장 함수 Math.floor( )을 사용하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;brush: javascript&quot;&gt;var a = 3
var b = 2

console.log(a / b)
console.log(Math.floor(a / b))

&amp;gt; 1.5
&amp;gt; 1
&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;두 번째 예제는 두 수를 더할 때 발생할 수 있는 부동 소수점 오차이다. 부동 소수점은 한정된 메모리 내에서 넓은 범위의 수를 표현하기 위해 사용된다. 하지만 정확한 수를 저장하는 것이 아니라 근삿값으로 저장을 하기 때문에 연산을 할 때 오차가 발생을 할 수 있다. 아래 예제를 보면서 확인을 해보자.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush: javascript&quot;&gt;var a = 0.1
var b = 0.2

console.log(a * b, a + b)

&amp;gt; 0.020000000000000004 0.30000000000000004
&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;우리가 원하는 값은 암산으로 계산할 수 있을 정도로 간단하다. 0.1과 0.2을 곱하면 0.02가 돼야하고 0.1과 0.2를 더하면 0.3이 돼야 한다. 하지만 실제로 출력된 값은 기괴하기 짝이없다. 이런 부동 소수점 오류는 개발자가 의도하지 않은 상황을 야기하기 때문에 항상 주의를 할 필요가 있다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;문자열(String)&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;문자열은 말 그대로 문자열을 표현할 수 있는 데이터 타입이다. C, C++에서는 문자열은&amp;nbsp;큰따옴표로 초기화 하고, \(char\)는 작은따옴표로 초기화를 했다. 하지만 자바스크립트는 다르다. 큰따옴표와 작은따옴표 모두 문자열을 초기화 하는데 사용할 수 있다. 그리고 위에서 언급을 안했듯이 자바스크립트에는 \(char\)타입이 없다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush: javascript&quot;&gt;var str1 = &quot;hello world&quot;
var str2 = 'hello world'
var ch = 'c'

console.log(
	typeof str1,
	typeof str2,
	typeof ch
)

&amp;gt; string string string
&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;위 예제에서는 str1은 큰따옴표로 문자열을 초기화하고, str2는 작은따옴표로 문자열을 초기화했다. 마지막으로 ch 변수에 한 문자만 대입했다. 그리고 typeof 연산을 통해 str1, str2, ch의 데이터 타입을 출력한다. 세&amp;nbsp;변수 모두 string 데이터 타입을 갖는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;문자열에서 주의할 점은 한번 초기화 된 문자열은 변하지 않는다는 점이다. 아래 예제를 살펴보자.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush: javascript&quot;&gt;var str = &quot;hello world&quot;
str[0] = &quot;H&quot;

console.log(str)

&amp;gt; hello world
&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;위 코드를 작성한 개발자는 첫 글자를 대문자로 바꾸고 출력하길 원했다. 하지만 출력 결과는 의도와는 전혀 다르게 원래 문자열이 출력된다. 심지어 런타임때 에러도 발생하지 않는다!! 자바스크립트에서 문자열을 수정하려면 새롭게 문자열 변수를 만들어 줘야 한다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;불리언(Boolean)&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;불리언 타입은 C나 C++과 거의 비슷하다. true 또는 false의 값을 가진다. 불리언에 대해서는 자세히 다룰 내용이 없으므로 간단한 예제코드 하나를 보고 넘어가자.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush: javascript&quot;&gt;var bool = true

console.log(typeof bool)

&amp;gt; boolean
&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;undefined와 null&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;undefined와 null은 한 챕터에서 설명을 하려 한다. 두 개를 헷갈려서 사용하는 개발자들이 의외로 많기 때문이다. 특히 C++이나 JAVA를 다루다 자바스크립트를 처음 접하는 사람들은 이 두 개의 차이를 확실하게 알아 둘 필요가 있다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;undefined는 값이 정의되지 않은 상태다. 변수가 선언된 뒤 어떠한 값도 할당받지 못한 상태라 할 수 있다. undefined 타입은 값 또한 undefined이다. 즉 undefined는 타입과 값을 동시에 나타내는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;null은 개발자가 명시적으로 값이 없다는 것을 나타내는 것이다. 여기서 주의할 점은 null의 typeof 값은 object라는 점이다. 따라서 자바스크립트에서 null 값을 확인할 때 일치 연산자('===')를 사용해 값을 직접 비교해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;아래 예제에서 아무 값도 할당하지 않은 변수가 undefined 타입을 가지는 것을 확인할 수 있다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush: javascript&quot;&gt;var undefinedVal
var nullVal = null

console.log(undefinedVal, typeof undefinedVal) // undefined 'undefined'
console.log(nullVal, typeof nullVal) // null 'object'

console.log(typeof nullVal == null) // false
console.log(nullVal == null) // true

&amp;gt; undefined 'undefined'
&amp;gt; null 'object'
&amp;gt; false
&amp;gt; true&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>개발로그/JavaScript, TypeScript</category>
      <category>object</category>
      <category>Symbol</category>
      <category>undefined</category>
      <category>객체</category>
      <category>데이터 타입</category>
      <category>문자열</category>
      <category>숫자</category>
      <category>자바스크립트</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/30</guid>
      <comments>https://www.ohnimdev.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85#entry30comment</comments>
      <pubDate>Fri, 20 Jan 2017 22:58:36 +0900</pubDate>
    </item>
    <item>
      <title>실수 없는 프로그래밍을 위한 몇 가지 방법</title>
      <link>https://www.ohnimdev.com/entry/%EC%8B%A4%EC%88%98-%EC%97%86%EB%8A%94-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%AA%87-%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p&gt;프로그래밍 대회에서 좋은 성적을 올리기 위한 방법에는 어떤 것이 있을까요? 아마 대부분의 사람들이 다양한 알고리즘을 알고 있는 것이라 대답할 것입니다. 물론 문제를 풀기 위해서 여러 알고리즘을 아는 것은 매우 중요합니다. 하지만 단순히 알고리즘만&amp;nbsp;많이 알고 있다고 해서 프로그래밍 대회에서 좋은 성적을 거둘 수 있는 것은 아닙니다. 그렇다면 어떤 것이 가장 중요할까요?&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;혼자서 공부를 할 때는 제한시간이 없기 때문에 여유로운 마음으로 코딩을 할 수 있습니다. 하지만 프로그래밍 대회에서는 그렇지 않습니다. 제한시간이 있고 다른 사람들이 문제를 얼마나 풀었는지 볼 수 있습니다. 또한 팀원들이 같이 있을 경우 내가 잡은 문제를 꼭 풀어야 한다는 압박감을 받을 수 있습니다. 이러한 상황 속에서 여유로운 마음으로 코딩을 하기 쉽지 않습니다. 이때 평상시와 같이 코딩을 할 수 있게 도와주는 것은 철저하게 훈련된 &lt;b&gt;&lt;i&gt;&lt;u&gt;코딩 능력&lt;/u&gt;&lt;/i&gt;&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;여기서 말하는 코딩 능력은 구현 능력과는 다릅니다. 구현 능력은 내가 생각한 것을 코드로 옮기는 능력이고 코딩 능력은 정확하고 읽기 쉬운 코드를 작성하는 능력입니다. 이 글에서는 코딩 능력을 높이기 위한 몇 가지 주제를 다뤄보려고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p id=&quot;bookmark&quot;&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;책갈피&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;#indentation&quot;&gt;1. 들여쓰기의 중요성&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;#convention&quot;&gt;2. 자신만의 코딩 컨벤션&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;#macro&quot;&gt;3. 매크로 사용을 지양&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;#modularization&quot;&gt;4. 코드의 재활용&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;#naming&quot;&gt;&lt;b&gt;5. 의미있는 작명&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;#stl&quot;&gt;6. STL과 c++11을 활용&lt;/a&gt;&lt;/p&gt;
&lt;p id=&quot;indentation&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;들여쓰기의 중요성&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;코딩을 할 때 들여쓰기는&amp;nbsp;생명과도 같습니다. 혹자는 당연히 지켜야 하는 것 아니냐 얘기할 수 있지만&amp;nbsp;생각보다 많은 사람들이 들여쓰기를 지키지 않고 코딩을 합니다. 아래 코드는 들여쓰기를 지키지 않은 코드입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush:cpp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;algorithm&amp;gt;

int main() {
  int n, A[1010];
  scanf(&quot;%d&quot;, &amp;amp;n);
  
  for (int i = 0 ; i &amp;lt; n ; ++i) {
  scanf(&quot;%d&quot;, &amp;amp;A[i]);
  }
  
  for (int i = 0 ; i &amp;lt; n ; ++i) {
  for (int j = 0 ; j &amp;lt; i ; ++j) {
    if (A[j] &amp;gt; A[i]) {
    std::swap(A[i], A[j]);
  }
  }
  }
}&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;단순한 코드임에도 불구하고 한눈에 알아보기 힘듭니다. 20줄 남짓한 코드조차 알아보기 힘든데 코드가 길어진다면 어떻게 될까요? 그 누구도 읽기 싫은 코드가 될 것이 자명해 보입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;물론 미관상의 이유만으로 들여쓰기를 지키라는 것이 아닙니다. Python, Ruby 같은 언어와 달리 C, C++, Java는 중괄호로 Block Scope가 나눠집니다. 때문에&amp;nbsp;Block 안에서만 유효한 변수 또는 로직이 있을 수 있습니다. 만약 들여쓰기를 지키지 않는다면 한 Block의 시작과 끝을 한눈에 알 수 없어&amp;nbsp;자신이 선언한 변수나 로직이 어디서부터 어디까지 영향을 미치는지 다시 확인을 해야 하는 경우가 발생할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;1분 1초도 아까운 프로그래밍 대회에서 들여쓰기를 지키는 것만으로 디버깅 시간을 줄일 수 있다면 당연히 들여쓰기를 지켜야 할 것입니다. 거의 모든 에디터들이 자동 들여쓰기를 지원해 주니 이를 활용하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;위에 들여쓰기가&amp;nbsp;엉망인 코드는 아래와 같이 바꿔야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush:cpp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;algorithm&amp;gt;

int main() {
  int n, A[1010];
  scanf(&quot;%d&quot;, &amp;amp;n);
  
  for (int i = 0 ; i &amp;lt; n ; ++i) {
    scanf(&quot;%d&quot;, &amp;amp;A[i]);
  }
  
  for (int i = 0 ; i &amp;lt; n ; ++i) {
    for (int j = 0 ; j &amp;lt; i ; ++j) {
      if (A[j] &amp;gt; A[i]) {
        std::swap(A[i], A[j]);
      }
    }
  }
}&lt;/pre&gt;

&lt;p id=&quot;convention&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;&amp;nbsp;자신만의 코딩 컨벤션&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;프로그래밍 대회를 준비하면 수많은 종류의 알고리즘을 공부하고 반복적으로 작성하게 됩니다. 한 알고리즘을 구현할 때 여러 가지 방법을 시도해 봅니다.&amp;nbsp;\(while\)과 \(do-while\)을 돌아가며 써보기도 하고, 반복문과 재귀를 번갈아 가며 써보기도 합니다. 이는 처음 알고리즘을 배우고 자신에게 최적화할 때 도움이 됩니다. 하지만 이런 방법을 계속해서 사용을 한다면 잦은 실수의 원인이 되기도 합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;새로운 방법으로 작성한 코드가 제대로 동작하는지 확인을 하기란 쉬운 일이 아닙니다. 물론 이렇게 바꾼 코드가 기대하던 대로 동작하고 성능이 향상될 수도 있지만 여러 변수가 존재하는 프로그래밍 대회에서 이런 여유를 부리다가는 좋지 않은 결과를 받기 십상입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;어떻게 하면 효율적이고 읽기 쉬운 코드를 짤 수 있는지 계속해서 생각을 할 필요는 있습니다. 하지만 이것을 굳이 시간에 쫓기는 프로그래밍 대회 때 할 필요는 없습니다. 자주 사용하는 알고리즘들은 여러 번 반복해서 작성해보고 자신만의 코딩 컨벤션을 확립할 필요가 있습니다. 그래야 문제를 풀 때 어떻게 구현을 할지 생각을 하지 않고 오로지 어떻게 풀지에 집중을 할 수 있습니다.&lt;/p&gt;

&lt;p id=&quot;macro&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;무분별한 매크로 사용을 지양&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;C에서는 어떤 문자열을 원하는 문자열로 치환시켜주는 매크로가 존재합니다. 많은 사람들이 \(min, max, square\) 등의 함수들을 매크로 함수로 정의해 사용하곤 합니다. 하지만 매크로 함수의 정확한 이해 없이 사용하다간 끝나지 않는 디버깅의 늪에 빠질 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;아래는 매크로 함수에 대한 예시입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush:cpp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
#define max(a, b) ((a) &amp;gt; (b) ? (a) : (b))

int main() {
  int a = 10;
  int b = 20;
  
  int mmax1 = max(a, b);
  int mmax2 = max(a, b++);
  
  printf(&quot;%d %d\n&quot;, mmax1, mmax2);
}&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;어떤 의도를 가지고 작성된 코드인지는 쉽게 알 수 있습니다. 그렇다면 어떤 값이 출력될까요? [20 20]이 출력된다고 생각하시는 분이 많을 거라 생각됩니다. 하지만 코드를 직접 실행해 본다면 [20 21]이 출력되는 것을 볼 수 있습니다. 만약 이러한 일이 프로그래밍 대회 때 발생한다면 어떻게 될까요. 매크로 함수를&amp;nbsp;잘못 사용했다는 생각을 못하고 알고리즘을 계속해서 확인할 확률이 높습니다. 생각만 해도 정말 끔찍한 일입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;그럼 왜 이러한 일이 일어나는지 간단하게 알아봅시다. C에서 제공하는 매크로는 컴파일 시간 전에 이뤄지는 텍스트의 치환 작업입니다. 즉 위 코드는 다음 코드와 같이 컴파일이 이뤄집니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush:cpp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;

int main() {
  int a = 10;
  int b = 20;
  
  int mmax1 = ((a) &amp;gt; (b) ? (a) : (b));
  int mmax2 = ((a) &amp;gt; (b++) ? (a) : (b++));
  
  printf(&quot;%d %d\n&quot;, mmax1, mmax2);
}&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;삼항 연산자 안에 후치 연산이 두 번이나 들어가 있는 형태가 돼버립니다. 심지어 \(b\)를 21로 만들려고 했음에도 최종적으로 22라는 값을 가집니다.&amp;nbsp;이렇듯 조금 복잡한 로직을 매크로 함수에 녹이게 된다면 직접 써보기 전까지 어떠한 일이 일어나는지 추적을 하기가 쉽지 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;매크로 함수를 정확하게 이해하고 사용한다면 이런 일이 발생하지 않겠지만 이보다 더 최선은 매크로 함수를 아예 사용하지 않는 것입니다. C++에서는 \(inline\) 함수가 있으므로 매크로 함수 대신 이를 사용하면 위와 같은 일을 원천봉쇄할 수 있습니다. 아래는 \(inline\) 함수를 사용한 예시입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush:cpp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;

inline int max(int a, int b) {
  return a &amp;gt; b ? a : b;
}

int main() {
  int a = 10;
  int b = 20;
  
  int mmax1 = max(a, b);
  int mmax2 = max(a, b++);
  
  printf(&quot;%d %d\n&quot;, mmax1, mmax2);
}&lt;/pre&gt;

&lt;p id=&quot;modularization&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;코드의 재활용&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;코딩을 하다 보면 반복해서 나오는 로직이 있습니다. 이때 반복되는 로직을 어떻게 처리하냐에 따라 코딩의 정확성, 속도가 달라집니다. 반복되는 로직이 나오면 이를 모듈화하는 것이 일반적입니다. 프로그래밍 대회에서 코딩을 한다고 이는 달라지지 않습니다. 하지만 시간에 쫓기는 프로그래밍 대회에서 반복되는 로직을 모듈화하는데 시간을 할애하기 쉽지 않습니다. 그럼에도 불구하고 코드를 재활용해야 하는 이유는 무엇일까요?&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;첫째, 코드를 한층 더 읽기 쉽게 만들어 줍니다. 가독성이 높은 코드가 눈에 더 잘 들어오고 다른 사람들을 이해시키기 훨씬 쉽습니다. 또한 계속해서 간결한 코드를 보게 된다면 간결한 코드를 짜는데 익숙해집니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;둘째, 디버깅 시간이 확연히 줄어듭니다. 만약 한 로직이 전체 코드에서 다섯 번 사용됐다고 가정을 합시다. 그런데 그 로직이 잘못됐다면 다섯 군데를 수정해야 합니다. 하지만 이를 모듈화했다면 단 한 군데만 수정하는 것으로 디버깅을 끝낼 수 있습니다. 또한 오타가 나기 쉬운 로직 같은 경우 한, 두 군데만 잘못될 수 있습니다. 이런 경우 모듈화를 하지 않았다면 디버깅 시간이 많게는 열 배 이상 차이 날 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;아래는 어떠한 동작을 하는 코드입니다. 어떤 일을 하는지 읽어보며 생각해 봅시다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush:cpp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;

int t, n, m, x[110], y[110];

int main() {
  scanf(&quot;%d%d&quot;, &amp;amp;n, &amp;amp;m);
  for (int i = 1 ; i &amp;lt; n ; ++i) {
    scanf(&quot;%d%d&quot;, &amp;amp;x[i], &amp;amp;y[i]);
  }
  for (int i = n ; i &amp;lt; n + m ; ++i) {
    scanf(&quot;%d%d&quot;, &amp;amp;x[i], &amp;amp;y[i]);
  }
  
  scanf(&quot;%d&quot;, &amp;amp;t);
  while (t--) {
    int q, from , to, ans;
    
    scanf(&quot;%d%d%d&quot;, &amp;amp;q, &amp;amp;from, &amp;amp;to);
    if (q == 1) {
      ans = (x[from] - x[to]) * (x[from] - x[to]) + (y[from] - y[to]) * (y[from] - y[to]);
    } else if (q == 2) {
      ans = (x[from + n] - x[to + n]) * (x[from + n] - x[to + n]) + (y[from + n] - y[to + n]) * (y[from + n] - y[to + n]);
    } else if (q == 3) {
      ans = (x[from] - x[to + n]) * (x[from] - x[to + n]) + (y[from] - y[to + n]) * (y[from] - y[to + n]);
    }
    
    printf(&quot;%d\n&quot;, ans);
  }
}&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;먼저 \(A\) 그룹, \(B\) 그룹의&amp;nbsp;좌표를 입력받고 쿼리에 따라 \(A\) 그룹 간의 거리, \(B\) 그룹 간의 거리, \(A\) 그룹과 \(B\) 그룹간의 거리를 구해서 출력하는 코드입니다. 거리를 구하는 로직이 세 번이나 반복되는 것을 볼 수 있습니다. 또한 쿼리를 처리하는 부분이 어떤 일을 하는지 설명을 듣기전에는 이해하기 힘듭니다. 만약 이 부분을 모듈화 하면 어떻게 될까요?&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;brush:cpp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;

int t, n, m, x[110], y[110];

int dist(int from, int to) {
  return (x[from] - x[to]) * (x[from] - x[to]) + (y[from] - y[to]) * (y[from] - y[to]);
}

int main() {
  scanf(&quot;%d%d&quot;, &amp;amp;n, &amp;amp;m);
  for (int i = 1 ; i &amp;lt; n ; ++i) {
    scanf(&quot;%d%d&quot;, &amp;amp;x[i], &amp;amp;y[i]);
  }
  for (int i = n ; i &amp;lt; n + m ; ++i) {
    scanf(&quot;%d%d&quot;, &amp;amp;x[i], &amp;amp;y[i]);
  }
  
  scanf(&quot;%d&quot;, &amp;amp;t);
  while (t--) {
    int q, from , to, ans;
    
    scanf(&quot;%d%d%d&quot;, &amp;amp;q, &amp;amp;from, &amp;amp;to);
    if (q == 1) {
      ans = dist(from, to);
    } else if (q == 2) {
      ans = dist(from + n, to + n);
    } else if (q == 3) {
      ans = dist(from, to + n);
    }
    
    printf(&quot;%d\n&quot;, ans);
  }
}&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;쿼리를 처리하는 부분이 훨씬 간결해진 것을 볼 수 있습니다. 또한 모듈화&amp;nbsp;한 부분이 어떤 일을 하는지 한눈에 볼 수 있게 됐습니다. 이렇듯 코드를 재활용하는 것은 잃는 것보다 얻는 것이 훨씬 더 많으므로 이에 익숙해질 필요가 있습니다.&lt;/p&gt;

&lt;p id=&quot;naming&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;의미있는 작명&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;우스갯소리로 프로그래머들이 코딩을 할 때 변수 및 함수 이름을 짓는데 가장 많은 시간이 소요된다고 합니다. 하지만 이를 단순 우스갯소리로 치부하면 안 됩니다. 그만큼 변수 및 함수의 작명이 중요하기 때문입니다. 하지만 프로그래밍 대회에서 사용되는 코드들을 보면 작명에 신경 쓰지 않은 경우가 많습니다. 프로그래밍 대회를 준비하는 사람이라면 아래와 같은 작명을 많이 봤을 겁니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;brush:cpp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;

int A[110], B[110], C[110], a, aa, b, bb;

bool flag(...) {
  // ...
}

int main() {
  /*
   main function
   */
}&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이런 작명은 코딩을 한 본인은 알아볼 수 있을지 모르겠지만 다른 사람 혹은 같은 팀원이 보기에는 최악의 코드입니다. 또한 디버깅을 할 때 변수 혹은 함수가 어떤 역할을 하는지 계속해서 생각을 해야 하기 때문에 시간이 더 오래 걸립니다. 물론 작명을 하는데 엄청난 시간을 할애하라는 것이 아닙니다. 작성자 본인 혹은 같은 팀원이 봤을 때 어떤 역할을 하는지 대략적으로 알 수 있다면 그것으로 충분합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;프로그래밍 대회에서 많이 사용하는 알고리즘들은 함수 및 변수들을 의미 있게 작명한 다음 꾸준히 사용하는 것도 좋은 방법입니다.&lt;/p&gt;

&lt;p id=&quot;stl&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color=&quot;#747474&quot;&gt;&lt;span style=&quot;font-size: 18.6667px;&quot;&gt;&lt;b&gt;STL과 C++11의 활용&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;알고리즘 공부를 처음 시작하는 사람들이 흔히 하는 생각은 &lt;strike&gt;&lt;i&gt;&quot;처음부터 끝까지 가내수공업&quot;&lt;/i&gt;&lt;/strike&gt; 입니다. 벡터, 큐, 균형잡힌 이진트리 등 많은 자료구조들을 직접 구현하고 사용합니다. 물론 한 번쯤은 직접 구현해보는 것은 좋은 방법입니다. 하지만 시간제한이 있는 대회에서 이들을 직접 구현해서 쓴다는 것은 시간 낭비입니다.&lt;/p&gt;
&lt;p&gt;(간혹가다 수행 시간에 병적으로 집착한다거나 컴파일 최적화를 안 해주는 대회에서는 어쩔 수 없이 직접 구현해야 할 때도 있긴 하지만 대부분의 경우는 아닙니다.)&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;C++에서는 표준 템플릿 라이브러리(이하 STL)를 제공합니다. 벡터, 큐, 맵, 셋 등 여러 자료구조들을 헤더 파일을 하나 추가하는 것만으로 사용할 수 있습니다. 이 STL들은 수많은 사람들이 사용하고 수없이 많이 검증됐기 때문에 믿고 사용할 수 있습니다. 또한 누가 사용하던 인터페이스가 똑같기 때문에 다른 팀원이 봤을 때 코드를 이해하기 한결 수월합니다. 그렇기 때문에 STL 사용방법을 익히는 것은 필수적입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;다음은 C++11에 대한 내용입니다.(C++11에 대해서 자세한 내용은 이 글에서는 다루지 않습니다.) C++11을 활용하면 익명 함수를 사용해 코드의 가독성을&amp;nbsp;높일 수 있을 뿐더러 &amp;nbsp;\(for-each\)구문을 사용해 배열의 범위 밖에 접근하는 것을 방지할 수 있습니다. 따라서 C++11의 문법을 익히고 사용한다면 코딩을 할 때 실수할 확률을 줄일 수 있습니다.&lt;/p&gt;</description>
      <category>개발로그/잡다한 이야기</category>
      <category>실수 없는 프로그래밍</category>
      <category>코딩 능력</category>
      <author>Ohnim &amp;middot; 오님</author>
      <guid isPermaLink="true">https://www.ohnimdev.com/29</guid>
      <comments>https://www.ohnimdev.com/entry/%EC%8B%A4%EC%88%98-%EC%97%86%EB%8A%94-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%AA%87-%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95#entry29comment</comments>
      <pubDate>Sat, 31 Dec 2016 02:51:16 +0900</pubDate>
    </item>
  </channel>
</rss>