<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>ImJay</title>
    <link>https://develop247.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 12 Aug 2026 02:29:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>ImJay</managingEditor>
    <image>
      <title>ImJay</title>
      <url>https://tistory1.daumcdn.net/tistory/4224936/attach/54e7293f73d1438fb29cb9842187d9fc</url>
      <link>https://develop247.tistory.com</link>
    </image>
    <item>
      <title>[리눅스 마스터 2급] LVM(Logical Volume Manager) 완벽 가이드 &amp;mdash; 기초부터 활용까지</title>
      <link>https://develop247.tistory.com/625</link>
      <description>&lt;h1 data-end=&quot;224&quot; data-start=&quot;176&quot;&gt;LVM(Logical Volume Manager) 완벽 가이드 &amp;mdash; 기초부터 활용까지&lt;/h1&gt;
&lt;h2 data-end=&quot;236&quot; data-start=&quot;226&quot; data-ke-size=&quot;size26&quot;&gt;1. 들어가며&lt;/h2&gt;
&lt;p data-end=&quot;444&quot; data-start=&quot;237&quot; data-ke-size=&quot;size16&quot;&gt;서버나 스토리지를 운영하다 보면, &amp;ldquo;디스크 공간이 부족해졌을 때 어떻게 확장하지?&amp;rdquo;라는 고민을 하게 됩니다.&lt;br /&gt;전통적인 파티션 방식은 구조가 고정되어 있어, 용량을 변경하려면 서비스 중단이 필요하고 데이터 손실 위험도 있습니다.&lt;br /&gt;이때 **LVM(Logical Volume Manager)**을 사용하면, 디스크 공간을 훨씬 더 유연하고 안전하게 관리할 수 있습니다.&lt;/p&gt;
&lt;hr data-end=&quot;449&quot; data-start=&quot;446&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;465&quot; data-start=&quot;451&quot; data-ke-size=&quot;size26&quot;&gt;2. 기본 배경 지식&lt;/h2&gt;
&lt;h3 data-end=&quot;493&quot; data-start=&quot;467&quot; data-ke-size=&quot;size23&quot;&gt;2-1. 파티션(Partition)이란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;682&quot; data-start=&quot;494&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;525&quot; data-start=&quot;494&quot;&gt;&lt;b&gt;하드디스크나 SSD를 '논리적으로 나눈 구역'&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;549&quot; data-start=&quot;526&quot;&gt;각 구역은 독립적으로 운영체제에서 관리&lt;/li&gt;
&lt;li data-end=&quot;592&quot; data-start=&quot;550&quot;&gt;예: C 드라이브(운영체제), D 드라이브(데이터), E 드라이브(백업)&lt;/li&gt;
&lt;li data-end=&quot;632&quot; data-start=&quot;593&quot;&gt;리눅스에서는 /dev/sda1, /dev/sda2 같은 형식&lt;/li&gt;
&lt;li data-end=&quot;682&quot; data-start=&quot;633&quot;&gt;&lt;b&gt;단점&lt;/b&gt;: 크기를 변경하기 어렵고, 다른 디스크 공간을 합치는 것이 사실상 불가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;711&quot; data-start=&quot;684&quot; data-ke-size=&quot;size23&quot;&gt;2-2. 컨트롤러(Controller)란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;916&quot; data-start=&quot;712&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;749&quot; data-start=&quot;712&quot;&gt;하드웨어 장치와 운영체제(OS) 사이에서 데이터를 주고받는 장치&lt;/li&gt;
&lt;li data-end=&quot;789&quot; data-start=&quot;750&quot;&gt;&lt;b&gt;스토리지 컨트롤러&lt;/b&gt;: 디스크 입출력, RAID 관리 등을 수행&lt;/li&gt;
&lt;li data-end=&quot;916&quot; data-start=&quot;790&quot;&gt;&lt;b&gt;RAID의 동작 레벨&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;916&quot; data-start=&quot;812&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;861&quot; data-start=&quot;812&quot;&gt;&lt;b&gt;하드웨어 RAID&lt;/b&gt;: 컨트롤러가 직접 관리, OS에는 하나의 디스크처럼 보임&lt;/li&gt;
&lt;li data-end=&quot;916&quot; data-start=&quot;864&quot;&gt;&lt;b&gt;소프트웨어 RAID&lt;/b&gt;: OS가 직접 RAID 기능을 수행 (리눅스 mdadm 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;921&quot; data-start=&quot;918&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;935&quot; data-start=&quot;923&quot; data-ke-size=&quot;size26&quot;&gt;3. LVM이란?&lt;/h2&gt;
&lt;p data-end=&quot;1080&quot; data-start=&quot;936&quot; data-ke-size=&quot;size16&quot;&gt;**LVM(Logical Volume Manager)**는 운영체제(OS) 레벨에서 디스크를 추상화해 &lt;b&gt;논리적 볼륨&lt;/b&gt;으로 관리하는 기술입니다.&lt;br /&gt;즉, 여러 디스크나 파티션을 하나의 저장 풀(VG)로 묶고, 필요한 만큼 잘라서(LV) 사용할 수 있습니다.&lt;/p&gt;
&lt;hr data-end=&quot;1085&quot; data-start=&quot;1082&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1103&quot; data-start=&quot;1087&quot; data-ke-size=&quot;size26&quot;&gt;4. LVM의 구성 요소&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1457&quot; data-start=&quot;1104&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1213&quot; data-start=&quot;1104&quot;&gt;&lt;b&gt;PV (Physical Volume)&lt;/b&gt; &amp;ndash; 물리 볼륨&lt;br /&gt;LVM이 관리할 수 있도록 초기화된 디스크 또는 파티션&lt;/li&gt;
&lt;li data-end=&quot;1213&quot; data-start=&quot;1181&quot;&gt;
&lt;div&gt;bash&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;복사&lt;span data-state=&quot;closed&quot;&gt;편집&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;pvcreate /dev/sda1 &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1334&quot; data-start=&quot;1215&quot;&gt;&lt;b&gt;VG (Volume Group)&lt;/b&gt; &amp;ndash; 볼륨 그룹&lt;br /&gt;여러 PV를 묶어서 하나의 큰 저장 풀로 만든 것&lt;/li&gt;
&lt;li data-end=&quot;1334&quot; data-start=&quot;1286&quot;&gt;
&lt;div&gt;bash&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;복사&lt;span data-state=&quot;closed&quot;&gt;편집&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;vgcreate my_vg /dev/sda1 /dev/sdb1 &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1457&quot; data-start=&quot;1336&quot;&gt;&lt;b&gt;LV (Logical Volume)&lt;/b&gt; &amp;ndash; 논리 볼륨&lt;br /&gt;VG에서 필요한 만큼 공간을 할당해 만든 가상의 디스크&lt;/li&gt;
&lt;li data-end=&quot;1457&quot; data-start=&quot;1412&quot;&gt;
&lt;div&gt;nginx&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;복사&lt;span data-state=&quot;closed&quot;&gt;편집&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;lvcreate&lt;/span&gt;&lt;/span&gt;&lt;span&gt; -L &lt;/span&gt;&lt;span&gt;&lt;span&gt;100G&lt;/span&gt;&lt;/span&gt;&lt;span&gt; -n my_lv my_vg &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-end=&quot;1462&quot; data-start=&quot;1459&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1485&quot; data-start=&quot;1464&quot; data-ke-size=&quot;size26&quot;&gt;5. LVM과 전통 파티션의 차이&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;항목전통 파티션LVM
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1681&quot; data-start=&quot;1487&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;1681&quot; data-start=&quot;1538&quot;&gt;
&lt;tr data-end=&quot;1567&quot; data-start=&quot;1538&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1546&quot; data-start=&quot;1538&quot;&gt;크기 변경&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1556&quot; data-start=&quot;1546&quot;&gt;어려움, 위험&lt;/td&gt;
&lt;td data-end=&quot;1567&quot; data-start=&quot;1556&quot; data-col-size=&quot;sm&quot;&gt;유연하고 안전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1592&quot; data-start=&quot;1568&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1580&quot; data-start=&quot;1568&quot;&gt;여러 디스크 결합&lt;/td&gt;
&lt;td data-end=&quot;1586&quot; data-start=&quot;1580&quot; data-col-size=&quot;sm&quot;&gt;불가능&lt;/td&gt;
&lt;td data-end=&quot;1592&quot; data-start=&quot;1586&quot; data-col-size=&quot;sm&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1638&quot; data-start=&quot;1593&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1607&quot; data-start=&quot;1593&quot;&gt;서비스 중 용량 변경&lt;/td&gt;
&lt;td data-end=&quot;1624&quot; data-start=&quot;1607&quot; data-col-size=&quot;sm&quot;&gt;불가능(마운트 해제 필요)&lt;/td&gt;
&lt;td data-end=&quot;1638&quot; data-start=&quot;1624&quot; data-col-size=&quot;sm&quot;&gt;가능(실시간 확장)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1657&quot; data-start=&quot;1639&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1645&quot; data-start=&quot;1639&quot;&gt;스냅샷&lt;/td&gt;
&lt;td data-end=&quot;1651&quot; data-start=&quot;1645&quot; data-col-size=&quot;sm&quot;&gt;불가능&lt;/td&gt;
&lt;td data-end=&quot;1657&quot; data-start=&quot;1651&quot; data-col-size=&quot;sm&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1681&quot; data-start=&quot;1658&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1664&quot; data-start=&quot;1658&quot;&gt;복잡성&lt;/td&gt;
&lt;td data-end=&quot;1669&quot; data-start=&quot;1664&quot; data-col-size=&quot;sm&quot;&gt;단순&lt;/td&gt;
&lt;td data-end=&quot;1681&quot; data-start=&quot;1669&quot; data-col-size=&quot;sm&quot;&gt;구조 이해 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;1686&quot; data-start=&quot;1683&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1707&quot; data-start=&quot;1688&quot; data-ke-size=&quot;size26&quot;&gt;6. &quot;마운트 해제 없이&quot;란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1867&quot; data-start=&quot;1708&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1748&quot; data-start=&quot;1708&quot;&gt;&lt;b&gt;마운트(Mount)&lt;/b&gt;: 디스크를 특정 경로(폴더)에 연결하는 것&lt;/li&gt;
&lt;li data-end=&quot;1780&quot; data-start=&quot;1749&quot;&gt;&lt;b&gt;마운트 해제(Unmount)&lt;/b&gt;: 연결을 끊는 것&lt;/li&gt;
&lt;li data-end=&quot;1824&quot; data-start=&quot;1781&quot;&gt;전통 파티션 방식: 용량 변경 시 반드시 마운트 해제 &amp;rarr; 해당 서비스 중단&lt;/li&gt;
&lt;li data-end=&quot;1867&quot; data-start=&quot;1825&quot;&gt;LVM: 온라인 상태에서 크기 변경 가능 &amp;rarr; 서비스 중단 없이 운영 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1872&quot; data-start=&quot;1869&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1890&quot; data-start=&quot;1874&quot; data-ke-size=&quot;size26&quot;&gt;7. LVM이 좋은 이유&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;2166&quot; data-start=&quot;1891&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;2000&quot; data-start=&quot;1891&quot;&gt;&lt;b&gt;용량 확장/축소 자유로움&lt;/b&gt;&lt;br /&gt;새 디스크를 VG에 추가 &amp;rarr; LV 용량 확장 &amp;rarr; 파일시스템 확장&lt;br /&gt;(예: /home을 500GB &amp;rarr; 800GB로 확장, 서비스 중단 없음)&lt;/li&gt;
&lt;li data-end=&quot;2061&quot; data-start=&quot;2002&quot;&gt;&lt;b&gt;여러 디스크를 하나처럼 사용&lt;/b&gt;&lt;br /&gt;서로 다른 크기&amp;middot;종류의 디스크도 하나의 VG로 묶어 활용&lt;/li&gt;
&lt;li data-end=&quot;2116&quot; data-start=&quot;2063&quot;&gt;&lt;b&gt;스냅샷 지원&lt;/b&gt;&lt;br /&gt;작업 전 상태를 저장해두고, 실패 시 해당 시점으로 롤백 가능&lt;/li&gt;
&lt;li data-end=&quot;2166&quot; data-start=&quot;2118&quot;&gt;&lt;b&gt;마운트 해제 없이 관리 가능&lt;/b&gt;&lt;br /&gt;서비스 가동 중에도 디스크 관리 가능&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-end=&quot;2171&quot; data-start=&quot;2168&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2195&quot; data-start=&quot;2173&quot; data-ke-size=&quot;size26&quot;&gt;8. LVM을 굳이 쓰지 않는 경우&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2377&quot; data-start=&quot;2196&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2243&quot; data-start=&quot;2196&quot;&gt;&lt;b&gt;단순 환경&lt;/b&gt;: 개인 PC, NAS, 외장하드 등 용량 변동이 거의 없는 경우&lt;/li&gt;
&lt;li data-end=&quot;2292&quot; data-start=&quot;2244&quot;&gt;&lt;b&gt;성능 우선 환경&lt;/b&gt;: 극한의 I/O 성능이 필요한 경우(미세한 성능 저하 방지)&lt;/li&gt;
&lt;li data-end=&quot;2339&quot; data-start=&quot;2293&quot;&gt;&lt;b&gt;호환성 문제&lt;/b&gt;: 오래된 OS나 일부 복구 툴이 LVM을 지원하지 않는 경우&lt;/li&gt;
&lt;li data-end=&quot;2377&quot; data-start=&quot;2340&quot;&gt;&lt;b&gt;복잡성 회피&lt;/b&gt;: PV/VG/LV 구조 학습이 부담되는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2382&quot; data-start=&quot;2379&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2403&quot; data-start=&quot;2384&quot; data-ke-size=&quot;size26&quot;&gt;9. LVM과 RAID의 관계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2558&quot; data-start=&quot;2404&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2454&quot; data-start=&quot;2404&quot;&gt;RAID: 디스크를 묶어 &lt;b&gt;성능&amp;middot;안정성&lt;/b&gt; 향상 (하드웨어 또는 소프트웨어에서 처리)&lt;/li&gt;
&lt;li data-end=&quot;2499&quot; data-start=&quot;2455&quot;&gt;LVM: 디스크를 묶어 &lt;b&gt;공간 관리 유연성&lt;/b&gt; 향상 (OS 레벨에서 처리)&lt;/li&gt;
&lt;li data-end=&quot;2558&quot; data-start=&quot;2500&quot;&gt;둘은 계층이 다르며, &lt;b&gt;병행 사용 가능&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2558&quot; data-start=&quot;2529&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2558&quot; data-start=&quot;2529&quot;&gt;예: RAID-5로 묶은 디스크를 LVM으로 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2563&quot; data-start=&quot;2560&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2574&quot; data-start=&quot;2565&quot; data-ke-size=&quot;size26&quot;&gt;10. 정리&lt;/h2&gt;
&lt;p data-end=&quot;2731&quot; data-start=&quot;2575&quot; data-ke-size=&quot;size16&quot;&gt;LVM은 &lt;b&gt;운영 중 디스크 관리가 필요한 환경&lt;/b&gt;에서 강력한 도구입니다.&lt;br /&gt;특히 서버 운영 중 용량 부족 문제를 &lt;b&gt;서비스 중단 없이 해결&lt;/b&gt;할 수 있다는 점이 큰 장점입니다.&lt;br /&gt;다만 단순 환경에서는 오히려 복잡성만 늘릴 수 있으므로, 용도에 맞게 선택하는 것이 중요합니다.&lt;/p&gt;
&lt;hr data-end=&quot;2736&quot; data-start=&quot;2733&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;2753&quot; data-start=&quot;2738&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;한 줄 요약&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-end=&quot;2839&quot; data-start=&quot;2754&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;2839&quot; data-start=&quot;2756&quot; data-ke-size=&quot;size16&quot;&gt;LVM = 디스크 공간을 가변적으로 나누고 합치는 &amp;lsquo;가변형 칸막이&amp;rsquo;&lt;br /&gt;RAID = 여러 디스크를 묶어 성능과 안정성을 높이는 &amp;lsquo;집의 기초 공사&amp;rsquo;&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>CS 지식</category>
      <author>ImJay</author>
      <guid isPermaLink="true">https://develop247.tistory.com/625</guid>
      <comments>https://develop247.tistory.com/625#entry625comment</comments>
      <pubDate>Sat, 9 Aug 2025 19:40:08 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 마스터 2급] RAID 완벽 정리 &amp;mdash; 원리부터 스페어 디스크, 복구 방식까지</title>
      <link>https://develop247.tistory.com/624</link>
      <description>&lt;h1 data-end=&quot;167&quot; data-start=&quot;131&quot;&gt;RAID 완벽 정리 &amp;mdash; 원리부터 스페어 디스크, 복구 방식까지&lt;/h1&gt;
&lt;h2 data-end=&quot;181&quot; data-start=&quot;169&quot; data-ke-size=&quot;size26&quot;&gt;1. RAID란?&lt;/h2&gt;
&lt;p data-end=&quot;308&quot; data-start=&quot;182&quot; data-ke-size=&quot;size16&quot;&gt;RAID(Redundant Array of Independent/Inexpensive Disks)는 &lt;b&gt;여러 개의 물리 디스크를 하나의 논리적 장치처럼 묶어서&lt;/b&gt; 성능 향상, 데이터 안정성, 또는 둘 다를 달성하는 기술입니다.&lt;/p&gt;
&lt;hr data-end=&quot;313&quot; data-start=&quot;310&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;334&quot; data-start=&quot;315&quot; data-ke-size=&quot;size26&quot;&gt;2. RAID-5란 무엇인가?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;567&quot; data-start=&quot;335&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;358&quot; data-start=&quot;335&quot;&gt;&lt;b&gt;구성&lt;/b&gt;: 최소 3개의 디스크 필요&lt;/li&gt;
&lt;li data-end=&quot;431&quot; data-start=&quot;359&quot;&gt;&lt;b&gt;방식&lt;/b&gt;: 데이터를 스트라이핑(Stripe)하여 나누고, 각 스트라이프마다 &lt;b&gt;패리티(Parity)&lt;/b&gt; 블록을 1개씩 저장&lt;/li&gt;
&lt;li data-end=&quot;473&quot; data-start=&quot;432&quot;&gt;&lt;b&gt;장점&lt;/b&gt;: 읽기 속도 우수, 디스크 1개 장애에도 데이터 복구 가능&lt;/li&gt;
&lt;li data-end=&quot;526&quot; data-start=&quot;474&quot;&gt;&lt;b&gt;단점&lt;/b&gt;: 쓰기 성능이 다소 저하(패리티 연산), 동시에 2개 이상 장애 시 복구 불가&lt;/li&gt;
&lt;li data-end=&quot;567&quot; data-start=&quot;527&quot;&gt;&lt;b&gt;용량 계산&lt;/b&gt;: (전체 디스크 수 - 1) &amp;times; 각 디스크 용량&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;572&quot; data-start=&quot;569&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;600&quot; data-start=&quot;574&quot; data-ke-size=&quot;size26&quot;&gt;3. 스페어 디스크(Hot Spare)란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;846&quot; data-start=&quot;601&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;673&quot; data-start=&quot;601&quot;&gt;&lt;b&gt;정의&lt;/b&gt;: RAID에 포함되어 있지만 평상시에는 데이터 저장에 쓰이지 않고, &lt;b&gt;고장 시 자동으로 투입&lt;/b&gt;되는 예비 디스크&lt;/li&gt;
&lt;li data-end=&quot;737&quot; data-start=&quot;674&quot;&gt;&lt;b&gt;역할&lt;/b&gt;: 장애 발생 시 즉시 리빌드(Rebuild)를 시작하여 Degraded 모드의 위험 기간을 최소화&lt;/li&gt;
&lt;li data-end=&quot;846&quot; data-start=&quot;738&quot;&gt;&lt;b&gt;차이점&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;846&quot; data-start=&quot;753&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;805&quot; data-start=&quot;753&quot;&gt;&lt;b&gt;패리티 저장 공간&lt;/b&gt;: 정상 동작 시 항상 데이터와 복구 정보를 저장하는 주전 선수&lt;/li&gt;
&lt;li data-end=&quot;846&quot; data-start=&quot;808&quot;&gt;&lt;b&gt;스페어 디스크&lt;/b&gt;: 평상시 놀다가 고장나면 들어오는 교체 선수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;851&quot; data-start=&quot;848&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;889&quot; data-start=&quot;853&quot; data-ke-size=&quot;size26&quot;&gt;4. RAID-5와 스페어 디스크가 함께 있을 때 용량 계산&lt;/h2&gt;
&lt;p data-end=&quot;901&quot; data-start=&quot;890&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 예시&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;948&quot; data-start=&quot;902&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;917&quot; data-start=&quot;902&quot;&gt;10GB 디스크 8개&lt;/li&gt;
&lt;li data-end=&quot;948&quot; data-start=&quot;918&quot;&gt;스페어 디스크 1개, 나머지로 RAID-5 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1077&quot; data-start=&quot;949&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;977&quot; data-start=&quot;949&quot;&gt;전체: 8 &amp;times; 10GB = &lt;b&gt;80GB&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1005&quot; data-start=&quot;978&quot;&gt;스페어 제외: 8 - 1 = &lt;b&gt;7개&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1045&quot; data-start=&quot;1006&quot;&gt;RAID-5 패리티: 7 - 1 = &lt;b&gt;6개&lt;/b&gt; 데이터 디스크&lt;/li&gt;
&lt;li data-end=&quot;1077&quot; data-start=&quot;1046&quot;&gt;실제 용량: 6 &amp;times; 10GB = &lt;b&gt;60GB&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;1093&quot; data-start=&quot;1079&quot; data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;정답: 60GB&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;1098&quot; data-start=&quot;1095&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1120&quot; data-start=&quot;1100&quot; data-ke-size=&quot;size26&quot;&gt;5. RAID-5의 패리티 원리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1292&quot; data-start=&quot;1121&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1175&quot; data-start=&quot;1121&quot;&gt;&lt;b&gt;패리티&lt;/b&gt;는 단순 복사본이 아닌, 같은 스트라이프 내의 데이터 블록들을 XOR 연산한 결과&lt;/li&gt;
&lt;li data-end=&quot;1232&quot; data-start=&quot;1176&quot;&gt;XOR 특징: A &amp;oplus; B &amp;oplus; B = A &amp;rarr; 한 블록이 없어도 나머지 블록과 패리티로 복원 가능&lt;/li&gt;
&lt;li data-end=&quot;1292&quot; data-start=&quot;1233&quot;&gt;각 스트라이프의 패리티 블록은 내용이 다르며, 모든 디스크에 &lt;b&gt;분산 저장&lt;/b&gt;되어 부하가 고르게 분배됨&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1297&quot; data-start=&quot;1294&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1315&quot; data-start=&quot;1299&quot; data-ke-size=&quot;size26&quot;&gt;6. 복구 과정 (예시)&lt;/h2&gt;
&lt;p data-end=&quot;1335&quot; data-start=&quot;1316&quot; data-ke-size=&quot;size16&quot;&gt;예: 디스크 4개, Disk3 장애&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;StripeDisk1Disk2Disk3Disk4
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1527&quot; data-start=&quot;1336&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;1527&quot; data-start=&quot;1422&quot;&gt;
&lt;tr data-end=&quot;1479&quot; data-start=&quot;1422&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1431&quot; data-start=&quot;1422&quot;&gt;1&lt;/td&gt;
&lt;td data-end=&quot;1439&quot; data-start=&quot;1431&quot; data-col-size=&quot;sm&quot;&gt;D1&lt;/td&gt;
&lt;td data-end=&quot;1447&quot; data-start=&quot;1439&quot; data-col-size=&quot;sm&quot;&gt;D2&lt;/td&gt;
&lt;td data-end=&quot;1463&quot; data-start=&quot;1447&quot; data-col-size=&quot;sm&quot;&gt;&lt;b&gt;D3&lt;/b&gt;(복구 대상)&lt;/td&gt;
&lt;td data-end=&quot;1479&quot; data-start=&quot;1463&quot; data-col-size=&quot;sm&quot;&gt;P1(D1&amp;oplus;D2&amp;oplus;D3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1527&quot; data-start=&quot;1480&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1489&quot; data-start=&quot;1480&quot;&gt;2&lt;/td&gt;
&lt;td data-end=&quot;1497&quot; data-start=&quot;1489&quot; data-col-size=&quot;sm&quot;&gt;D4&lt;/td&gt;
&lt;td data-end=&quot;1505&quot; data-start=&quot;1497&quot; data-col-size=&quot;sm&quot;&gt;D5&lt;/td&gt;
&lt;td data-end=&quot;1521&quot; data-start=&quot;1505&quot; data-col-size=&quot;sm&quot;&gt;&lt;b&gt;P2&lt;/b&gt;(복구 대상)&lt;/td&gt;
&lt;td data-end=&quot;1527&quot; data-start=&quot;1521&quot; data-col-size=&quot;sm&quot;&gt;D6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1540&quot; data-start=&quot;1529&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;복구 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1658&quot; data-start=&quot;1541&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1574&quot; data-start=&quot;1541&quot;&gt;Stripe 1: D3 = D1 &amp;oplus; D2 &amp;oplus; P1&lt;/li&gt;
&lt;li data-end=&quot;1658&quot; data-start=&quot;1575&quot;&gt;Stripe 2: P2 = D4 &amp;oplus; D5 &amp;oplus; D6 (패리티 재생성)&lt;br /&gt;이 과정을 각 스트라이프별로 반복하여 고장 디스크의 모든 블록을 재구성&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1663&quot; data-start=&quot;1660&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1684&quot; data-start=&quot;1665&quot; data-ke-size=&quot;size26&quot;&gt;7. Degraded 모드란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1850&quot; data-start=&quot;1685&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1732&quot; data-start=&quot;1685&quot;&gt;디스크 1개 고장 시, RAID-5는 남은 데이터 + 패리티로 즉시 서비스를 유지&lt;/li&gt;
&lt;li data-end=&quot;1789&quot; data-start=&quot;1733&quot;&gt;하지만 고장난 블록이 있을 때마다 &lt;b&gt;실시간으로 패리티 연산을 수행&lt;/b&gt;해야 하므로 성능 저하 발생&lt;/li&gt;
&lt;li data-end=&quot;1850&quot; data-start=&quot;1790&quot;&gt;이 상태에서 추가 장애가 나면 데이터 전체 손실 &amp;rarr; &lt;b&gt;Hot Spare&lt;/b&gt;로 즉시 복구를 시작하는 이유&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1855&quot; data-start=&quot;1852&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1889&quot; data-start=&quot;1857&quot; data-ke-size=&quot;size26&quot;&gt;8. RAID-5가 같은 데이터를 여러 번 저장하나?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2014&quot; data-start=&quot;1890&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1995&quot; data-start=&quot;1890&quot;&gt;&lt;b&gt;아니요&lt;/b&gt;&lt;br /&gt;RAID-5는 RAID-1처럼 같은 데이터를 통째로 복사하지 않습니다.&lt;br /&gt;&amp;rarr; N개의 디스크 중 1개 분량만 패리티로 사용, 나머지는 모두 실제 데이터 저장&lt;/li&gt;
&lt;li data-end=&quot;2014&quot; data-start=&quot;1996&quot;&gt;용량 효율: (N-1)/N&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2019&quot; data-start=&quot;2016&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2039&quot; data-start=&quot;2021&quot; data-ke-size=&quot;size26&quot;&gt;9. RAID 구성별 비교표&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;RAID 종류최소 디스크 수저장 방식장애 허용용량 효율장점단점
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;2478&quot; data-start=&quot;2041&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;2478&quot; data-start=&quot;2178&quot;&gt;
&lt;tr data-end=&quot;2236&quot; data-start=&quot;2178&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2187&quot; data-start=&quot;2178&quot;&gt;RAID 0&lt;/td&gt;
&lt;td data-end=&quot;2191&quot; data-start=&quot;2187&quot; data-col-size=&quot;sm&quot;&gt;2&lt;/td&gt;
&lt;td data-end=&quot;2199&quot; data-start=&quot;2191&quot; data-col-size=&quot;sm&quot;&gt;스트라이핑&lt;/td&gt;
&lt;td data-end=&quot;2204&quot; data-start=&quot;2199&quot; data-col-size=&quot;sm&quot;&gt;0개&lt;/td&gt;
&lt;td data-end=&quot;2211&quot; data-start=&quot;2204&quot; data-col-size=&quot;sm&quot;&gt;100%&lt;/td&gt;
&lt;td data-end=&quot;2222&quot; data-start=&quot;2211&quot; data-col-size=&quot;sm&quot;&gt;속도 매우 빠름&lt;/td&gt;
&lt;td data-end=&quot;2236&quot; data-start=&quot;2222&quot; data-col-size=&quot;sm&quot;&gt;고장 시 전체 손실&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2290&quot; data-start=&quot;2237&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2246&quot; data-start=&quot;2237&quot;&gt;RAID 1&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2250&quot; data-start=&quot;2246&quot;&gt;2&lt;/td&gt;
&lt;td data-end=&quot;2256&quot; data-start=&quot;2250&quot; data-col-size=&quot;sm&quot;&gt;미러링&lt;/td&gt;
&lt;td data-end=&quot;2263&quot; data-start=&quot;2256&quot; data-col-size=&quot;sm&quot;&gt;N/2개&lt;/td&gt;
&lt;td data-end=&quot;2269&quot; data-start=&quot;2263&quot; data-col-size=&quot;sm&quot;&gt;50%&lt;/td&gt;
&lt;td data-end=&quot;2278&quot; data-start=&quot;2269&quot; data-col-size=&quot;sm&quot;&gt;안정성 높음&lt;/td&gt;
&lt;td data-end=&quot;2290&quot; data-start=&quot;2278&quot; data-col-size=&quot;sm&quot;&gt;용량 효율 낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2355&quot; data-start=&quot;2291&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2300&quot; data-start=&quot;2291&quot;&gt;RAID 5&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2304&quot; data-start=&quot;2300&quot;&gt;3&lt;/td&gt;
&lt;td data-end=&quot;2319&quot; data-start=&quot;2304&quot; data-col-size=&quot;sm&quot;&gt;스트라이핑+단일 패리티&lt;/td&gt;
&lt;td data-end=&quot;2324&quot; data-start=&quot;2319&quot; data-col-size=&quot;sm&quot;&gt;1개&lt;/td&gt;
&lt;td data-end=&quot;2334&quot; data-start=&quot;2324&quot; data-col-size=&quot;sm&quot;&gt;(N-1)/N&lt;/td&gt;
&lt;td data-end=&quot;2346&quot; data-start=&quot;2334&quot; data-col-size=&quot;sm&quot;&gt;성능&amp;middot;안정성 균형&lt;/td&gt;
&lt;td data-end=&quot;2355&quot; data-start=&quot;2346&quot; data-col-size=&quot;sm&quot;&gt;쓰기 느림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2417&quot; data-start=&quot;2356&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2365&quot; data-start=&quot;2356&quot;&gt;RAID 6&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2369&quot; data-start=&quot;2365&quot;&gt;4&lt;/td&gt;
&lt;td data-end=&quot;2384&quot; data-start=&quot;2369&quot; data-col-size=&quot;sm&quot;&gt;스트라이핑+이중 패리티&lt;/td&gt;
&lt;td data-end=&quot;2389&quot; data-start=&quot;2384&quot; data-col-size=&quot;sm&quot;&gt;2개&lt;/td&gt;
&lt;td data-end=&quot;2399&quot; data-start=&quot;2389&quot; data-col-size=&quot;sm&quot;&gt;(N-2)/N&lt;/td&gt;
&lt;td data-end=&quot;2406&quot; data-start=&quot;2399&quot; data-col-size=&quot;sm&quot;&gt;안정성&amp;uarr;&lt;/td&gt;
&lt;td data-end=&quot;2417&quot; data-start=&quot;2406&quot; data-col-size=&quot;sm&quot;&gt;쓰기 더 느림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2478&quot; data-start=&quot;2418&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2428&quot; data-start=&quot;2418&quot;&gt;RAID 10&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2432&quot; data-start=&quot;2428&quot;&gt;4&lt;/td&gt;
&lt;td data-end=&quot;2443&quot; data-start=&quot;2432&quot; data-col-size=&quot;sm&quot;&gt;미러+스트라이프&lt;/td&gt;
&lt;td data-end=&quot;2455&quot; data-start=&quot;2443&quot; data-col-size=&quot;sm&quot;&gt;각 미러에서 1개&lt;/td&gt;
&lt;td data-end=&quot;2461&quot; data-start=&quot;2455&quot; data-col-size=&quot;sm&quot;&gt;50%&lt;/td&gt;
&lt;td data-end=&quot;2471&quot; data-start=&quot;2461&quot; data-col-size=&quot;sm&quot;&gt;빠름+안정성&amp;uarr;&lt;/td&gt;
&lt;td data-end=&quot;2478&quot; data-start=&quot;2471&quot; data-col-size=&quot;sm&quot;&gt;비용&amp;uarr;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2483&quot; data-start=&quot;2480&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2495&quot; data-start=&quot;2485&quot; data-ke-size=&quot;size26&quot;&gt;10. 마무리&lt;/h2&gt;
&lt;p data-end=&quot;2659&quot; data-start=&quot;2496&quot; data-ke-size=&quot;size16&quot;&gt;RAID-5는 &lt;b&gt;용량 효율, 성능, 안정성의 균형&lt;/b&gt;이 좋아서 많이 쓰입니다.&lt;br /&gt;하지만 복구 중에는 취약하므로 &lt;b&gt;Hot Spare&lt;/b&gt; 구성으로 위험 기간을 최소화하는 것이 안전합니다.&lt;br /&gt;또한 RAID는 백업이 아니므로, 중요한 데이터는 반드시 &lt;b&gt;별도 백업 체계&lt;/b&gt;를 운영해야 합니다.&lt;/p&gt;</description>
      <category>CS 지식</category>
      <author>ImJay</author>
      <guid isPermaLink="true">https://develop247.tistory.com/624</guid>
      <comments>https://develop247.tistory.com/624#entry624comment</comments>
      <pubDate>Sat, 9 Aug 2025 19:02:52 +0900</pubDate>
    </item>
    <item>
      <title>[기출문제] 리눅스마스터 2급 기출문제</title>
      <link>https://develop247.tistory.com/623</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. 다음 중 소스 코드를 수정해서 만든 2차적 저작물을 반드시 공개해야 하는 라이선스로 알맞은 것은?&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;MPL&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;2. 다음 중 10분 후에 시스템을 종료하는 명령으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;shutdown -h +10&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;3. 다음 중 MPL 라이선스에 대한 설명으로 틀린 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;MPL 소스 코드와 다른 코드를 결합한 경우에 다른 소스 코드를 공개해야 한다.&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;4. 다음 중 리눅스의 섀도 패스워드(Shadow Password)에 대한 설명으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;UNIX 네트워크에서 일반적으로 사용되는 네트워크 인증 기능&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;5. 다음 중 콘솔 모드나 터미널창에서 로그아웃하는 방법으로 틀린 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[Ctrl]+[c]&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;6. 다음에서 설명하는 라이선스로 알맞은 것은?&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: 100%;&quot;&gt;해당 소프트웨어를 누구나 개작할 수 있고, 수정본의 재배포 시에 소스 코드의 비공개가 가능하다. 이 라이선스가 적용된 소프트웨어에는 X Window System, JQuery, Node.js 등이 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;BSD&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;7. 다음은 ls -l 명령을 실행시킨 결과이다. 이에 대한 설명으로 틀린 것은?&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: 100%;&quot;&gt;-rwxr-xr-x ihd project 1024 Aug 13 10:10 client.c&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;소유자는 client.c를 수정할 수 없다.&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;8. 다음 설명에 해당하는 파일로 알맞은 것은?&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: 100%;&quot;&gt;콘솔로 로그인을 시도하는 사용자에게 메시지를 전달하려고 할 때 관리자가 이용하는 파일이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;/etc/motd&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;9. 다음 중 특정 도메인이 사용하는 IP 주소를 조회하는 명령으로 틀린 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;hostname&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;10. 다음 중 ls 명령에서 알파벳 역순으로 출력할 때 사용하는 옵션으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;-r&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;11. 다음 명령의 결과에 대한 설명으로 알맞은 것은?&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: 100%;&quot;&gt;rm -r 디렉터리명&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;12. 다음 중 &quot;/&quot; 디렉터리 바로 하단에 위치하는 디렉터리로 가장 거리가 먼 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;/include&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;13. 다음 명령에 대한 설명으로 맞는 것은?&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: 100%;&quot;&gt;useradd -f 5 ihduser&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;ihduser 계정은 패스워드 사용기한 만료 후 5일 동안만 사용할 수 있도록 한다.&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;14. 다음 설명에 적합한 라이선스로 알맞은 것은?&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: 100%;&quot;&gt;소스 코드를 수정해서 프로그램을 개발한 후에 소스 코드를 공개하지 않고 독점적 소프트웨어로 사용하려고 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;BSD&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;15. 다음 ( 괄호 ) 안에 들어갈 명령어로 알맞은 것은?&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: 100%;&quot;&gt;&lt;span&gt;( ) -l kaituser&lt;/span&gt;&lt;br /&gt;&lt;span&gt;Last password change : Jul 05, 2021&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;chage&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;16. 다음 중 시스템에 로그인한 전체 사용자에게 메시지를 전달할 때 사용하는 명령으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;wall&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;17. 다음 중 ping 명령어를 사용하여 확인할 수 있는 정보로 틀린 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;외부에서 사용 중인 시스템에 PING을 이용한 DOS attack 실행 유무&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;18. 다음 ( 괄호 ) 안에 들어갈 명령어로 알맞은 것은?&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: 100%;&quot;&gt;&lt;span&gt;( ) ihduser&lt;/span&gt;&lt;br /&gt;&lt;span&gt;22:34:05 up 160 days, 9:19, 6 users, load average: 0:00&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;w&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;19. &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;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: 100%;&quot;&gt;&lt;span&gt;( 괄호 ) com1.txt com2.txt&lt;/span&gt;&lt;br /&gt;&lt;span&gt;com1.txt com2.txt differ: byte 8, line 1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;cmp&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;20. 다음 설명에 해당하는 명령으로 알맞은 것은?&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: 100%;&quot;&gt;등록된 DNS 서버를 이용하여 도메인이나 IP를 조회하는 명령이다. 셀 상에서 직접 조회할 수도 있고, 이 명령 실행 후에 대화형으로도 조회할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;nslookup&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;21. 다음 명령의 실행 결과에 대한 설명으로 가장 알맞은 것은?&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: 100%;&quot;&gt;&lt;span&gt;traceroute ihd.or.kr&lt;/span&gt;&lt;br /&gt;&lt;span&gt;traceroute to ihd.or.kr ..&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;ihd.or.kr까지 도달하기 위해 거쳐 가는 연결된 노드들을 확인하고 있다.&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;22. 다음 중 파티션 설정에 관한 설명으로 틀린 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;시스템의 여러 관리정보가 있는 /var는 별도의 파티션으로 분할하는 것이 좋다.&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;23. 텍스트 파일인 a, b의 내용이 아래와 같을 때 ㉠ 명령의 실행에 따른 ㉡의 결과로 알맞은 것은?&amp;nbsp;&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: 100%;&quot;&gt;&lt;span&gt;cat a&lt;/span&gt;&lt;br /&gt;&lt;span&gt;korea team&lt;/span&gt;&lt;br /&gt;&lt;span&gt;cat b&lt;/span&gt;&lt;br /&gt;&lt;span&gt;fighting&lt;/span&gt;&lt;br /&gt;&lt;span&gt;㉠&lt;/span&gt;&lt;br /&gt;&lt;span&gt;cat b&lt;/span&gt;&lt;br /&gt;&lt;span&gt;㉡&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; ㉠ cat a &amp;gt; b ㉡ korea team&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;24. /etc/passwd 파일의 7개 필드 관련 정보를 얻기 위한 명령으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;man 5 passwd&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;25. 다음 중 CD-ROM이 IDE 장치의 Secondary Slave로 연결되어 있을 때, 일반적으로 사용하는 장치 파일명으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;/dev/hdd1&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;26. 다음 중 리눅스 배포판에 대한 설명으로 알맞은 것은?&lt;/p&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;27. 다음 중 리눅스에서 최상위 디렉터리로 알맞은 것은?&lt;/p&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;28. 다음 명령의 실행 결과에 대한 설명으로 가장 알맞은 것은?&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: 100%;&quot;&gt;&lt;span&gt;groupdel ihd1&lt;/span&gt;&lt;br /&gt;&lt;span&gt;groupdel: group 'ihd1' does not exist&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;ihd1 그룹을 제거하고자 하였으나, 해당 그룹이 존재하지 않아서 제거하지 못하였다.&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;29. 다음 주 traceroute 명령과 가장 관련이 깊은 프로토콜로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;ICMP&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;30. 다음 중 리눅스 커널이 들어있는 디렉터리로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;/boot&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;31. 다음 중 시스템의 메모리 사용량 정보를 확인하는 명령으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;free&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;32. 물리적 시스템의 메모리(RAM)의 크기가 1GB일 때 권장할 만한 스왑 공간 용량으로 가장 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1GB&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;33. 다음 중 bash(Bourne Again Sheel)에서 fileB 파일의 뒷 부분에 fileA 파일의 내용을 추가하여 저장하는 명령은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;cat fileA &amp;gt;&amp;gt; fileB&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;34. 다음 중 확장 파티션에 부여될 수 있는 최대 파티션 번호 값으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;5&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;35. 다음 중 리눅스의 특징으로 틀린 것은?&lt;/p&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;36. /etc/passwd 파일의 내용이 다음과 같은 경우 관련 설명으로 틀린 것은?&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 17px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 17px;&quot;&gt;ihd:x:1000:1000::/home/ihd:/bin/false&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;ihd 계정의 패스워드는 암호화되어 /etc/shadows 파일에 저장되어 있다.&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;37. 리눅스의 기본 명령어에 대한 설명으로 틀린 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;내부명령어는 실행 시 새로운 서브 프로세스를 exec하여 실행한다.&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;38. 다음 중 nslokkup 실행 중인 상태에서 메일 서버 정보를 조회하기 위해 입력하는 명령으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;set type=mx&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;39. 다음 중 유닉스(Unix) 운영체제를 개발한 인물들의 조합으로 알맞은 것은?&lt;/p&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;40. 다음 중 파티션과 장치명에 대한 설명으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;hda는 첫 번째 IDE HDD를 나타낸다.&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;41. 섀도 패스워드(Shadow Password)를 사용할 때, /etc/passwd 파일에 포함된 내용으로 틀린 것은?&lt;/p&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;42. 다음 중 논리 파티션에 부여될 수 있는 최소 파티션 번호 값으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;5&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;43. test라는 바이너리 파일을 cat 명령으로 출력하였더니 모니터에 이상한 문자들이 출력되었다. 이렇게 출력된 문자들을 깔금히 모니터에서 제거하고자 할 때 사용하는 명령으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;clear&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;44. 다음 중 디렉터리 내의 파일들의 정보를 화면 단위로 출력할 때 사용하는 명령으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;ls -al | more&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;45. 다음 명령의 결과에 대한 설명으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mkdir .fileA&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;ls 명령에서 -a 옵션을 이용하면 .fileA 디렉터리를 찾을 수 있다.&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;46. 다음 중 사용 중인 호스트에서 ihd.or.kr까지 연결되는 과정을 알고 싶을 때 사용하는 명령으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;traceroute ihd.or.kr&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;47. 다음 중 구글이 리눅스 커널 기반으로 만든 모바일 운영체제로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Android&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;48. 다음 중 현재 사용 중인 가상 콘솔창 정보를 확인하는 명령으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;tty&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;49. 다음 중 Bash 셸에서 설정된 PATH 환경변수에 /usr/local/bin이라는 새로운 경로를 추가하기 위한 명령으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;export PATH=/usr/local/bin&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;50. 다음 명령어의 실행 결과에 대한 설명으로 알맞은 것은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cal&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JhIfs/btsNJYOJOwg/FKdV7nffyrFzgbWkxWfIV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JhIfs/btsNJYOJOwg/FKdV7nffyrFzgbWkxWfIV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JhIfs/btsNJYOJOwg/FKdV7nffyrFzgbWkxWfIV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJhIfs%2FbtsNJYOJOwg%2FFKdV7nffyrFzgbWkxWfIV0%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;752&quot; height=&quot;370&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&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;오답이 8문제 있습니다.   양해 부탁드립니다.&amp;nbsp;&lt;/p&gt;</description>
      <category>CS 지식</category>
      <category>리눅스마스터 2급</category>
      <category>리눅스마스터 2급 기출문제</category>
      <author>ImJay</author>
      <guid isPermaLink="true">https://develop247.tistory.com/623</guid>
      <comments>https://develop247.tistory.com/623#entry623comment</comments>
      <pubDate>Tue, 6 May 2025 14:20:55 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ/Java] 12422. 창문 깨기 (Large)</title>
      <link>https://develop247.tistory.com/622</link>
      <description>&lt;h2 data-end=&quot;393&quot; data-start=&quot;358&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  [BOJ/Java] 12422. 창문 깨기 (Large)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;468&quot; data-start=&quot;409&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://www.acmicpc.net/problem/12422&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;문제 링크&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr data-end=&quot;473&quot; data-start=&quot;470&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;493&quot; data-start=&quot;475&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문제 해석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;635&quot; data-start=&quot;495&quot; data-ke-size=&quot;size16&quot;&gt;본 문제는 K개의 창문으로 둘러싸인 방에서 진행되는 상황을 모델링한다. 창문은 기본적으로 돌 한 개에 깨지나, H개의 창문은 강화되어 있어 첫 번째 돌은 견디고 두 번째 돌에 깨진다. 모임 전에 M명의 일꾼이 각각 무작위로 한 창문을 선택하여 추가 강화 작업을 수행하며, 한 번 강화할 때마다 해당 창문은 돌을 한 번 더 버틸 수 있다. 모임 당일에는 N명의 악당이 각자 한 개의 창문을 선택하여 돌을 던지며, 이미 깨진 창문에 돌이 던져지면 돌은 그대로 통과한다. &lt;br /&gt;&lt;br /&gt;문제의 목표는 모임 후에 최소 한 개 이상의 창문이 깨질 확률을 구하는 것이다.&lt;/p&gt;
&lt;hr data-end=&quot;473&quot; data-start=&quot;470&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;493&quot; data-start=&quot;475&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 풀이 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;635&quot; data-start=&quot;495&quot; data-ke-size=&quot;size16&quot;&gt;본 문제는 각 창문의 내구성을 강화와 돌 던지기의 결과로 모델링하여, 전체 방이 안전한 상태(즉, 한 개의 창문도 깨지지 않은 상태)가 될 확률을 계산한 후, 1에서 해당 확률을 빼어 창문이 깨질 확률을 구하는 문제이다. 문제 해결 과정은 다음과 같이 진행된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;680&quot; data-start=&quot;635&quot; data-ke-size=&quot;size16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;창문 그룹 분리&lt;/b&gt;: &lt;br /&gt;K개의 창문 중 H개는 처음부터 강화된 창문(그룹 A)으로 기본 내구성이 2이며, 나머지 K &amp;minus; H개는 기본 창문(그룹 B)으로 기본 내구성이 1이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동적 계획법(DP)을 통한 경우의 수 계산&lt;/b&gt;: &lt;br /&gt;각 창문에 대해 추가 강화 횟수(&lt;i&gt;dr&lt;/i&gt;)와 돌에 맞은 횟수(&lt;i&gt;ds&lt;/i&gt;)의 조합으로 기여도를 계산한다. 각 창문은 다음의 안전 조건을 만족해야 한다. &lt;br /&gt;- 그룹 A(강화 창문): &lt;code&gt;ds &amp;le; dr + 1&lt;/code&gt; &lt;br /&gt;- 그룹 B(기본 창문): &lt;code&gt;ds &amp;le; dr&lt;/code&gt; &lt;br /&gt;각 창문에 대해 1/(dr! &amp;times; ds!)의 가중치를 부여하는 생성함수를 고려하며, 전체 창문에 대한 경우의 수는 두 그룹의 DP 결과를 합성(convolution)하여 구한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최종 확률 계산&lt;/b&gt;: &lt;br /&gt;모임에서 총 M회의 강화와 N회의 돌 던지기가 이루어질 때, 안전한 상태의 확률 &lt;code&gt;P_safe&lt;/code&gt;는 아래와 같이 계산된다. &lt;br /&gt;&lt;br /&gt;&lt;code&gt;P_safe = (M! &amp;times; N! / K^(M+N)) &amp;times; S_total&lt;/code&gt; &lt;br /&gt;&lt;br /&gt;여기서 &lt;i&gt;S_total&lt;/i&gt;은 전체 generating function에서 x^M&amp;middot;y^N 항의 계수이다. 최종적으로 창문이 깨질 확률은 &lt;code&gt;1 &amp;minus; P_safe&lt;/code&gt;로 구한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;473&quot; data-start=&quot;470&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;493&quot; data-start=&quot;475&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드 구현 (Java)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1741854811723&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    // M과 N의 최대값이 100임을 고려하여 MAX를 110으로 설정한다.
    static final int MAX = 110;
    
    public static void main(String[] args) throws Exception {
        // 빠른 입출력을 위하여 BufferedReader와 PrintWriter를 사용한다.
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(System.out);
        
        int T = Integer.parseInt(br.readLine().trim());
        
        // 0부터 MAX-1까지의 팩토리얼과 로그 팩토리얼을 미리 계산한다.
        double[] fact = new double[MAX];
        double[] logFact = new double[MAX];
        fact[0] = 1.0;
        logFact[0] = 0.0;
        for (int i = 1; i &amp;lt; MAX; i++) {
            fact[i] = fact[i - 1] * i;
            logFact[i] = logFact[i - 1] + Math.log(i);
        }
        // 0부터 MAX-1까지의 1/n! 값을 미리 계산한다.
        double[] invFact = new double[MAX];
        for (int i = 0; i &amp;lt; MAX; i++) {
            invFact[i] = 1.0 / fact[i];
        }
        
        // 각 테스트 케이스를 처리한다.
        for (int t = 1; t &amp;lt;= T; t++) {
            String line = br.readLine();
            String[] parts = line.trim().split(&quot;\\s+&quot;);
            int K = Integer.parseInt(parts[0]); // 창문의 총 개수이다.
            int N = Integer.parseInt(parts[1]); // 돌을 던지는 악당의 수이다.
            int M = Integer.parseInt(parts[2]); // 강화하는 일꾼의 수이다.
            int H = Integer.parseInt(parts[3]); // 처음부터 강화된 창문의 개수이다.
            
            // 그룹 A는 강화된 창문, 그룹 B는 기본 창문이다.
            int countA = H;
            int countB = K - H;
            
            // 그룹 A에 대한 DP 테이블을 구성한다.
            // dpA[r][s]는 그룹 A의 창문에 대해 총 r회의 추가 강화와 s회의 돌 던지기가 이루어졌을 때의 기여도 합이다.
            // 그룹 A의 안전 조건은 ds &amp;le; r + 1이다.
            double[][] dpA = new double[M + 1][N + 1];
            dpA[0][0] = 1.0;
            for (int i = 0; i &amp;lt; countA; i++) {
                double[][] newdp = new double[M + 1][N + 1];
                // 현재까지의 강화 횟수(r0)와 돌 던진 횟수(s0)에 대하여 순회한다.
                for (int r0 = 0; r0 &amp;lt;= M; r0++) {
                    for (int s0 = 0; s0 &amp;lt;= N; s0++) {
                        double cur = dpA[r0][s0];
                        if (cur == 0.0) continue;
                        int remR = M - r0; // 남은 강화 횟수이다.
                        int remS = N - s0; // 남은 돌 던지기 횟수이다.
                        // 새 창문에 대해 가능한 (dr, ds) 조합을 시도한다.
                        // 단, 안전 조건은 ds &amp;le; dr + 1이다.
                        for (int dr = 0; dr &amp;lt;= remR; dr++) {
                            int maxDs = Math.min(remS, dr + 1);
                            for (int ds = 0; ds &amp;lt;= maxDs; ds++) {
                                // 한 창문에서의 기여도는 1/(dr! &amp;times; ds!)이다.
                                double add = invFact[dr] * invFact[ds];
                                newdp[r0 + dr][s0 + ds] += cur * add;
                            }
                        }
                    }
                }
                dpA = newdp;
            }
            
            // 그룹 B에 대한 DP 테이블을 구성한다.
            // dpB[r][s]는 그룹 B의 창문에 대해 총 r회의 추가 강화와 s회의 돌 던지기가 이루어졌을 때의 기여도 합이다.
            // 그룹 B의 안전 조건은 ds &amp;le; r이다.
            double[][] dpB = new double[M + 1][N + 1];
            dpB[0][0] = 1.0;
            for (int i = 0; i &amp;lt; countB; i++) {
                double[][] newdp = new double[M + 1][N + 1];
                for (int r0 = 0; r0 &amp;lt;= M; r0++) {
                    for (int s0 = 0; s0 &amp;lt;= N; s0++) {
                        double cur = dpB[r0][s0];
                        if (cur == 0.0) continue;
                        int remR = M - r0;
                        int remS = N - s0;
                        // 그룹 B의 경우, 가능한 (dr, ds) 조합은 ds &amp;le; dr를 만족한다.
                        for (int dr = 0; dr &amp;lt;= remR; dr++) {
                            int maxDs = Math.min(remS, dr);
                            for (int ds = 0; ds &amp;lt;= maxDs; ds++) {
                                double add = invFact[dr] * invFact[ds];
                                newdp[r0 + dr][s0 + ds] += cur * add;
                            }
                        }
                    }
                }
                dpB = newdp;
            }
            
            // 두 그룹의 DP 결과를 합성하여 전체 generating function에서 x^M &amp;middot; y^N 항의 계수 S_total을 구한다.
            double S_total = 0.0;
            for (int rA = 0; rA &amp;lt;= M; rA++) {
                for (int sA = 0; sA &amp;lt;= N; sA++) {
                    int rB = M - rA;
                    int sB = N - sA;
                    if (rB &amp;lt; 0 || sB &amp;lt; 0) continue;
                    S_total += dpA[rA][sA] * dpB[rB][sB];
                }
            }
            
            // 전체 방이 안전한 상태(즉, 한 개의 창문도 깨지지 않은 상태)가 될 확률 P_safe는 아래와 같이 계산된다.
            // P_safe = (M! &amp;times; N! / K^(M+N)) &amp;times; S_total
            // 큰 수의 계산을 위하여 로그를 사용하여 계산한다.
            double logP_safe;
            if (S_total == 0.0) {
                logP_safe = Double.NEGATIVE_INFINITY;
            } else {
                logP_safe = logFact[M] + logFact[N] + Math.log(S_total) - (M + N) * Math.log(K);
            }
            double P_safe = (logP_safe == Double.NEGATIVE_INFINITY ? 0.0 : Math.exp(logP_safe));
            double ans = 1.0 - P_safe;
            if (ans &amp;lt; 0) ans = 0.0;
            if (ans &amp;gt; 1) ans = 1.0;
            
            out.printf(&quot;Case #%d: %.8f%n&quot;, t, ans);
        }
        out.flush();
        out.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;1085&quot; data-start=&quot;1082&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1120&quot; data-start=&quot;1086&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⏱️ 시간 복잡도 분석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1160&quot; data-start=&quot;1121&quot; data-ke-size=&quot;size16&quot;&gt;본 알고리즘은 각 창문마다 가능한 강화와 돌 던지기 조합을 2차원 동적 계획법(DP)을 사용하여 계산한다. DP 배열의 크기는 (M+1)&amp;times;(N+1)이며, 내부 반복문에서는 강화 횟수와 돌 던지기 횟수를 모두 고려한다. &lt;br /&gt;&lt;br /&gt;그룹 A와 그룹 B의 창문 수의 합은 K(최대 200)개이므로, 전체 시간 복잡도는 제한 내에서 충분히 계산 가능하다.&lt;/p&gt;
&lt;hr data-end=&quot;1165&quot; data-start=&quot;1162&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1200&quot; data-start=&quot;1166&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  느낀점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1240&quot; data-start=&quot;1201&quot; data-ke-size=&quot;size16&quot;&gt;본 문제는 단순해 보이는 상황을 수학적 모델링과 동적 계획법을 통하여 효과적으로 해결한 모범적인 사례이다. 각 창문의 내구성을 강화와 돌 던지기의 조합으로 세분화하여 안전 조건을 엄밀하게 정의한 점이 인상적이다. 또한, 큰 수의 연산에서 로그를 사용하여 오버플로우를 방지한 접근법은 계산의 안정성을 보장한다. 이러한 방법론은 복잡한 조합론 문제를 효율적으로 해결하는 데 큰 도움이 된다.&lt;/p&gt;</description>
      <category>알고리즘/BOJ - Java</category>
      <category>12422</category>
      <category>12422 boj</category>
      <category>12422 java</category>
      <category>12422 백준</category>
      <category>12422 자바</category>
      <category>boj 12422</category>
      <category>java 12422</category>
      <category>[boj/java] 12422. 창문 깨기 (large)</category>
      <category>백준 12422</category>
      <category>자바 12422</category>
      <author>ImJay</author>
      <guid isPermaLink="true">https://develop247.tistory.com/622</guid>
      <comments>https://develop247.tistory.com/622#entry622comment</comments>
      <pubDate>Fri, 14 Mar 2025 15:42:18 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ/Java] 3628. Knockdown</title>
      <link>https://develop247.tistory.com/621</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  [BOJ/Java] 3628. Knockdown  &lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 링크&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3628&quot;&gt;  3628번 - Knockdown&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문제 해석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &lt;b&gt;전 세계를 파괴&lt;/b&gt;하기 위해 특정 지점에 &lt;b&gt;폭탄&lt;/b&gt;을 배치하는 시나리오를 다룬다.  &lt;br /&gt;각 폭탄은 &lt;b&gt;동일한 파괴 반경&lt;/b&gt;을 가지며, 전 세계를 파괴할 수 있는 &lt;b&gt;최소한의 반경&lt;/b&gt;을 찾아야 한다.  ➡️ &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지구는 반지름이 &lt;b&gt;1인 단위 구(Sphere)&lt;/b&gt; 로 모델링되며, 폭탄의 위치는 &lt;b&gt;위도(latitude, &amp;phi;)와 경도(longitude, &amp;lambda;)&lt;/b&gt; 로 주어진다.&lt;br /&gt;거리는 &lt;b&gt;구면 거리(Spherical Distance)&lt;/b&gt; 로 측정된다.  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  풀이 과정&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1️⃣ 구 좌표 변환 (위도/경도 &amp;rarr; 3D 좌표)  ️➡️ &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지구는 구형이므로, 주어진 위도와 경도를 &lt;b&gt;3D 좌표계&lt;/b&gt;로 변환해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위도(&amp;phi;)와 경도(&amp;lambda;)를 사용하여 다음 공식을 이용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1741932760645&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = cos(&amp;phi;) * cos(&amp;lambda;)  
y = cos(&amp;phi;) * sin(&amp;lambda;)  
z = sin(&amp;phi;)&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;b&gt;3D 공간의 점(Point)&lt;/b&gt; 으로 변환된다.  ️ &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2️⃣ 최소 반경을 찾기 위한 접근 방식  &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;목표&lt;/b&gt;는 &lt;b&gt;어느 지점에서든 가장 가까운 폭탄까지의 거리&lt;/b&gt;가 최소 반경 &lt;span&gt;r&lt;/span&gt; 이하가 되도록 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;지구상의 모든 점이 어떤 폭탄과도 최대 &lt;span&gt;r&lt;/span&gt; 거리 이내에 있도록 보장해야 한다.&lt;/b&gt;&lt;br /&gt;이를 해결하기 위해, &lt;b&gt;이분 탐색(이진 탐색) + 기하학적 계산&lt;/b&gt;을 활용한다. ⚙️ &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  고려해야 할 후보 점들&lt;/b&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;각 폭탄의 반대편 점 (Antipodal Point)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;폭탄이 위치한 점의 &lt;b&gt;정반대&lt;/b&gt; 지점이 유력한 후보가 된다.&lt;/li&gt;
&lt;li&gt;이는 &lt;span&gt;(&amp;minus;x,&amp;minus;y,&amp;minus;z)(-x, -y, -z)&lt;/span&gt; 로 쉽게 구할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;두 폭탄 사이의 중간점 (Pairwise Midpoint)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 개의 폭탄이 있다면, 이들의 &lt;b&gt;중간 벡터 방향&lt;/b&gt;이 적절한 후보가 될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;세 폭탄으로 이루어진 정점 (Triple-wise Normal Vector)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세 개의 폭탄이 형성하는 평면의 법선 벡터가 유력한 후보가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  최적화 목표: min max(dot product)&lt;/b&gt;&lt;b&gt;&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;모든 폭탄과의 최대 내적(dot product)&lt;/b&gt; 값을 최소화하는 &lt;span&gt;p&lt;/span&gt;를 찾아야 한다.&lt;br /&gt;이 값이 작을수록 최적의 반경이 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소 반경은 &lt;b&gt;acos(최적의 내적 값)&lt;/b&gt; 으로 계산된다.&lt;/p&gt;
&lt;pre id=&quot;code_1741932791717&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;r = arccos(min max(p &amp;middot; bomb_i))&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드 구현 (Java)&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static final double EPS = 1e-9;  // 오차 허용 범위

    // 3차원 좌표를 저장하는 클래스
    static class Point {
        double x, y, z;
        Point(double x, double y, double z) {
            this.x = x; this.y = y; this.z = z;
        }
        Point add(Point p) { return new Point(x + p.x, y + p.y, z + p.z); }
        Point subtract(Point p) { return new Point(x - p.x, y - p.y, z - p.z); }
        Point multiply(double s) { return new Point(x * s, y * s, z * s); }
        double dot(Point p) { return x * p.x + y * p.y + z * p.z; }  // 내적
        Point cross(Point p) {  // 외적
            return new Point(y * p.z - z * p.y,
                             z * p.x - x * p.z,
                             x * p.y - y * p.x);
        }
        double norm() { return Math.sqrt(x * x + y * y + z * z); }  // 벡터 크기
        Point normalize() {  // 단위 벡터 변환
            double n = norm();
            if(n &amp;lt; EPS) return new Point(0, 0, 0);
            return new Point(x / n, y / n, z / n);
        }
    }

    static double candidateValue(Point p, Point[] bombs) {
        double val = -1e9;
        for (Point b : bombs) {
            val = Math.max(val, p.dot(b));
        }
        return val;
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(System.out);

        int n = Integer.parseInt(br.readLine().trim());
        Point[] bombs = new Point[n];

        for (int i = 0; i &amp;lt; n; i++) {
            String[] parts = br.readLine().trim().split(&quot;\\s+&quot;);
            double lat = Math.toRadians(Double.parseDouble(parts[0]));
            double lon = Math.toRadians(Double.parseDouble(parts[1]));
            double cosLat = Math.cos(lat);
            bombs[i] = new Point(cosLat * Math.cos(lon), cosLat * Math.sin(lon), Math.sin(lat));
        }

        double best = 1e9;

        for (int i = 0; i &amp;lt; n; i++) {
            Point cand = new Point(-bombs[i].x, -bombs[i].y, -bombs[i].z);
            best = Math.min(best, candidateValue(cand, bombs));
        }

        for (int i = 0; i &amp;lt; n; i++) {
            for (int j = i+1; j &amp;lt; n; j++) {
                Point sum = bombs[i].add(bombs[j]);
                if (sum.norm() &amp;gt; EPS) {
                    best = Math.min(best, candidateValue(sum.multiply(-1).normalize(), bombs));
                }
            }
        }

        for (int i = 0; i &amp;lt; n; i++) {
            for (int j = i+1; j &amp;lt; n; j++) {
                for (int k = j+1; k &amp;lt; n; k++) {
                    Point cross = bombs[i].subtract(bombs[j]).cross(bombs[i].subtract(bombs[k]));
                    if (cross.norm() &amp;gt; EPS) {
                        best = Math.min(best, candidateValue(cross.normalize(), bombs));
                        best = Math.min(best, candidateValue(cross.multiply(-1).normalize(), bombs));
                    }
                }
            }
        }

        best = Math.max(-1, Math.min(1, best));
        double ans = Math.acos(best);
        out.printf(&quot;%.6f\n&quot;, ans);
        out.flush();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⏳ 시간 복잡도 분석&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;각 폭탄마다 후보 탐색&lt;/b&gt; &amp;rarr; &lt;span&gt;O(n)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;두 개의 폭탄을 이용한 후보 탐색&lt;/b&gt; &amp;rarr; &lt;span&gt;O(n^2)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;세 개의 폭탄을 이용한 후보 탐색&lt;/b&gt; &amp;rarr; &lt;span&gt;O(n^3)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최대 폭탄 개수 &lt;span&gt;n=20n = 20&lt;/span&gt;&lt;/b&gt; 이므로 최악의 경우 &lt;span&gt;O(8000)&lt;/span&gt;, 충분히 실행 가능 ✅&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  느낀 점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;구면 기하학&lt;/b&gt;과 &lt;b&gt;이분 탐색&lt;/b&gt;을 조합한 흥미로운 문제였다!  ✨&lt;br /&gt;✅ 폭탄이 최소한으로 커버해야 할 범위를 찾는 과정이 기하학적으로 재미있었다.  &lt;br /&gt;✅ &lt;b&gt;벡터 연산(내적, 외적, 단위 벡터 변환)&lt;/b&gt; 의 중요성을 다시 한번 체감했다!  &lt;/p&gt;</description>
      <category>알고리즘/BOJ - Java</category>
      <category>3628</category>
      <category>3628 boj</category>
      <category>3628 java</category>
      <category>3628 백준</category>
      <category>3628 자바</category>
      <category>boj 3628</category>
      <category>java 3628</category>
      <category>[boj/java] 3628. knockdown</category>
      <category>백준 3628</category>
      <category>자바 3628</category>
      <author>ImJay</author>
      <guid isPermaLink="true">https://develop247.tistory.com/621</guid>
      <comments>https://develop247.tistory.com/621#entry621comment</comments>
      <pubDate>Fri, 14 Mar 2025 15:12:04 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ/Java] 6902. Orko</title>
      <link>https://develop247.tistory.com/620</link>
      <description>&lt;h2 data-end=&quot;393&quot; data-start=&quot;358&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  [BOJ/Java] 6902. Orko&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;468&quot; data-start=&quot;409&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;문제 링크&lt;/b&gt;: &lt;a href=&quot;https://www.acmicpc.net/problem/6902&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문제 보기&lt;/a&gt;&lt;/p&gt;
&lt;hr data-end=&quot;473&quot; data-start=&quot;470&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;493&quot; data-start=&quot;475&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문제 해석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;635&quot; data-start=&quot;495&quot; data-ke-size=&quot;size16&quot;&gt;Dumitru가 이상한 꿈을 꾸게 된다. 꿈 속에서 문이 잠긴 방에 갇힌 Dumitru는 n개의 박스를 발견하며, 각 박스에는 m개의 접시가 있다. 각 접시에는 1 이상의 정수가 적혀 있다. 그러던 중, 두 개의 정수 k와 l이 적힌 쪽지를 발견하고, 쪽지에는 아래와 같은 작업을 하라는 지시가 적혀 있다.&lt;/p&gt;
&lt;p data-end=&quot;635&quot; data-start=&quot;495&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;650&quot; data-start=&quot;637&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1단계&lt;/b&gt;: 첫 번째 박스부터 n번째 박스까지 순서대로 각 박스에서 한 개의 접시를 선택한다. 선택한 접시의 숫자를 노트에 기록한 후, 해당 접시의 숫자를 1로 변경한다.&lt;/p&gt;
&lt;p data-end=&quot;650&quot; data-start=&quot;637&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;680&quot; data-start=&quot;650&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2단계&lt;/b&gt;: n-1번째 박스부터 두 번째 박스까지 역순으로 각 박스에서 한 개의 접시를 선택한다.&lt;/p&gt;
&lt;p data-end=&quot;715&quot; data-start=&quot;680&quot; data-ke-size=&quot;size16&quot;&gt;목표는 노트에 기록된 숫자들의 곱이 k로 나누어 떨어지도록 하는 선택 방법의 총 가짓수 T를 구하고, T를 l로 나눈 나머지를 출력하는 것이다.&lt;/p&gt;
&lt;hr data-end=&quot;807&quot; data-start=&quot;804&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;840&quot; data-start=&quot;810&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 풀이 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;875&quot; data-start=&quot;841&quot; data-ke-size=&quot;size16&quot;&gt;먼저, n, m, k, l과 각 박스의 접시 값들을 입력받는다. 문제의 핵심은 k의 약수를 상태로 활용하는 것이다. k의 모든 약수를 구한 뒤, 각 약수에 인덱스를 매핑하여 상태 전이 테이블을 구성한다.&lt;/p&gt;
&lt;p data-end=&quot;875&quot; data-start=&quot;841&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;915&quot; data-start=&quot;876&quot; data-ke-size=&quot;size16&quot;&gt;각 박스마다 접시 선택의 경우의 수를 계산한다. 첫 번째 박스와 마지막 박스는 접시를 한 번만 선택하므로, 각 접시가 기여하는 값 그대로 반영된다.&lt;/p&gt;
&lt;p data-end=&quot;915&quot; data-start=&quot;876&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;960&quot; data-start=&quot;916&quot; data-ke-size=&quot;size16&quot;&gt;중간 박스는 접시를 두 번 선택하는 경우가 있으므로, 첫 번째 선택(접시의 원래 숫자 기록)과 두 번째 선택(접시 숫자가 1로 변경됨)을 모두 고려하여 경우의 수를 누적한다.&lt;/p&gt;
&lt;p data-end=&quot;960&quot; data-start=&quot;916&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1000&quot; data-start=&quot;961&quot; data-ke-size=&quot;size16&quot;&gt;동적 계획법(DP)을 이용해 각 박스에서 계산된 경우의 수를 누적하며, 현재까지 선택한 접시들의 곱의 gcd(k, &amp;middot;) 값을 상태로 표현하여 전이를 수행한다.&lt;/p&gt;
&lt;p data-end=&quot;1000&quot; data-start=&quot;961&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1040&quot; data-start=&quot;1001&quot; data-ke-size=&quot;size16&quot;&gt;모든 박스를 처리한 후, 최종 상태가 k(즉, k로 나누어 떨어지는 상태)가 되는 경우의 수를 l로 나눈 나머지를 출력한다.&lt;/p&gt;
&lt;hr data-end=&quot;1045&quot; data-start=&quot;1042&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1080&quot; data-start=&quot;1046&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드 구현 (Java)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1741854811723&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*; 
import java.util.*;

public class Main {

// 유클리드 호제법을 이용하여 최대공약수(GCD)를 구하는 메소드
static long gcd(long a, long b) {
    // b가 0이 될 때까지 반복
    while (b != 0) {
        long temp = a % b;  // a를 b로 나눈 나머지를 임시 저장
        a = b;              // a에 b 값을 대입
        b = temp;           // b에 나머지 값을 대입
    }
    return a;             // 최대공약수 반환
}

public static void main(String[] args) throws Exception {
    // 입력을 위한 BufferedReader 초기화
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    
    // 첫 번째 줄 입력: 박스 개수 n과 각 박스당 접시 개수 m
    StringTokenizer st = new StringTokenizer(br.readLine());
    int n = Integer.parseInt(st.nextToken());
    int m = Integer.parseInt(st.nextToken());
    
    // 두 번째 줄 입력: 목표 정수 k와 모듈러 연산에 사용할 값 mod
    st = new StringTokenizer(br.readLine());
    int k = Integer.parseInt(st.nextToken());
    int mod = Integer.parseInt(st.nextToken());
    
    // k의 모든 약수를 구함 (각 상태로 활용)
    ArrayList&amp;lt;Integer&amp;gt; dList = new ArrayList&amp;lt;&amp;gt;();
    for (int i = 1; i * i &amp;lt;= k; i++) {
        if (k % i == 0) {
            dList.add(i);
            if (i * i != k) {
                dList.add(k / i);
            }
        }
    }
    
    // 약수를 오름차순으로 정렬
    Collections.sort(dList);
    int dCount = dList.size();
    
    // 약수를 배열에 저장
    int[] dArr = new int[dCount];
    for (int i = 0; i &amp;lt; dCount; i++) {
        dArr[i] = dList.get(i);
    }
    
    // 각 약수에 대해 인덱스를 매핑 (상태 전이 테이블 구성에 활용)
    HashMap&amp;lt;Integer, Integer&amp;gt; dIndex = new HashMap&amp;lt;&amp;gt;();
    for (int i = 0; i &amp;lt; dCount; i++) {
        dIndex.put(dArr[i], i);
    }
    
    // 상태 전이 테이블 구성: 두 상태의 곱의 결과에 대해 gcd(k, 곱) 값을 미리 계산
    int[][] mult = new int[dCount][dCount];
    for (int i = 0; i &amp;lt; dCount; i++) {
        for (int j = 0; j &amp;lt; dCount; j++) {
            long product = (long) dArr[i] * dArr[j];
            int g = (int) gcd(product, k);
            mult[i][j] = dIndex.get(g);
        }
    }
    
    // DP 배열 초기화: dp[i]는 현재 상태가 dArr[i]일 때의 경우의 수
    int[] dp = new int[dCount];
    // 초기 상태는 접시 선택 전 곱이 1인 상태
    dp[dIndex.get(1)] = 1;
    
    // 각 박스를 순서대로 처리
    for (int box = 1; box &amp;lt;= n; box++) {
        
        // 현재 박스의 접시들을 읽어, 각 접시의 숫자에 따른 상태(약수 상태)의 빈도수를 계산
        int[] freq = new int[dCount];
        st = new StringTokenizer(br.readLine());
        for (int j = 0; j &amp;lt; m; j++) {
            int plateValue = Integer.parseInt(st.nextToken());
            int g = (int) gcd(plateValue, k);
            int idx = dIndex.get(g);
            freq[idx]++;  // 해당 상태의 빈도수 증가
        }
        
        // 현재 박스에서 선택 가능한 경우의 수 분포를 저장할 배열
        int[] boxDist = new int[dCount];
        
        // 첫 번째 박스와 마지막 박스는 접시를 한 번만 선택하므로 단순히 freq 값을 사용
        if (box == 1 || box == n) {
            for (int i = 0; i &amp;lt; dCount; i++) {
                boxDist[i] = freq[i] % mod;
            }
        } else {
            // 중간 박스는 접시를 두 번 선택해야 함
            // 첫 번째 선택: 접시의 원래 숫자를 기록
            // 두 번째 선택: 해당 접시의 숫자가 1로 변함
            int[] doubleDist = new int[dCount];
            
            // 첫 번째 선택의 경우의 수를 추가
            for (int i = 0; i &amp;lt; dCount; i++) {
                doubleDist[i] = (doubleDist[i] + freq[i]) % mod;
            }
            
            // 두 번째 선택에 대해, 같은 접시를 두 번 선택하는 경우와 다른 접시를 선택하는 경우를 모두 고려
            for (int i = 0; i &amp;lt; dCount; i++) {
                for (int j = 0; j &amp;lt; dCount; j++) {
                    long ways;
                    if (i == j) {
                        // 같은 접시를 두 번 선택하는 경우
                        ways = (long) freq[i] * (freq[i] - 1);
                    } else {
                        // 서로 다른 접시를 선택하는 경우
                        ways = (long) freq[i] * freq[j];
                    }
                    if (ways == 0)
                        continue;
                    
                    // 두 선택의 결과 상태를 상태 전이 테이블을 이용해 계산
                    int newIndex = mult[i][j];
                    doubleDist[newIndex] = (int) ((doubleDist[newIndex] + ways) % mod);
                }
            }
            boxDist = doubleDist;
        }
        
        // 현재까지의 dp 배열과 현재 박스의 경우의 수(boxDist)를 이용하여 상태 전이를 수행
        int[] nextDP = new int[dCount];
        for (int i = 0; i &amp;lt; dCount; i++) {
            if (dp[i] == 0)
                continue;
            for (int j = 0; j &amp;lt; dCount; j++) {
                if (boxDist[j] == 0)
                    continue;
                int nextState = mult[i][j];  // 상태 전이
                nextDP[nextState] = (int) ((nextDP[nextState] + (long) dp[i] * boxDist[j]) % mod);
            }
        }
        
        // dp 배열을 업데이트
        dp = nextDP;
    }
    
    // 최종적으로 목표 상태 k(즉, k로 나누어 떨어지는 상태)의 경우의 수를 mod로 나눈 나머지를 출력
    System.out.println(dp[dIndex.get(k)] % mod);
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;1085&quot; data-start=&quot;1082&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1120&quot; data-start=&quot;1086&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⏱️ 시간 복잡도 분석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1160&quot; data-start=&quot;1121&quot; data-ke-size=&quot;size16&quot;&gt;문제는 각 박스마다 m개의 접시를 처리하고, k의 약수 개수를 d라 할 때 전체 시간 복잡도는 O(n*m + n*d&amp;sup2;)이다.&lt;br /&gt;n은 최대 200, m은 최대 10,000이며, d는 k의 약수 개수로 보통 &amp;radic;k에 가까워 제한 내에서 충분히 계산 가능하다.&lt;/p&gt;
&lt;hr data-end=&quot;1165&quot; data-start=&quot;1162&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1200&quot; data-start=&quot;1166&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  느낀점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1240&quot; data-start=&quot;1201&quot; data-ke-size=&quot;size16&quot;&gt;문제 해결 과정에서 각 박스에서 접시 선택에 따른 상태 전이를 명확하게 처리하는 것이 핵심이었다. &lt;br /&gt;정수론과 gcd를 활용하여 k의 약수를 상태로 처리한 점, 그리고 중간 박스에서 두 번 선택하는 경우를 세밀하게 고려한 점이 인상적이었다.&lt;/p&gt;</description>
      <category>알고리즘/BOJ - Java</category>
      <category>6902</category>
      <category>6902 boj</category>
      <category>6902 java</category>
      <category>6902 백준</category>
      <category>6902 자바</category>
      <category>boj 6902</category>
      <category>java 6902</category>
      <category>[boj/java] 6902. orko</category>
      <category>백준 6902</category>
      <category>자바 6902</category>
      <author>ImJay</author>
      <guid isPermaLink="true">https://develop247.tistory.com/620</guid>
      <comments>https://develop247.tistory.com/620#entry620comment</comments>
      <pubDate>Thu, 13 Mar 2025 17:35:41 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ/Java] 5266. Strange Dream</title>
      <link>https://develop247.tistory.com/619</link>
      <description>&lt;h2 data-end=&quot;393&quot; data-start=&quot;358&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  [BOJ/Java] 5266. Strange Dream&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;468&quot; data-start=&quot;409&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;문제 링크&lt;/b&gt;: &lt;a href=&quot;https://www.acmicpc.net/problem/5266&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문제 보기&lt;/a&gt;&lt;/p&gt;
&lt;hr data-end=&quot;473&quot; data-start=&quot;470&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;493&quot; data-start=&quot;475&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문제 해석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;635&quot; data-start=&quot;495&quot; data-ke-size=&quot;size16&quot;&gt;Dumitru가 이상한 꿈을 꿨다. 꿈속에서 문이 잠긴 방에 갇힌 Dumitru는 n개의 박스를 발견했다. 각 박스에는 m개의 접시가 있었고, 각 접시에는 1 이상의 정수가 적혀 있었다. 그러던 중 두 개의 정수 k와 l이 적힌 쪽지를 발견했다. 쪽지에는 다음과 같은 작업을 하라고 되어 있었다.&lt;/p&gt;
&lt;p data-end=&quot;635&quot; data-start=&quot;495&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;650&quot; data-start=&quot;637&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1단계&lt;/b&gt;: 첫 번째 박스부터 n번째 박스까지 순서대로 각 박스에서 한 개의 접시를 선택한다. 선택한 접시의 숫자를 노트에 기록한 후, 해당 접시의 숫자를 1로 바꾼다.&lt;/p&gt;
&lt;p data-end=&quot;680&quot; data-start=&quot;650&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2단계&lt;/b&gt;: n-1번째 박스부터 두 번째 박스까지 역순으로 각 박스에서 한 개의 접시를 선택한다.&lt;/p&gt;
&lt;p data-end=&quot;715&quot; data-start=&quot;680&quot; data-ke-size=&quot;size16&quot;&gt;목표는 노트에 기록된 숫자들의 곱이 k로 나누어 떨어지도록 하는 선택 방법의 총 가짓수 T를 구하고, T를 l로 나눈 나머지를 출력하는 것이다.&lt;/p&gt;
&lt;p data-end=&quot;715&quot; data-start=&quot;680&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;730&quot; data-start=&quot;720&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 핵심 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;800&quot; data-start=&quot;731&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;760&quot; data-start=&quot;731&quot;&gt;정수론과 &lt;b&gt;gcd&lt;/b&gt;를 활용해 k의 약수를 처리한다.&lt;/li&gt;
&lt;li data-end=&quot;785&quot; data-start=&quot;761&quot;&gt;각 박스별 접시 선택의 경우의 수를 구분해 계산한다.&lt;/li&gt;
&lt;li data-end=&quot;800&quot; data-start=&quot;786&quot;&gt;동적 계획법(DP)을 통해 상태 전이를 누적한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;807&quot; data-start=&quot;804&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;840&quot; data-start=&quot;810&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ️ 풀이 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;875&quot; data-start=&quot;841&quot; data-ke-size=&quot;size16&quot;&gt;먼저 입력으로 n, m, k, l과 각 박스의 접시에 적힌 수를 받았다. k의 모든 약수를 구해 각 약수를 상태로 사용하고, 약수에 대한 인덱스를 매핑했다.&lt;/p&gt;
&lt;p data-end=&quot;875&quot; data-start=&quot;841&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;915&quot; data-start=&quot;876&quot; data-ke-size=&quot;size16&quot;&gt;각 박스마다 접시 선택의 경우의 수를 계산했다. 첫 번째 박스와 마지막 박스는 접시를 한 번만 선택하므로 각 접시가 기여하는 값이 그대로 반영되었다.&lt;/p&gt;
&lt;p data-end=&quot;915&quot; data-start=&quot;876&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;960&quot; data-start=&quot;916&quot; data-ke-size=&quot;size16&quot;&gt;중간 박스는 두 번 선택하는 경우가 있어, 같은 접시를 두 번 선택하는 경우(두 번째 선택 시 1이 기록됨)와 서로 다른 접시를 선택하는 경우를 모두 고려했다.&lt;/p&gt;
&lt;p data-end=&quot;960&quot; data-start=&quot;916&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1000&quot; data-start=&quot;961&quot; data-ke-size=&quot;size16&quot;&gt;DP 배열을 이용해 각 박스에서 계산된 경우의 수를 누적하며 상태 전이를 진행했다. 현재 상태는 지금까지 선택한 접시들의 숫자 곱의 gcd(k, &amp;middot;) 값으로 표현된다.&lt;/p&gt;
&lt;p data-end=&quot;1000&quot; data-start=&quot;961&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1040&quot; data-start=&quot;1001&quot; data-ke-size=&quot;size16&quot;&gt;모든 박스를 처리한 후, 최종 상태가 k로 나누어 떨어지는 경우의 수를 l로 나눈 나머지를 출력했다.&lt;/p&gt;
&lt;hr data-end=&quot;1045&quot; data-start=&quot;1042&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1080&quot; data-start=&quot;1046&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  코드 구현 (Java)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1741567097689&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // 입력을 위한 BufferedReader 초기화
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        
        // 첫 번째 줄: n (박스 개수), m (각 박스 당 접시 개수) 입력
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());
        
        // 두 번째 줄: k (목표 배수), mod (나머지 연산 모듈) 입력
        st = new StringTokenizer(br.readLine());
        int k = Integer.parseInt(st.nextToken());
        int mod = Integer.parseInt(st.nextToken());
        
        // k의 모든 약수를 구함 (각 상태를 약수로 사용)
        ArrayList&amp;lt;Integer&amp;gt; dlist = new ArrayList&amp;lt;&amp;gt;();
        for (int i = 1; i * i &amp;lt;= k; i++) {
            if (k % i == 0) {
                dlist.add(i);
                if (i * i != k)
                    dlist.add(k / i);
            }
        }
        // 약수를 오름차순으로 정렬
        Collections.sort(dlist);
        int dCount = dlist.size();
        int[] dArr = new int[dCount];
        for (int i = 0; i &amp;lt; dCount; i++) {
            dArr[i] = dlist.get(i);
        }
        
        // 각 약수에 인덱스를 매핑 (상태 전이 테이블 구성에 사용)
        HashMap&amp;lt;Integer, Integer&amp;gt; dIndex = new HashMap&amp;lt;&amp;gt;();
        for (int i = 0; i &amp;lt; dCount; i++) {
            dIndex.put(dArr[i], i);
        }
        
        // 두 상태의 곱셈 결과의 gcd(k, 곱)으로 전이되는 상태를 미리 계산 (상태 전이 테이블)
        int[][] mult = new int[dCount][dCount];
        for (int i = 0; i &amp;lt; dCount; i++) {
            for (int j = 0; j &amp;lt; dCount; j++) {
                long prod = (long) dArr[i] * dArr[j];
                int g = (int) gcd(prod, k);
                mult[i][j] = dIndex.get(g);
            }
        }
        
        // dp 배열 초기화 (현재 상태별 경우의 수 저장)
        int[] dp = new int[dCount];
        // 초기 상태는 1 (곱의 결과가 1)인 상태에서 시작
        dp[dIndex.get(1)] = 1;
        
        // 각 박스별로 처리 (총 n개의 박스)
        for (int box = 1; box &amp;lt;= n; box++) {
            // 각 박스 내에서 약수 상태에 해당하는 접시 개수 카운트
            int[] freq = new int[dCount];
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j &amp;lt; m; j++) {
                int val = Integer.parseInt(st.nextToken());
                int g = (int) gcd(val, k);
                int idx = dIndex.get(g);
                freq[idx]++; // 해당 약수 상태에 해당하는 접시 개수 증가
            }
            
            // 현재 박스에서 선택할 경우의 수를 계산할 배열
            int[] boxDist = new int[dCount];
            if (box == 1 || box == n) {
                // 첫 번째 박스와 마지막 박스는 한 번만 선택하므로 그대로 사용
                for (int i = 0; i &amp;lt; dCount; i++) {
                    boxDist[i] = freq[i] % mod;
                }
            } else {
                // 중간 박스는 두 번 선택함 (한 번은 기록 후 1로 변경, 두 번 선택 시 곱에 영향 없음)
                int[] doubleDist = new int[dCount];
                // 먼저 한 번 선택하는 경우 (기본적으로 freq 값을 더함)
                for (int i = 0; i &amp;lt; dCount; i++) {
                    doubleDist[i] = (doubleDist[i] + freq[i]) % mod;
                }
                // 두 번째 선택의 경우: 같은 접시 또는 다른 접시 선택 경우 고려
                for (int i = 0; i &amp;lt; dCount; i++) {
                    for (int j = 0; j &amp;lt; dCount; j++) {
                        // i와 j가 같은 경우: 같은 접시를 두 번 선택하는 경우
                        // i와 j가 다른 경우: 서로 다른 접시를 선택하는 경우
                        long ways = (i == j) ? (long) freq[i] * (freq[i] - 1) : (long) freq[i] * freq[j];
                        if (ways == 0)
                            continue;
                        int idx = mult[i][j];
                        doubleDist[idx] = (int) ((doubleDist[idx] + ways) % mod);
                    }
                }
                boxDist = doubleDist;
            }
            
            // 현재까지의 dp와 이번 박스의 boxDist를 이용해 다음 상태(ndp)를 계산
            int[] ndp = new int[dCount];
            for (int i = 0; i &amp;lt; dCount; i++) {
                if (dp[i] == 0)
                    continue;
                for (int j = 0; j &amp;lt; dCount; j++) {
                    if (boxDist[j] == 0)
                        continue;
                    int ns = mult[i][j]; // 현재 상태 i에서 박스의 선택 j를 적용한 새로운 상태
                    ndp[ns] = (int) ((ndp[ns] + (long) dp[i] * boxDist[j]) % mod);
                }
            }
            // dp 배열 갱신
            dp = ndp;
        }
        // 최종적으로 dp 배열에서 목표 상태 k (즉, 약수 상태가 k가 되는 경우)의 값을 출력
        System.out.println(dp[dIndex.get(k)] % mod);
    }
    
    // 유클리드 호제법을 이용한 최대공약수(GCD) 계산 메소드
    static long gcd(long a, long b) {
        while (b != 0) {
            long t = a % b;
            a = b;
            b = t;
        }
        return a;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;1085&quot; data-start=&quot;1082&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1120&quot; data-start=&quot;1086&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⏱️ 시간 복잡도 분석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1160&quot; data-start=&quot;1121&quot; data-ke-size=&quot;size16&quot;&gt;문제는 각 박스마다 m개의 접시를 처리하고, k의 약수 개수를 d라 할 때 O(n*m + n*d&amp;sup2;)의 시간 복잡도를 가진다. &lt;br /&gt;n은 최대 200, m은 최대 10,000이며, d는 k의 약수 개수로 보통 &amp;radic;k에 가까워 제한 내에서 충분히 계산된다.&lt;/p&gt;
&lt;hr data-end=&quot;1165&quot; data-start=&quot;1162&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1200&quot; data-start=&quot;1166&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  느낀점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1240&quot; data-start=&quot;1201&quot; data-ke-size=&quot;size16&quot;&gt;문제 접근 방식은 각 박스에서의 접시 선택에 따른 상태 전이를 명확하게 처리하는 데 초점을 두었다. &lt;br /&gt;정수론과 gcd를 활용해 k의 약수를 처리하며 상태 전이 테이블을 구성한 점이 인상적이다. &lt;br /&gt;동일한 박스에서 접시를 두 번 선택하는 경우를 세밀하게 고려한 점이 문제 해결의 핵심 포인트였다.&lt;/p&gt;</description>
      <category>알고리즘/BOJ - Java</category>
      <category>5266</category>
      <category>5266 boj</category>
      <category>5266 java</category>
      <category>5266 백준</category>
      <category>5266 자바</category>
      <category>boj 5266</category>
      <category>java 5266</category>
      <category>[boj/java] 5266. strange dream</category>
      <category>백준 5266</category>
      <category>자바 5266</category>
      <author>ImJay</author>
      <guid isPermaLink="true">https://develop247.tistory.com/619</guid>
      <comments>https://develop247.tistory.com/619#entry619comment</comments>
      <pubDate>Thu, 13 Mar 2025 17:12:40 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ/Java] 11404. 플로이드</title>
      <link>https://develop247.tistory.com/618</link>
      <description>&lt;h3 data-end=&quot;35&quot; data-start=&quot;0&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  [BOJ/Java] 11404. 플로이드&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;87&quot; data-start=&quot;37&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;87&quot; data-start=&quot;37&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11404&quot; data-end=&quot;85&quot; data-start=&quot;39&quot;&gt;문제 링크&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;92&quot; data-start=&quot;89&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;111&quot; data-start=&quot;94&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  문제 해석&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;190&quot; data-start=&quot;113&quot; data-ke-size=&quot;size16&quot;&gt;N개의 도시가 있고, M개의 버스 노선이 존재한다.&lt;br /&gt;각 버스는 특정한 비용이 있으며, 도시 간 최단 비용을 구하는 문제이다.&lt;/p&gt;
&lt;p data-end=&quot;207&quot; data-start=&quot;192&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;주어진 정보&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;329&quot; data-start=&quot;208&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;239&quot; data-start=&quot;208&quot;&gt;N: 도시의 개수 (2 &amp;le; N &amp;le; 100)&lt;/li&gt;
&lt;li data-end=&quot;278&quot; data-start=&quot;240&quot;&gt;M: 버스 노선의 개수 (1 &amp;le; M &amp;le; 100,000)&lt;/li&gt;
&lt;li data-end=&quot;329&quot; data-start=&quot;279&quot;&gt;A B C: A번 도시에서 B번 도시로 가는 비용이 C (중복 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;411&quot; data-start=&quot;331&quot; data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;목표:&lt;/b&gt;&lt;br /&gt;1️⃣ 모든 도시 쌍(i &amp;rarr; j)에 대한 &lt;b&gt;최소 비용 경로&lt;/b&gt;를 구하기&lt;br /&gt;2️⃣ 경로가 없는 경우 0을 출력&lt;/p&gt;
&lt;hr data-end=&quot;416&quot; data-start=&quot;413&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;435&quot; data-start=&quot;418&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  풀이 과정&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-end=&quot;466&quot; data-start=&quot;437&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1️⃣ 플로이드-워셜 알고리즘 사용&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;570&quot; data-start=&quot;467&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;527&quot; data-start=&quot;467&quot;&gt;&lt;b&gt;모든 노드에서 모든 노드까지의 최단 거리&lt;/b&gt;를 구하는 문제이므로 플로이드-워셜 알고리즘을 사용&lt;/li&gt;
&lt;li data-end=&quot;570&quot; data-start=&quot;528&quot;&gt;&lt;b&gt;시간 복잡도&lt;/b&gt;: O(N&amp;sup3;), N=100이므로 충분히 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;589&quot; data-start=&quot;572&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2️⃣ 초기화&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;705&quot; data-start=&quot;590&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;632&quot; data-start=&quot;590&quot;&gt;&lt;b&gt;무한대(INF) 값 설정&lt;/b&gt; (dist[i][j] = INF)&lt;/li&gt;
&lt;li data-end=&quot;671&quot; data-start=&quot;633&quot;&gt;&lt;b&gt;자기 자신으로 가는 경로 dist[i][i] = 0&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;705&quot; data-start=&quot;672&quot;&gt;&lt;b&gt;입력값을 바탕으로 직접 이동 가능한 거리 설정&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;731&quot; data-start=&quot;707&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3️⃣ 플로이드-워셜 수행&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;792&quot; data-start=&quot;732&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;792&quot; data-start=&quot;732&quot;&gt;k번 노드를 거쳐가는 경로(i &amp;rarr; k &amp;rarr; j)를 고려하여 i &amp;rarr; j로 가는 최소 비용 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;797&quot; data-start=&quot;794&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;820&quot; data-start=&quot;799&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  코드 (Java)&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1741570370459&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

public class Main {
    static final int INF = 1000000000;
    static int N, M;
    static int[][] dist;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        M = sc.nextInt();
        dist = new int[N + 1][N + 1];

        // 거리 배열 초기화
        for (int i = 1; i &amp;lt;= N; i++) {
            Arrays.fill(dist[i], INF);
            dist[i][i] = 0; // 자기 자신은 0
        }

        // 입력 받기
        for (int i = 0; i &amp;lt; M; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            int c = sc.nextInt();
            dist[a][b] = Math.min(dist[a][b], c); // 같은 노선이 여러 개일 수 있음
        }

        // 플로이드-워셜 알고리즘 수행
        for (int k = 1; k &amp;lt;= N; k++) {
            for (int i = 1; i &amp;lt;= N; i++) {
                for (int j = 1; j &amp;lt;= N; j++) {
                    if (dist[i][k] != INF &amp;amp;&amp;amp; dist[k][j] != INF) {
                        dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
                    }
                }
            }
        }

        // 결과 출력
        for (int i = 1; i &amp;lt;= N; i++) {
            for (int j = 1; j &amp;lt;= N; j++) {
                System.out.print((dist[i][j] == INF ? &quot;0&quot; : dist[i][j]) + &quot; &quot;);
            }
            System.out.println();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-4o&quot; data-message-id=&quot;ecb2d452-a65a-4cc8-8b54-4b61d2eeb0c4&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;hr data-end=&quot;2151&quot; data-start=&quot;2148&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2173&quot; data-start=&quot;2153&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;⏱ 시간 복잡도 분석&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2248&quot; data-start=&quot;2175&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2248&quot; data-start=&quot;2175&quot;&gt;&lt;b&gt;플로이드-워셜 알고리즘&lt;/b&gt;: O(N&amp;sup3;), N &amp;le; 100이므로 100&amp;sup3; = 1,000,000 연산은 충분히 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2253&quot; data-start=&quot;2250&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2271&quot; data-start=&quot;2255&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  느낀 점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;2410&quot; data-start=&quot;2273&quot; data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;플로이드-워셜 알고리즘의 기본적인 형태&lt;/b&gt;를 익힐 수 있었다.&lt;br /&gt;✅ &lt;b&gt;중복 간선이 존재할 수 있으므로 Math.min()으로 최소 비용 갱신&lt;/b&gt;해야 한다.&lt;br /&gt;✅ &lt;b&gt;경로가 없을 경우 0을 출력하는 처리&lt;/b&gt;를 조심해야 한다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;2460&quot; data-start=&quot;2412&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;그래프 최단 경로 문제의 기본 중 기본! 확실하게 익혀야 할 문제다.&lt;/b&gt;  &lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>알고리즘/BOJ - Java</category>
      <category>11404</category>
      <category>11404 boj</category>
      <category>11404 java</category>
      <category>11404 백준</category>
      <category>11404 자바</category>
      <category>boj 11404</category>
      <category>java 11404</category>
      <category>[boj/java] 11404. 플로이드</category>
      <category>백준 11404</category>
      <category>자바 11404</category>
      <author>ImJay</author>
      <guid isPermaLink="true">https://develop247.tistory.com/618</guid>
      <comments>https://develop247.tistory.com/618#entry618comment</comments>
      <pubDate>Mon, 10 Mar 2025 10:33:18 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ/Java] 2931. 가스관</title>
      <link>https://develop247.tistory.com/617</link>
      <description>&lt;h3 data-end=&quot;33&quot; data-start=&quot;0&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  [BOJ/Java] 2931. 가스관&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;84&quot; data-start=&quot;35&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;84&quot; data-start=&quot;35&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2931&quot; data-end=&quot;82&quot; data-start=&quot;37&quot;&gt;문제 링크&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;89&quot; data-start=&quot;86&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;108&quot; data-start=&quot;91&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  문제 해석&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;233&quot; data-start=&quot;110&quot; data-ke-size=&quot;size16&quot;&gt;도시에는 여러 개의 &lt;b&gt;가스관&lt;/b&gt;이 설치되어 있으며, 가스관은 &lt;b&gt;일정한 패턴의 블록&lt;/b&gt;으로 이루어져 있다.&lt;br /&gt;어느 날 &lt;b&gt;하나의 블록이 사라지는 사고&lt;/b&gt;가 발생하여, 이를 찾아 &lt;b&gt;원래의 블록을 복구&lt;/b&gt;해야 한다.&lt;/p&gt;
&lt;p data-end=&quot;250&quot; data-start=&quot;235&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;주어진 정보&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;350&quot; data-start=&quot;251&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;308&quot; data-start=&quot;251&quot;&gt;N &amp;times; M 크기의 도시 지도 (.: 빈칸, + - | 1 2 3 4 M Z: 가스관)&lt;/li&gt;
&lt;li data-end=&quot;329&quot; data-start=&quot;309&quot;&gt;M: 가스가 시작되는 위치&lt;/li&gt;
&lt;li data-end=&quot;350&quot; data-start=&quot;330&quot;&gt;Z: 가스가 도착하는 위치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;426&quot; data-start=&quot;352&quot; data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;목표:&lt;/b&gt;&lt;br /&gt;1️⃣ &lt;b&gt;사라진 블록의 위치&lt;/b&gt;를 찾아 출력&lt;br /&gt;2️⃣ 해당 위치에 들어갈 &lt;b&gt;올바른 블록 종류&lt;/b&gt;를 출력&lt;/p&gt;
&lt;hr data-end=&quot;431&quot; data-start=&quot;428&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;450&quot; data-start=&quot;433&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  풀이 과정&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-end=&quot;480&quot; data-start=&quot;452&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1️⃣ 사라진 블록(.) 찾기&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;511&quot; data-start=&quot;481&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;511&quot; data-start=&quot;481&quot;&gt;가스관을 따라가다가 연결이 끊긴 지점을 찾는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;548&quot; data-start=&quot;513&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2️⃣ 사라진 블록이 가질 수 있는 형태 탐색&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;620&quot; data-start=&quot;549&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;581&quot; data-start=&quot;549&quot;&gt;+ - | 1 2 3 4 중 올바른 블록을 결정&lt;/li&gt;
&lt;li data-end=&quot;620&quot; data-start=&quot;582&quot;&gt;&lt;b&gt;연결된 블록들의 방향을 고려하여 가능한 블록을 찾는다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;651&quot; data-start=&quot;622&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3️⃣ BFS를 활용한 가스관 탐색&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;734&quot; data-start=&quot;652&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;693&quot; data-start=&quot;652&quot;&gt;M에서 시작하여 &lt;b&gt;가스관을 따라 이동하며 연결된 블록 탐색&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;734&quot; data-start=&quot;694&quot;&gt;.을 만나는 순간 끊어졌으므로 &lt;b&gt;사라진 블록의 위치 기록&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;739&quot; data-start=&quot;736&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;762&quot; data-start=&quot;741&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  코드 (Java)&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1741569062652&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
    static int R, C;
    static char[][] map;

    static final int[] dx = {-1, 1, 0, 0};  // 상, 하, 좌, 우
    static final int[] dy = {0, 0, -1, 1};

    static final int TOP = 0, BOTTOM = 1, LEFT = 2, RIGHT = 3;

    public static void main(String[] args) throws Exception {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

        String[] input = in.readLine().split(&quot; &quot;);
        R = Integer.parseInt(input[0]);
        C = Integer.parseInt(input[1]);
        map = new char[R][C];

        for (int i = 0; i &amp;lt; R; i++) {
            map[i] = in.readLine().toCharArray();
        }

        for (int i = 0; i &amp;lt; R; i++) {
            for (int j = 0; j &amp;lt; C; j++) {
                if (map[i][j] == '.' &amp;amp;&amp;amp; check(i, j)) {
                    return;
                }
            }
        }
    }

    private static boolean check(int x, int y) {
        boolean[] connected = new boolean[4];

        if (isValid(x + dx[TOP], y + dy[TOP]) &amp;amp;&amp;amp; canComeBottom(map[x + dx[TOP]][y + dy[TOP]])) {
            connected[TOP] = true;
        }
        if (isValid(x + dx[BOTTOM], y + dy[BOTTOM]) &amp;amp;&amp;amp; canComeTop(map[x + dx[BOTTOM]][y + dy[BOTTOM]])) {
            connected[BOTTOM] = true;
        }
        if (isValid(x + dx[LEFT], y + dy[LEFT]) &amp;amp;&amp;amp; canComeRight(map[x + dx[LEFT]][y + dy[LEFT]])) {
            connected[LEFT] = true;
        }
        if (isValid(x + dx[RIGHT], y + dy[RIGHT]) &amp;amp;&amp;amp; canComeLeft(map[x + dx[RIGHT]][y + dy[RIGHT]])) {
            connected[RIGHT] = true;
        }

        char correctPipe = findCorrectPipe(connected);
        if (correctPipe != '?') {
            System.out.println((x + 1) + &quot; &quot; + (y + 1) + &quot; &quot; + correctPipe);
            return true;
        }

        return false;
    }

    private static char findCorrectPipe(boolean[] connected) {
        if (connected[TOP] &amp;amp;&amp;amp; connected[BOTTOM] &amp;amp;&amp;amp; connected[LEFT] &amp;amp;&amp;amp; connected[RIGHT]) return '+';
        if (connected[TOP] &amp;amp;&amp;amp; connected[BOTTOM]) return '|';
        if (connected[LEFT] &amp;amp;&amp;amp; connected[RIGHT]) return '-';
        if (connected[BOTTOM] &amp;amp;&amp;amp; connected[RIGHT]) return '1';
        if (connected[TOP] &amp;amp;&amp;amp; connected[RIGHT]) return '2';
        if (connected[TOP] &amp;amp;&amp;amp; connected[LEFT]) return '3';
        if (connected[BOTTOM] &amp;amp;&amp;amp; connected[LEFT]) return '4';
        return '?';
    }

    private static boolean canComeTop(char c) {
        return c == '|' || c == '+' || c == '2' || c == '3';
    }

    private static boolean canComeBottom(char c) {
        return c == '|' || c == '+' || c == '1' || c == '4';
    }

    private static boolean canComeLeft(char c) {
        return c == '-' || c == '+' || c == '3' || c == '4';
    }

    private static boolean canComeRight(char c) {
        return c == '-' || c == '+' || c == '1' || c == '2';
    }

    private static boolean isValid(int x, int y) {
        return x &amp;gt;= 0 &amp;amp;&amp;amp; x &amp;lt; R &amp;amp;&amp;amp; y &amp;gt;= 0 &amp;amp;&amp;amp; y &amp;lt; C;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;4094&quot; data-start=&quot;4091&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4116&quot; data-start=&quot;4096&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;⏱ 시간 복잡도 분석&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4232&quot; data-start=&quot;4117&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4144&quot; data-start=&quot;4117&quot;&gt;&lt;b&gt;BFS 탐색&lt;/b&gt; &amp;rarr; O(N &amp;times; M)&lt;/li&gt;
&lt;li data-end=&quot;4191&quot; data-start=&quot;4145&quot;&gt;&lt;b&gt;가능한 블록 탐색&lt;/b&gt; &amp;rarr; O(7 &amp;times; 4) = O(28) (상수 시간)&lt;/li&gt;
&lt;li data-end=&quot;4232&quot; data-start=&quot;4192&quot;&gt;&lt;b&gt;총 시간 복잡도&lt;/b&gt; &amp;rarr; O(N &amp;times; M), 충분히 해결 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;4237&quot; data-start=&quot;4234&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4255&quot; data-start=&quot;4239&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  느낀 점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;4370&quot; data-start=&quot;4257&quot; data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;양방향 연결을 고려하는 것이 중요했다.&lt;/b&gt;&lt;br /&gt;✅ 올바른 블록을 찾을 때 &lt;b&gt;모든 방향에서 연결 가능 여부를 체크해야 했다.&lt;/b&gt;&lt;br /&gt;✅ &lt;b&gt;출력 좌표는 1부터 시작&lt;/b&gt;하므로 주의해야 했다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;4412&quot; data-start=&quot;4372&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;디테일한 조건을 정확히 처리하는 것이 핵심인 문제였다!&lt;/b&gt;  &lt;/p&gt;</description>
      <category>알고리즘/BOJ - Java</category>
      <category>2931</category>
      <category>2931 boj</category>
      <category>2931 java</category>
      <category>2931 백준</category>
      <category>2931 자바</category>
      <category>boj 2931</category>
      <category>java 2931</category>
      <category>[boj/java] 2931. 가스관</category>
      <category>백준 2931</category>
      <category>자바 2931</category>
      <author>ImJay</author>
      <guid isPermaLink="true">https://develop247.tistory.com/617</guid>
      <comments>https://develop247.tistory.com/617#entry617comment</comments>
      <pubDate>Mon, 10 Mar 2025 10:31:04 +0900</pubDate>
    </item>
    <item>
      <title>[BOJ/Java] 11657. 타임머신</title>
      <link>https://develop247.tistory.com/616</link>
      <description>&lt;h3 data-end=&quot;35&quot; data-start=&quot;0&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  [BOJ/Java] 11657. 타임머신&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;87&quot; data-start=&quot;37&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;87&quot; data-start=&quot;37&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11657&quot; data-end=&quot;85&quot; data-start=&quot;39&quot;&gt;문제 링크&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;92&quot; data-start=&quot;89&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;111&quot; data-start=&quot;94&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  문제 해석&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;203&quot; data-start=&quot;113&quot; data-ke-size=&quot;size16&quot;&gt;한 도시에서 다른 도시로 이동하는 버스 노선이 주어진다.&lt;br /&gt;일부 노선에는 &lt;b&gt;음의 가중치&lt;/b&gt;(시간)가 존재하며, &lt;b&gt;음수 사이클이 발생할 수도 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;220&quot; data-start=&quot;205&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;주어진 정보&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;322&quot; data-start=&quot;221&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;241&quot; data-start=&quot;221&quot;&gt;N: 도시의 개수 (노드)&lt;/li&gt;
&lt;li data-end=&quot;265&quot; data-start=&quot;242&quot;&gt;M: 버스 노선의 개수 (간선)&lt;/li&gt;
&lt;li data-end=&quot;322&quot; data-start=&quot;266&quot;&gt;A B C: A번 도시에서 B번 도시로 가는 &lt;b&gt;시간이 C(양수 또는 음수)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;407&quot; data-start=&quot;324&quot; data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;목표:&lt;/b&gt;&lt;br /&gt;1️⃣ 1번 도시에서 &lt;b&gt;모든 도시로 가는 최단 시간&lt;/b&gt;을 구한다.&lt;br /&gt;2️⃣ &lt;b&gt;음수 사이클이 존재하는지&lt;/b&gt; 판별한다.&lt;/p&gt;
&lt;pre id=&quot;code_1741568940242&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;3 4
1 2 4
1 3 3
2 3 -1
3 1 -2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;497&quot; data-start=&quot;456&quot; data-ke-size=&quot;size16&quot;&gt;  1 &amp;rarr; 2 &amp;rarr; 3 &amp;rarr; 1로 돌아오면 &lt;b&gt;음수 사이클 발생!&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;502&quot; data-start=&quot;499&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;521&quot; data-start=&quot;504&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  풀이 과정&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-end=&quot;565&quot; data-start=&quot;523&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1️⃣ 벨만-포드 (Bellman-Ford) 알고리즘 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;693&quot; data-start=&quot;566&quot; data-ke-size=&quot;size16&quot;&gt;✔ &lt;b&gt;음수 가중치가 존재할 수 있기 때문에&lt;/b&gt; 다익스트라가 아닌 &lt;b&gt;벨만-포드 알고리즘&lt;/b&gt;을 사용&lt;br /&gt;✔ N-1번 모든 간선을 확인하며 최단 경로 갱신&lt;br /&gt;✔ N번째 갱신에서 값이 줄어들면 &lt;b&gt;음수 사이클 존재!&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-end=&quot;719&quot; data-start=&quot;695&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2️⃣ 알고리즘 진행 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;825&quot; data-start=&quot;720&quot; data-ke-size=&quot;size16&quot;&gt;✅ 모든 최단 경로를 **무한대(INF)**로 초기화&lt;br /&gt;✅ N-1번 동안 모든 간선을 순회하면서 거리 갱신&lt;br /&gt;✅ N번째 반복에서 갱신이 발생하면 &lt;b&gt;음수 사이클 존재!&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;830&quot; data-start=&quot;827&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;853&quot; data-start=&quot;832&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  코드 (Java)&lt;/b&gt;&lt;/h2&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1741568954055&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

public class Main {
    static final long INF = Long.MAX_VALUE;
    static int N, M;
    static List&amp;lt;Edge&amp;gt; edges;
    static long[] dist;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        M = sc.nextInt();

        edges = new ArrayList&amp;lt;&amp;gt;();
        dist = new long[N + 1];
        Arrays.fill(dist, INF);
        dist[1] = 0; // 시작점 설정

        for (int i = 0; i &amp;lt; M; i++) {
            int u = sc.nextInt();
            int v = sc.nextInt();
            int w = sc.nextInt();
            edges.add(new Edge(u, v, w));
        }

        if (bellmanFord()) {
            System.out.println(&quot;-1&quot;); // 음수 사이클 존재
        } else {
            for (int i = 2; i &amp;lt;= N; i++) {
                System.out.println(dist[i] == INF ? &quot;-1&quot; : dist[i]);
            }
        }
    }

    static boolean bellmanFord() {
        boolean updated = false;
        
        for (int i = 1; i &amp;lt; N; i++) { // N-1번 반복
            updated = false;
            for (Edge edge : edges) {
                if (dist[edge.u] != INF &amp;amp;&amp;amp; dist[edge.v] &amp;gt; dist[edge.u] + edge.w) {
                    dist[edge.v] = dist[edge.u] + edge.w;
                    updated = true;
                }
            }
            if (!updated) break;
        }

        // N번째 반복에서 갱신 발생하면 음수 사이클 존재
        for (Edge edge : edges) {
            if (dist[edge.u] != INF &amp;amp;&amp;amp; dist[edge.v] &amp;gt; dist[edge.u] + edge.w) {
                return true;
            }
        }
        return false;
    }

    static class Edge {
        int u, v, w;
        public Edge(int u, int v, int w) {
            this.u = u;
            this.v = v;
            this.w = w;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;2595&quot; data-start=&quot;2592&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2617&quot; data-start=&quot;2597&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;⏱ 시간 복잡도 분석&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2718&quot; data-start=&quot;2619&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2649&quot; data-start=&quot;2619&quot;&gt;&lt;b&gt;벨만-포드 알고리즘&lt;/b&gt;: O(N &amp;times; M)&lt;/li&gt;
&lt;li data-end=&quot;2718&quot; data-start=&quot;2650&quot;&gt;N &amp;le; 500, M &amp;le; 6000이므로 최악의 경우 500 &amp;times; 6000 = 3,000,000 연산 (가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2723&quot; data-start=&quot;2720&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2741&quot; data-start=&quot;2725&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  느낀 점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;2859&quot; data-start=&quot;2743&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;벨만-포드를 활용한 대표적인 문제였다.&lt;/b&gt;&lt;br /&gt;➡️ 음수 가중치가 포함된 경우는 &lt;b&gt;다익스트라 대신 벨만-포드 사용!&lt;/b&gt;&lt;br /&gt;➡️ &lt;b&gt;N번째 갱신 여부를 체크하면 음수 사이클 판별이 가능하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;3020&quot; data-start=&quot;2861&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;디테일한 부분에서 실수하기 쉬웠다.&lt;/b&gt;&lt;br /&gt;✅ dist 배열을 &lt;b&gt;Long 타입으로 선언&lt;/b&gt;해야 오버플로우 방지 가능!&lt;br /&gt;✅ &lt;b&gt;시작점(1번 도시)을 0으로 초기화&lt;/b&gt;하는 것 잊지 않기&lt;br /&gt;✅ 음수 사이클이 존재하면 &lt;b&gt;-1을 바로 출력&lt;/b&gt;하고 종료해야 한다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;3067&quot; data-start=&quot;3022&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;음수 가중치 그래프 문제를 연습하는 데 아주 좋은 문제였다!&lt;/b&gt;  &lt;/p&gt;</description>
      <category>알고리즘/BOJ - Java</category>
      <category>11657</category>
      <category>11657 boj</category>
      <category>11657 java</category>
      <category>11657 백준</category>
      <category>11657 자바</category>
      <category>boj 11657</category>
      <category>java 11657</category>
      <category>[boj/java] 11657. 타임머신</category>
      <category>백준 11657</category>
      <category>자바 11657</category>
      <author>ImJay</author>
      <guid isPermaLink="true">https://develop247.tistory.com/616</guid>
      <comments>https://develop247.tistory.com/616#entry616comment</comments>
      <pubDate>Mon, 10 Mar 2025 10:10:10 +0900</pubDate>
    </item>
  </channel>
</rss>