<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Compy&apos;s Blog</title><description>
AI Dev</description><link>https://compy07.github.io/</link><language>ko</language><item><title>[AI] AI TOP 100 예선 후기</title><link>https://compy07.github.io/Blog/posts/ai/ai_top_100/2026/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/ai_top_100/2026/</guid><description>이게 진짜 되냐?</description><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;작년에 AI TOP 100을 일정 때문에 못 나가게돼서 이번에 꼭 나가야지 하고서 나오게 되었다.&lt;/p&gt;
&lt;p&gt;일단 아무 준비없이 그냥 대회를 치루게 되었다.(본선은 서비스 개발인줄 알았는데 본선도 문제 풀이었다는 것도 나중에 알았다.)
일단 CS문제가 나오겠거니 했는데 들어가자마자 나온게 무슨 특이한 문제들이 나왔다.&lt;/p&gt;
&lt;p&gt;처음에는 당황해서 어떻게 풀지 몰랐고, 대회 시간을 다 쓰지 못하는 상황이었다.(그때 한 2시간 정도 풀 수 있었다. 면접 때문에..)
그래서 최대한 빨리 풀면서 효율적으로 점수를 분배할 수 있도록 전략을 세우고 claude, gemini와 함께 했다.&lt;/p&gt;
&lt;p&gt;첫번째 문제는 claude한테 넘겨주고 다른 애들은 내가 읽어보고 방향잡고 토론하면서 문제를 풀었다.
정확히 문제는 기억나지 않지만, 가능성을 좁혀나가면서 풀었던 것이 기억이 나고 이미지 검색 처리 같은 것은 훈련된 모델로 그냥 긁었더니
눈으로 만든 레이블 데이터와 비교해보니 대충 90% 이상은 나오길래 그것만 돌려서 정답을 제출했다.&lt;/p&gt;
&lt;p&gt;그외로 ocr문제도 등장했는데, 이를 hugging face에 올라온 모델(내가 즐겨쓴느 모델 중 하나)랑 gemini랑 같이 돌리면서
의미를 파악하려고 했다. 또한 원하는 정보가 약간 수정된 주변의 박스가 쳐져있어서 그거 detect도 시도해봣는데 그건 제대로 안됐고
이미지 다 잘라서 의미 맥락이 맞는 블럭마다 다 읽어와서 처리해서 내용을 다 찾아서 넣었던 기억이 있다.
총 1시간 30분 정도 풀었고 제출하고 면접을 보러 갔다는... ㅠㅜ&lt;/p&gt;
&lt;p&gt;그래서 붙을 생각 조차 못하고 있었다.&lt;/p&gt;
&lt;p&gt;&amp;lt;h2&amp;gt; 그런데! &amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./mail.png&quot; alt=&quot;본선이다&quot; /&gt;&lt;/p&gt;
&lt;p&gt;음.. 이게 아주 ㄴㅇㅅ한 상화이 됐거든요?
내가 top 100이라니? 내가 카카오 센터도 간다니 신기하네요&lt;/p&gt;
&lt;p&gt;본선에서 네트워킹, 문제풀이 잘하고 오겠습니다.(솔직히 최근 인공지능 너무 잘해져서 어떻게 될지 진짜 잘 모르겠네요)&lt;/p&gt;
</content:encoded></item><item><title>[AI] AI TOP 100 본선 후기</title><link>https://compy07.github.io/Blog/posts/ai/ai_top_100/2026main/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/ai_top_100/2026main/</guid><description>우와ㅏㅏㅏ</description><pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;일단 본선 당일 전날 완전 예전에 잡아놓은 밥약이 있어서 간단히 먹고 집에 와서 자려고 했다가...
어떤 일이 있어서 잠을 자지 못한 상태로 대회장에 가게 되었다. 이때 정신이 하도 없어서 힘들었었는데, 도와줬던 친구한테 너무 고마웠다. (대회장까지 같이 갔었음)
그래서 사실 씻긴 했지만 정신이 없어서 면도나 이런걸 제대로 못했어 가버렸음.. 그래서 마스크들고 가긴 했는데 중간에 잃어버려서... ㅠㅜ&lt;/p&gt;
&lt;p&gt;하튼 대회장에 도착했더니 완전 깔끔한 건물과 멋있는 스탭분들이 타란!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./before.jpeg&quot; alt=&quot;before&quot; /&gt;
이야ㅏㅑㅑㅏ 스크린 완전 멋있었다. 이게 애니메이션도 쫀득하고, 그 대회장에서도 계속 나오던 bgm이 ㄹㅇ 맛도리다.
계속 들뜬 상태에 기대감이 쭉쭉 올라가는 상태를 만드는 느낌...&lt;/p&gt;
&lt;p&gt;&amp;lt;br/&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./me.jpeg&quot; alt=&quot;me&quot; /&gt;
대회장에 들어가면 굿즈도 막 주셔서 기분좋게 받아서 &quot;와이씨 쩐다&quot; 이러면서 신나했었다.&lt;/p&gt;
&lt;p&gt;그리고 대회장 앞 큰 스크린에서는 본선 진출자 대상으로 조사했던 사진과 글들이 나오고 있었다.
&lt;img src=&quot;./screen.png&quot; alt=&quot;screen&quot; /&gt;&lt;/p&gt;
&lt;p&gt;아아 나중에 생각해보니 약간 재밌는 글도 쓰고 사진도 사용할걸 이라는 생각이 많이 들었다.
너무 약간 감다뒤로 나와서 다른분들은 막 재밌는데 혼자 진지하게 적어져있어서 약간 민망(?)했다.&lt;/p&gt;
&lt;p&gt;그 상태로 점심 먹을려구 했는데 시간이 다 돼버려서 못 먹고 제로 콜라 하나 들고 대회 시작했따!&lt;/p&gt;
&lt;p&gt;대회 문제가 아직 공개가 안 돼서 자세히는 못 적지만, 이렇게 문제 푸는 게 재밌었던 대회는 처음이었던 것 같다. AI한테 &quot;그냥 해줘&quot;라고 했을 때 왜 안 되는지를 몸으로 느끼게 만드는 문제들이었달까.
출제자분들이 학생들이 어떤 지점에서 막히는지, 뭘 재밌어하는지를 찾으려고 정말 노력하신 것이 잘 느껴졌다.&lt;/p&gt;
&lt;p&gt;그렇게 대회 문제 풀이 시간이 끝나고 네트워킹 시간이 되어서 돌아다니다가 학교 선배님들을 만났다.
완전 유쾌하시고, 재밌으셨던 분들이어서 너무 친해지고 싶었는데 먼저 연락처를 안 여쭤봐서 교환을 못했다 ㅠㅜㅜ...&lt;/p&gt;
&lt;p&gt;&amp;lt;br/&amp;gt;&lt;/p&gt;
&lt;p&gt;선배님들과 찍은 사진!
&lt;img src=&quot;./our_school.jpeg&quot; alt=&quot;our_school&quot; /&gt;
[얼굴은 다 가리구 올렸습니다]&lt;/p&gt;
&lt;p&gt;밥은 내 취향은 아니었지만 그래도 생긴게 예뻐서 맛있게 먹었던거 같다.
&lt;img src=&quot;./table.jpeg&quot; alt=&quot;table&quot; /&gt;
&lt;img src=&quot;./food.jpeg&quot; alt=&quot;food&quot; /&gt;&lt;/p&gt;
&lt;p&gt;밥을 먹으면서 말씀을 듣는데 진짜 무슨 개쩌는 분들이 막 말씀하시는거 들으며, 여러 인사이트들과
방향들을 얻어갈 수 있었고 동기부여와 더불어 롤모델도 찾을 수 있었다. (이분들이랑 연락처 교환했다!)&lt;/p&gt;
&lt;p&gt;너무 엄청난 분들이 많아서 &quot;흠! 이런 기회만이라도 너무 감사하다&quot;라는 생각을 가지고 시상식으로 갔다.&lt;/p&gt;
&lt;p&gt;그런데!?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./miracle.jpeg&quot; alt=&quot;miracle&quot; /&gt;&lt;/p&gt;
&lt;p&gt;이게 무슨 말도 안되는 행운이 일어나면서 너무 감사한 상을 받게 되었다.
사실 진짜 무슨 생각이 없었어서 진짜 꼴이 말이 아니었는데, 올라가서 옷 정리해주시면서 많이 혼났다..ㅠㅜ&lt;/p&gt;
&lt;p&gt;아무래도 나는 확신의 공학자상인거 같다...&lt;/p&gt;
&lt;p&gt;끝나고 집에 돌아가사면서 정말 감사하다는 생각과 엄청난 행운이 따라줬기에 이렇게 될 수 있었다는 생각이 많이 들었다.
또 상 탔다구 연락주신 분들 너무 감사합니다. 이게 저도 연락드릴려구 했는데 진짜 정신이 없어서 막 있다가 다음날 되니까 연락드리기가 너무 머쓱해서 하핳&lt;/p&gt;
&lt;p&gt;이번에 만나셨던 분들 꼭 나중에 봬요!
수상 소감 때 말하지 못한 말 하겠습니다. &quot;모두 수고하셨습니다. 화이팅입니다.&quot;&lt;/p&gt;
&lt;p&gt;(원래 사진을 많이 안 찍어서 많이 없습니둥..)&lt;/p&gt;
</content:encoded></item><item><title>[AI] Artificial Intelligence(인공지능, AI)</title><link>https://compy07.github.io/Blog/posts/ai/introduce/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/introduce/</guid><description>인공지능에 대해서 알아보고, 공부해볼 알고리즘 등 소개합니다.</description><pubDate>Mon, 24 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://news.skhynix.co.kr/post/all-around-ai-2&quot;&gt;인공지능이란?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;특정 목적을 위해 설계된 컴퓨터 알고리즘으로, 주어진 입력에 기반해 자동으로 과업을 처리한다.
이는 개발자가 직접 컴퓨터에 알고리즘을 입력하는 기존의 방법과 달리,
데이터를 기반으로 컴퓨터가 스스로 알고리즘을 학습한다는 점에서 큰 차이가 존재한다.
그래서 인공지능 알고리즘을 ‘알고리즘을 위한 알고리즘’이라고도 부른다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;알고리즘(Algorithm): 컴퓨터를 활용한 문제 해결 과정에서, 어떤 문제를 해결하기 위해 정의된 절차, 방법, 명령어 등의 집합.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;인공지능에서 학습이란?&lt;/h3&gt;
&lt;p&gt;학습 알고리즘에서 ‘학습(Learning)’은 주어진 데이터를 분석해 특정 작업을 수행할 수 있는 규칙이나 패턴을 발견하는 과정을 뜻한다.&lt;/p&gt;
&lt;p&gt;학습 알고리즘의 역할은 데이터로부터 주어진 과업을 수행할 알고리즘을 찾아내는 것이다.
예측 알고리즘에서 ‘예측(Prediction)’은 학습된 모델*을 사용하여 새로운 데이터에 대한 결과를 추론하는 과정을 말한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, 예측 알고리즘은 학습 알고리즘을 통해 학습되며, 특정 과업을 수행하는 역할을 한다.
이 두 알고리즘의 역할과 작동 원리를 이해하는 것은 인공지능의 기본 원리를 이해하는 데 매우 중요하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;모델(Model): 학습된 규칙이나 패턴을 표현하는 수학적 또는 계산적인 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;인공지능의 작동 과정 이해&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;문제 정의&lt;/li&gt;
&lt;li&gt;데이터 수집&lt;/li&gt;
&lt;li&gt;학습 알고리즘
&lt;ol&gt;
&lt;li&gt;학습 모형의 선택&lt;/li&gt;
&lt;li&gt;파라미터의 학습&lt;/li&gt;
&lt;li&gt;예측성능 측정&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;예측 및 모니터링&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;머신러닝, 딥러닝!&lt;/h2&gt;
&lt;p&gt;머신러닝(Machine Learning)은 인공지능의 한 분야로, 머신러닝은 인공지능의 하위 집합으로, 많은 양의 데이터를 제공하여 명시적으로 프로그래밍하지 않고 시스템이 자율적으로 학습하고 개선할 수 있게하는 방식으로 말할 수 있다.&lt;/p&gt;
&lt;p&gt;딥러닝(Deep Learning)은 인간의 두뇌에서 영감을 얻은 방식으로 데이터를 처리하도록 컴퓨터를 가르치는 인공 지능(AI) 방식, &apos;비선형 변환기법&apos;의 조합을 통해 높은 수준의 추상화를 시도하는 기계 학습 알고리즘의 집합으로 정의된다.
&amp;lt;!--여러 비선형 변환기법의 조합을 통해 높은 수준의 추상화(다량의 복잡한 자료들에서 핵심적인 내용만 추려내는 작업)을 시도하는 기계학습 알고리즘의 집합으로 뜻합니다.--&amp;gt;&lt;/p&gt;
&lt;p&gt;가장 큰 집합으로 인공지능이 있고, 그 인공지능이라는 분야 안에 머신러닝이 속해 있고, 또 머신러닝 안에는 딥러닝이 속해있다고 생각할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;ANN, DNN, RNN, CNN&lt;/h3&gt;
&lt;p&gt;이제부터 위의 뉴럴 네트워크에 대해서 포스팅을 작성할 예정입니다. 블로그를 찾다보면 이에 대한 정보가 올라가 있으니, 확인해주세요!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;이제부터 학습 알고리즘에 대해서 알아보고, 인공지능은 어떤 분야에서 이용되는지 알아보도록 합시다.&lt;/p&gt;
&lt;p&gt;이제부터 인공지능 공부는 꾸준히 해야하는 걸로!&lt;/p&gt;
</content:encoded></item><item><title>[AI] Machine Learning(기계 학습)</title><link>https://compy07.github.io/Blog/posts/ai/machinelearning/introduce/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/machinelearning/introduce/</guid><description>인공지능 머신러닝에 대해서 공부하고, 어떤 방식이 있는지 알아봅니다.</description><pubDate>Mon, 24 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;a href=&quot;https://www.sap.com/korea/products/artificial-intelligence/what-is-machine-learning.html&quot;&gt;Machine Learning이란?&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;정의로는 인공지능의 한 분야로, 머신러닝은 인공지능의 하위 집합으로, 많은 양의 데이터를 제공하여 명시적으로 프로그래밍하지 않고 시스템이 자율적으로 학습하고 개선할 수 있게하는 방식이라고 말할 수 있습니다.&lt;/p&gt;
&lt;p&gt;기본적으로 기계학습은 인공지능이라는 엄청 큰 집합의 하위 집합이라고 생각하시면 됩니다. 기계학습에서 알고리즘은 입력으로 들어온 dataset의 패턴과 상관관계를 찾아내고, 분석을 토대로 의사결정, 예측 등을 수행하도록 훈련됩니다.&lt;/p&gt;
&lt;h1&gt;기계학습의 작동방식&lt;/h1&gt;
&lt;p&gt;먼저 기계학습은 정말 다양한 알고리즘 기법을 적용하는 여러 유형의 모델로 구성됩니다.
&lt;strong&gt;데이터의 특성, 목적에 따라서 지도, 비지도, 준지도, 강화학습 등 대표적 4가지 학습모델&lt;/strong&gt; 중 하나를 적용합니다.&lt;/p&gt;
&lt;p&gt;머신러닝은 다양한 알고리즘 기법을 적용하는 여러 유형의 머신러닝 모델로 구성됩니다.
사용 중인 데이터 세트와 원하는 결과에 따라 각 모델 내에서 하나 이상의 알고리즘 기법을 적용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;머신러닝 알고리즘은 기본적으로 사물 분류, 패턴 발견, 결과 예측, 정보 기반 의사결정 등을 수행하도록 설계됩니다.
알고리즘은 정확도를 극대화하기 위해 여러 알고리즘을 결합할 수도 있으며, 하나만을 사용할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;:::warning[주의]
이 글은 굉장히 간단한 설명만 적혀있습니다.
자세한 학습 방법의 설명을 보고 싶다면 좀 더 세분화된 포스트로 들어가주세요.(이 글에서 예시 X)
:::&lt;/p&gt;
&lt;h2&gt;지도학습&lt;/h2&gt;
&lt;p&gt;지도 학습 알고리즘에서는 예시를 통해 머신을 훈련합니다.
지도 학습 모델은 &apos;입력&apos;, &apos;출력&apos; 데이터 쌍으로 구성되며, 원하는 값으로 출력 레이블을 지정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;시스템은 알고리즘을 통해 시간에 따라 이 훈련 데이터를 모두 컴파일한 다음 상관관계가 있는 유사성, 차이점, 기타 논리 지점을 결정합니다. 이 작업은 인풋에 대한 답을 스스로 예측할 수 있을 때까지 반복합니다.&lt;/p&gt;
&lt;h2&gt;비지도학습&lt;/h2&gt;
&lt;p&gt;비지도 학습 모델에는 정답 키가 없습니다.
머신이 입력 데이터(대부분 레이블이 없는 비정형 데이터)를 학습한 다음 관련성이 있고 액세스 가능한 데이터를 모두 사용해 패턴과 상관관계를 인식합니다.
비지도 학습은 사람이 세상을 관찰하는 방식을 기반으로 다양하게 모델링됩니다.&lt;/p&gt;
&lt;h2&gt;준지도학습&lt;/h2&gt;
&lt;p&gt;준지도학습은 소량의 레이블이 지정된 데이터를 입력해 레이블이 없는 데이터 세트를 증강합니다.
핵심은 레이블이 지정된 데이터를 통해 시스템이 학습 시작하며, 학습 속도와 정확성을 상당한 수준으로 개선하도록 하는 것입니다.
준지도 학습 알고리즘은 레이블이 지정된 데이터를 분석해 레이블이 없는 데이터에 적용 가능한 상관관계가 있는 속성을 찾도록 훈련합니다.&lt;/p&gt;
&lt;p&gt;:::tip[정보]
시스템이 레이블이 지정된 데이터에 포함된 결함까지 학습해 복제할 위험이 있습니다.
준지도 학습을 가장 성공적으로 활용하는 회사에서는 선진사례 프로토콜을 구축하고 있습니다.
:::&lt;/p&gt;
&lt;h2&gt;강화학습&lt;/h2&gt;
&lt;p&gt;지도형 학습에서는 머신에 정답 키를 제공해 모든 올바른 결과 중에서 상관관계를 찾아 학습하도록 합니다.
강화학습에서는 정답 키는 제공되지 않지만 일련의 허용 가능한 행동, 규칙, 잠재적 최종 상태가 입력됩니다.
강화학습에서 &apos;보상&apos;은 숫자이며, 시스템에서 수집하려는 항목으로 알고리즘에 프로그래밍됩니다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;이제부터 각 학습에 대한 구현과 자세한 설명을 넣은 포스트를 하도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;!--지도 학습이란?&lt;/p&gt;
&lt;p&gt;네 가지 머신러닝 모델 중 첫 번째는 지도 학습입니다. 지도 학습 알고리즘에서는 예시를 통해 머신을 훈련합니다. 지도 학습 모델은 &apos;입력&apos;, &apos;출력&apos; 데이터 쌍으로 구성되며, 원하는 값으로 출력 레이블을 지정할 수 있습니다. 예를 들어 머신을 데이지꽃과 팬지꽃의 차이를 식별할 수 있도록 훈련하려고 합니다. 하나의 이진 입력 데이터 쌍에는 데이지꽃의 이미지와 팬지꽃의 이미지가 모두 포함됩니다. 해당 특정 쌍에 원하는 결과는 데이지꽃을 선택하는 것이기 때문에, 이것이 올바른 결과로서 사전 식별됩니다.&lt;/p&gt;
&lt;p&gt;시스템은 알고리즘을 통해 시간에 따라 이 훈련 데이터를 모두 컴파일한 다음 상관관계가 있는 유사성, 차이점, 기타 논리 지점을 결정하기 시작하며, 이 작업은 데이지꽃인지 팬지꽃인지 묻는 질문에 대한 답을 스스로 예측할 수 있을 때까지 계속됩니다. 이는 어린 아이에게 일련의 문제를 정답 키와 함께 준 다음, 그들이 한 작업을 보여주고 논리를 설명하도록 하는 것과 같습니다. 지도형 학습 모델은 제품 추천 엔진이나 교통량 분석 앱(예: 하루 중 다른 시간대에 가장 빠른 이동 경로를 예측하는 Waze) 등 일상생활의 다양한 분야에서 사용됩니다.&lt;/p&gt;
&lt;p&gt;비지도 학습이란?&lt;/p&gt;
&lt;p&gt;네 가지 머신러닝 모델 중 두 번째는 비지도 학습입니다. 비지도 학습 모델에는 정답 키가 없습니다. 머신이 입력 데이터(대부분 레이블이 없는 비정형 데이터)를 학습한 다음 관련성이 있고 액세스 가능한 데이터를 모두 사용해 패턴과 상관관계를 인식하기 시작합니다. 비지도 학습은 사람이 세상을 관찰하는 방식을 기반으로 다양하게 모델링됩니다. 사람은 직관과 경험에 의존해 사물을 그룹화합니다. 어떤 사물에 대해 경험하는 예시의 수가 많을수록 그것을 분류하고 인식하는 능력이 더욱더 정확해집니다. 머신에 있어서 &apos;경험&apos;은 &apos;이용 가능한 입력 데이터의 양&apos;입니다. 비지도 학습 모델이 사용되는 대표적인 예는 안면 인식, 유전자 서열 분석, 시장 조사, 사이버 보안 등입니다.&lt;/p&gt;
&lt;p&gt;준지도 학습이란?&lt;/p&gt;
&lt;p&gt;네 가지 머신러닝 모델 중 세 번째는 준지도 학습입니다. 모든 데이터가 시스템에 입력되기 전에 정형화되고 레이블이 지정되어 있다면 더할 나위 없이 완벽할 것입니다. 그러나 실제에서는 이러한 일이 불가능하기 때문에, 대량의 원시 비정형 데이터를 처리해야 하는 경우 준지도 학습은 유효한 해결책이 될 수 있습니다. 이 모델은 소량의 레이블이 지정된 데이터를 입력해 레이블이 없는 데이터 세트를 증강합니다. 이 모델의 핵심은 레이블이 지정된 데이터를 통해 시스템이 학습을 시작하게 하며, 학습 속도와 정확성을 상당한 수준으로 개선하도록 하는 것입니다. 준지도 학습 알고리즘은 레이블이 지정된 데이터를 분석해 레이블이 없는 데이터에 적용 가능한 상관관계가 있는 속성을 찾도록 머신을 훈련합니다.&lt;/p&gt;
&lt;p&gt;그러나 이 모델에는 이 MIT 대학출판사 보고서에서 자세히 다룬 것과 같이 시스템이 레이블이 지정된 데이터에 포함된 결함까지 학습해 복제할 위험이 있습니다. 준지도 학습을 가장 성공적으로 활용하는 회사에서는 선진사례 프로토콜을 구축하고 있습니다. 준지도 학습은 음성 및 언어 분석, 복잡한 의료 연구(예: 단백질 분류), 상위레벨 부정행위 감지에 사용됩니다.&lt;/p&gt;
&lt;p&gt;강화학습이란?&lt;/p&gt;
&lt;p&gt;네 가지 머신러닝 모델 중 마지막 모델은 강화학습입니다. 지도형 학습에서는 머신에 정답 키를 제공해 모든 올바른 결과 중에서 상관관계를 찾아 학습하도록 합니다. 강화학습 모델에서는 정답 키는 제공되지 않지만 일련의 허용 가능한 행동, 규칙, 잠재적 최종 상태가 입력됩니다. 알고리즘의 원하는 목표가 고정되어 있거나 양자택일인 경우 머신은 예시를 통해 학습할 수 있습니다. 그러나 원하는 목표가 변동 가능한 경우에는 경험과 보상을 통해 학습해야 합니다. 강화학습 모델에서 &apos;보상&apos;은 숫자이며, 시스템에서 수집하려는 항목으로 알고리즘에 프로그래밍됩니다.&lt;/p&gt;
&lt;p&gt;이 모델은 여러 면에서 사람에게 체스 게임을 하는 법을 가르치는 것과 유사합니다. 체스 말이 이동할 수 있는 경우를 모두 보여주는 것은 불가능하며, 그 대신 규칙을 설명해주고 연습을 통해 기술을 습득하도록 합니다. 보상은 게임을 이기는 것뿐 아니라 상대방의 말을 획득하는 형태로 이루어집니다. 강화학습 적용 분야에는 온라인 광고 구매자의 자동 가격 입찰, 컴퓨터 게임 개발, 고위험 주식 시장 거래 등이 있습니다.&lt;/p&gt;
&lt;p&gt;이 모델은 여러 면에서 사람에게 체스 게임을 하는 법을 가르치는 것과 유사합니다. 체스 말이 이동할 수 있는 경우를 모두 보여주는 것은 불가능하며, 그 대신 규칙을 설명해주고 연습을 통해 기술을 습득하도록 합니다. 보상은 게임을 이기는 것뿐 아니라 상대방의 말을 획득하는 형태로 이루어집니다. 강화학습 적용 분야에는 온라인 광고 구매자의 자동 가격 입찰, 컴퓨터 게임 개발, 고위험 주식 시장 거래 등이 있습니다.
--&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[AI] 인공신경망 ANN(Artificial Neural Network)</title><link>https://compy07.github.io/Blog/posts/ai/neuralnetwork/ann/introduce/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/neuralnetwork/ann/introduce/</guid><description>인공신경망(Artificial Neural Network, ANN)에 대해서..</description><pubDate>Wed, 26 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;인공신경망이란?&lt;/h2&gt;
&lt;p&gt;인공신경망(Artificial Neural Network, ANN)은 딥러닝에서 굉장히 중요한 역할을하는 &lt;strong&gt;기본적인 알고리즘&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;ANN은 딥러닝의 기초적인 알고리즘으로써, 인간의 뇌의 프로세스를 모방하여 만들어진 학습 알고리즘입니다.
ANN은 주어진 데이터에서 패턴을 학습하고, 어떠한 값에 대해서 학습된 내용을 바탕으로 예측할 수 있습니다.
현재 ANN은 이미지 인식, 자연어 처리, 음성 인식 등 다양한 곳에서 사용되고 있습니다.&lt;/p&gt;
&lt;p&gt;일반적으로 어떠한 형태의 함수(function)이든 근사할 수 있는
ANN은 일반적으로 어떠한 형태의 function이든 근사할 수 있는 &lt;a href=&quot;https://en.wikipedia.org/wiki/Universal_approximation_theorem&quot;&gt;universal function approximator&lt;/a&gt;로도 알려져 있다.&lt;/p&gt;
&lt;h1&gt;구조&lt;/h1&gt;
&lt;h2&gt;인공신경망에서 뉴런?&lt;/h2&gt;
&lt;h3&gt;1. 뉴런&lt;/h3&gt;
&lt;p&gt;뉴런(Neuron)은 신경계와 신경조직을 이루는 기본 단위(in 생명과학)라고 알려진 신경 세포입니다.
신경계의 모든 작용은 &lt;strong&gt;신경 세포와 세포 간의 상호작용&lt;/strong&gt;으로 인해 이루어집니다.&lt;/p&gt;
&lt;p&gt;예를 들어봅시둥, 우리 몸의 내부와 외부에 자극을 가하게 되면 일련의 과정을 통해서 뉴런은 자극을 전달하게 됩니다.
최종적으로는 척수와 뇌 등의 중추신경계로 도달하게 되면서 중추신경계에서 처리한 정보를 다시 우리 몸으로 전달해 명령을 수행합니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/neuron.png&quot; alt=&quot;neuron&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;뉴런은 수백 개가 연결되어 있고, 신호를 전달하는 과정을 수업이 거치게 됩니다.
이 &lt;strong&gt;뉴런들의 입력, 출력의 신호는 다른 신호입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lh6.googleusercontent.com/proxy/xvHbW0YBhHjyCwD6QakZvFvidDQ9Ft_ALxwW5F3_isEqQeeqUBSSU8f4IDdtGV3BcLrPZiDr3JNYBhKGCSfD7UzRxO5ZieKBJw&quot;&gt;&lt;img src=&quot;./img/neuron_connect.png&quot; alt=&quot;neuron_connect&quot; /&gt;&lt;/a&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;수상돌기를 통해서 입력된 자극은 축삭돌기를 통해 다음 뉴런으로 전달됩니다.
이때 시냅스에서 신경전달물질이 배출되어 다음 뉴런의 수상돌기를 통해 입력됩니다.
이렇게 전달된 자극은 자극의 일정 정도 이상ㅇ이 되었을 경우 다음 뉴런으로 전달됩니다.&lt;/p&gt;
&lt;p&gt;보통 학습이라고 하는 것은 &lt;strong&gt;뉴런과 뉴런 사이의 연결 강도가 바뀌는 것&lt;/strong&gt;입니다.
다르게 말하면, 수상돌기에 입력된 자극(신경전달물질)의 세기가 모두 같게 적용되지 않는다는 것!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;신경망의 뉴런과 뉴런 사이의 연결 강도는 학습을 통해서 변화합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;2. 인공 뉴런&lt;/h3&gt;
&lt;p&gt;바로 위에서 설명한 인간의 뉴런 구조에서 영감을 받아 만들어진 놈이 바로 인공 뉴런입니다.
즉 인간의 뉴런 구조에 착안해 만들어진 것이죠. 이러한 &lt;strong&gt;인공 뉴런도 생물학적 뉴런과 비슷한 구조&lt;/strong&gt;로 되어있습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://matrix.skku.ac.kr/math4ai-intro/W13/PICA535.png&quot;&gt;&lt;img src=&quot;./img/neuron_learn.png&quot; alt=&quot;neuron_learn&quot; /&gt;&lt;/a&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;인공 뉴런의 구조를 그림으로 나타내면 위와 같은 그림이 나옵니다!&lt;/p&gt;
&lt;p&gt;인공신경망에서 &lt;strong&gt;학습으로 각 입력에 따른 연결 강도가 정해지는데&lt;/strong&gt; 우리는 그것을 가중치(weight, cost)라고 합니다. 위 그림에서 가중치는 w0, w1, w2,..., wn입니다. x0,...xn은 입력되는 값을 의미하며,
학습을 통해 우리가 구하고자 하는 값이 바로 w0, w1, ...., wn입니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/exp_1.jpeg&quot; alt=&quot;exp_1&quot; /&gt;
(으아... 내 글씨 넘 안 예쁘네..)
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;인공뉴런은 입력 x0, x1, ..., xn에 w0, w1, ..., wn가 각각 곱해지고 더해집니다.
이것을 **선형결합(sigma)**이라고 합니다. 이 값은 특정 함수 f에 의해 계산된 값이 출력되어 다음 뉴런의 입력으로 보내집니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Z = f(U)&lt;/h3&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;3. 활성화 함수&lt;/h3&gt;
&lt;p&gt;신경계의 뉴런은 들어오는 &lt;strong&gt;자극이 일정 크기 이상이 되어야 다음 뉴런으로 전달&lt;/strong&gt;됩니다.
요것을 모방한 놈이 바로 **활성화 함수(Activation Function)**입니다.&lt;/p&gt;
&lt;p&gt;활성화 함수는 &lt;strong&gt;선형 결합한 값을 다음 출력으로 보내야 할지를 결정&lt;/strong&gt;해 줍니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
Z=f(U)             if U&amp;gt; threshold 1 else 0
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;인간의 뉴런은 &lt;strong&gt;선형결합된 값이 일정 크기를 넘어섰을 때, 다음 출력&lt;/strong&gt;으로 보내집니다.
요것을 그대로 정의한 활성화 함수가 Binary Step 활성화 함수입니다.&lt;/p&gt;
&lt;h2&gt;인공신경망의 구조&lt;/h2&gt;
&lt;p&gt;인공신경망은 입력층(Input Layer), 은닉층(Hidden Layer), 출력층(Output Layer)으로
구성되며 **각 층의 뉴런들을 퍼셉트론(Perceptron)**이라고 부릅니다.
입력층의 뉴런의 수는 입력 변수의 수(입력층에서 바이러스를 사용하면 +1개)이며, 출력층은 분류 문제를 해결할 때 분류의 수와 일치합니다.&lt;/p&gt;
&lt;h3&gt;1. 얕은 신경망(Shallow Neural Network)&lt;/h3&gt;
&lt;p&gt;입력층과 출력층 사이에 있는 숨겨진 계층을 은닉층(Hidden Layer)이라고 합니다.
(뭔가 이름이 멋지지 않나요..? 나만 그런가)&lt;/p&gt;
&lt;p&gt;은닉층은 뉴런(Neuron)과 시냅스(Synapse)로 구성된 인간의 두뇌를 모방한 층(Layer)입니다.
&lt;strong&gt;보통 은닉층의 수가 적거나 없는 신경망을 얕은 신경망(Shallow Neural Network)&lt;/strong&gt; 이라고 부릅니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/shallow_layer.jpeg&quot; alt=&quot;shallow&quot; /&gt;
(은닉층 X, 출력층만 갖는 신경망의 모습)
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;요놈을 이제 수식으로 표현해 볼겁니다.
수식에서 등장하는 **b는 편향(bias)**를 의미합니다.
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/shallow_exp.jpeg&quot; alt=&quot;shallow_exp&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;얕은 신경망은 선형(Linear) 문제를 해결할 때 사용할 수 있겠지만...
비선형 문제를 해결해야 할 경우면 더 깊은 신경망이 필요합니다.&amp;lt;br&amp;gt;
(선형, 비선형에 대해선 다른 포스트에 다루고 있습니다.)&lt;/p&gt;
&lt;h3&gt;2. 여러가지 신경망&lt;/h3&gt;
&lt;p&gt;이제부터 나오는 신경망은 은닉층이 여러개로 구성된 신경망들이 나옵니다!!&lt;/p&gt;
&lt;p&gt;DNN, CNN, RNN 등에 대해서 다뤄보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;다른 포스팅에서 뵈어요!&lt;/p&gt;
</content:encoded></item><item><title>[AI] 합성곱 신경망 CNN(Convolution Neural Network) 소개</title><link>https://compy07.github.io/Blog/posts/ai/neuralnetwork/cnn/introduce/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/neuralnetwork/cnn/introduce/</guid><description>합성곱 신경망(Convolution Neural Network, CNN)에 대해서...</description><pubDate>Sat, 22 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Convolution Neural Network(CNN)이란 무엇인가요?&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./resource/arc.png&quot; alt=&quot;arc&quot; /&gt;
&amp;lt;/center&amp;gt;
Convolution Neural Network(이하 CNN)은 인간의 시각 처리 방식을 본떠 개발된 딥러닝 기술입니다. 특히 컴퓨터 비전(Computer Vision) 분야에서 뛰어난 성능을 발휘하며, 이미지와 관련된 작업에서 핵심적인 역할을 하고 있죠.&lt;/p&gt;
&lt;p&gt;CNN은 일반 신경망을 한 단계 발전시킨 형태라고 볼 수 있습니다. Convolution(합성곱) 연산을 활용해 이미지의 공간적 특징을 효과적으로 유지하면서도, Fully Connected Neural Network에 비해 연산량을 크게 줄였습니다. 덕분에 효율성과 성능을 동시에 잡은 기술로 평가받고 있어요.&lt;/p&gt;
&lt;p&gt;컴퓨터가 이미지를 어떻게 처리하는지 알아보면 CNN의 필요성이 더 분명해집니다. 컴퓨터는 이미지를 단순히 숫자의 배열로 인식합니다. 예를 들어, 흑백 이미지는 픽셀의 밝기를 나타내는 2차원 배열로, 컬러 이미지는 빨강, 초록, 파랑(RGB)의 강도를 담은 3차원 배열로 표현되죠. CNN은 이런 원시 데이터에서 의미 있는 패턴을 스스로 찾아내고 학습할 수 있도록 설계된 똑똑한 구조입니다.&lt;/p&gt;
&lt;h2&gt;CNN의 주요 활용 분야&lt;/h2&gt;
&lt;h3&gt;이미지 분류&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;소셜 미디어에서 사진에 태그를 자동으로 붙이는 기능&lt;/li&gt;
&lt;li&gt;의료 영상 분석을 통해 질병을 진단하는 시스템&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./resource/classification.png&quot; alt=&quot;image&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;객체 검출&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;자율주행 차량에서 보행자를 감지하는 기술&lt;/li&gt;
&lt;li&gt;보안 카메라로 침입자를 탐지하는 용도&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./resource/detection.png&quot; alt=&quot;image&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;이미지 생성 및 변환&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;흑백 사진을 컬러로 바꾸는 작업&lt;/li&gt;
&lt;li&gt;저해상도 이미지를 고해상도로 업스케일링하거나 보정&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./resource/generatino.png&quot; alt=&quot;image&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;이 외에도 의료, 엔터테인먼트 등 다양한 분야에서 CNN이 활약하고 있습니다. 활용 범위가 정말 넓죠!&lt;/p&gt;
&lt;h2&gt;CNN의 탄생과 발전 과정&lt;/h2&gt;
&lt;h3&gt;1950-60년대: 신경과학에서 얻은 영감&lt;/h3&gt;
&lt;p&gt;CNN의 기원은 1959년으로 거슬러 올라갑니다. David Hubel과 Torsten Wiesel이 고양이의 시각 피질을 연구하며 뉴런의 작동 방식을 밝혀냈는데, 이 발견이 CNN의 토대가 되었어요.&lt;/p&gt;
&lt;h4&gt;Local Receptive Field의 발견&lt;/h4&gt;
&lt;p&gt;뉴런은 시야 전체를 한꺼번에 처리하지 않고, 특정 작은 영역(수용 영역)에만 반응한다는 점이 드러났습니다. 이 영역들이 서로 겹치며 전체 시야를 커버하는 구조가 흥미롭죠.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./resource/local_receptive_field.png&quot; alt=&quot;field&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;계층적 정보 처리&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Simple Cell: 직선 같은 단순한 패턴을 감지&lt;/li&gt;
&lt;li&gt;Complex Cell: 더 넓은 범위에서 복잡한 패턴을 인식&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1970-80년대: 신경망의 기초 다지기&lt;/h3&gt;
&lt;p&gt;1979년 Kunihiko Fukushima는 이 아이디어를 바탕으로 Neocognitron을 개발했습니다. 여기서 CNN의 핵심 요소인 Convolutional Layer(특징 추출 층)와 Downsampling Layer(정보 압축 층)이 처음 등장했어요.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./resource/neocognitron.png&quot; alt=&quot;neocognitron&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;1989-1990년대: LeNet의 탄생&lt;/h3&gt;
&lt;p&gt;Yann LeCun과 그의 팀이 만든 LeNet-5는 CNN을 실용화한 첫 사례입니다. 우편번호 인식에 성공적으로 적용되며, 합성곱 층, 풀링 층, 완전 연결 층으로 이어지는 현대 CNN의 표준 구조를 확립했죠.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./resource/lenet.png&quot; alt=&quot;LeNet-5&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;이미지 데이터의 구조&lt;/h3&gt;
&lt;p&gt;컴퓨터가 이미지를 다루려면 시각 정보를 숫자로 변환해야 합니다&lt;/p&gt;
&lt;h4&gt;픽셀과 채널&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;흑백 이미지: 2차원 배열 (가로 × 세로)
&lt;ul&gt;
&lt;li&gt;각 픽셀은 0(검정)에서 255(흰색) 사이의 값을 가짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;컬러 이미지: 3차원 배열 (가로 × 세로 × 채널)
&lt;ul&gt;
&lt;li&gt;Red 채널: 빨간색 강도 (0-255)&lt;/li&gt;
&lt;li&gt;Green 채널: 초록색 강도 (0-255)&lt;/li&gt;
&lt;li&gt;Blue 채널: 파란색 강도 (0-255)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;채널의 역할&lt;/h4&gt;
&lt;p&gt;채널은 이미지의 서로 다른 속성을 나타냅니다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;흑백 이미지: 1채널 (밝기)&lt;/li&gt;
&lt;li&gt;RGB 이미지: 3채널 (빨강, 초록, 파랑)&lt;/li&gt;
&lt;li&gt;RGBA 이미지: 4채널 (RGB + 투명도)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./resource/channelPixel.png&quot; alt=&quot;pixelChannel&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;2012년: AlexNet의 혁신&lt;/h3&gt;
&lt;p&gt;2012년, Alex Krizhevsky, Ilya Sutskever, Geoffrey Hinton이 개발한 AlexNet은 CNN의 전환점을 만들었어요. ImageNet 대회에서 압도적인 성능을 보여주며 주목받았습니다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GPU를 활용한 대규모 학습&lt;/li&gt;
&lt;li&gt;ReLU 활성화 함수 도입&lt;/li&gt;
&lt;li&gt;Dropout으로 과적합 방지&lt;/li&gt;
&lt;li&gt;데이터 증강 기법 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2014년 이후: 더욱 깊고 효율적으로&lt;/h3&gt;
&lt;p&gt;AlexNet의 성공 이후 CNN은 빠르게 발전했습니다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2014년: VGGNet이 더 깊은 네트워크 구조 제시&lt;/li&gt;
&lt;li&gt;2014년: GoogLeNet(Inception)이 효율적인 설계 도입&lt;/li&gt;
&lt;li&gt;2015년: ResNet이 잔차 학습으로 초심층 네트워크 구현&lt;/li&gt;
&lt;li&gt;2017년 이후: MobileNet, EfficientNet 등 가볍고 강력한 모델 출현&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;저는 이제 개념 정리 및 코드로 찾아뵙겠습니다!&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Reference&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@jereminuerofficial/convolutional-neural-networks-explained-6e8c04a8f29b&quot;&gt;Convolutional Neural Networks Explained&lt;/a&gt;&amp;lt;/br&amp;gt;
&lt;a href=&quot;https://theaisummer.com/receptive-field/&quot;&gt;Understanding the receptive field of deep convolutional networks&lt;/a&gt;&amp;lt;/br&amp;gt;
&lt;a href=&quot;https://www.geeksforgeeks.org/&quot;&gt;GeeksforGeeks&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>[DEV] 개발할 때 맨날 발생할 문제, 순환 참조</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/etc/cyclic_import/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/etc/cyclic_import/</guid><description>NameError: name &apos;~~&apos; is not defined. Did you mean: &apos;~~&apos;?, ImportError: cannot import name ~~~</description><pubDate>Sun, 02 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;개발하다보면 어처구니 없는 문제가 종종 발생하곤 합니다. python에서는 조금 많이 발생하는 문제 중 하나가
Cyclic import 문제(다른 이름은 Circular import 문제)입니다.&lt;/p&gt;
&lt;p&gt;예를 들기 위해서 클래스 2개를 한번 만들어 볼게요!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# human_a.py

from .human_b import *
class human_a:
    def __init__(self):
        self.b = human_b()
        return


# human_b.py
from .human_a import *
class human_b:
    def __init__(self):
        self.a = human_a()
        return
        
        
# main.py
human = human_b()
print(human.a)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이러면 &lt;code&gt;human = human_b()&lt;/code&gt; 여기부터 오류가 날겁니다.(정확히는 이걸 타고 올라가서 import 문에서 오류가 터집니다)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;NameError: name &apos;human_a&apos; is not defined. Did you mean: &apos;human_b&apos;?&lt;/code&gt;
이런 문구가 올라오면서요!&lt;/p&gt;
&lt;p&gt;이유는 human_b를 정의하기 위해서 코드로 올라갔더니 human_b를 정의하기 위해서는 human_a라는 친구가 필요했던 겁니다.
그래서 human_a를 먼저 정의하려고 했더니! human_a를 정의하려면 human_b가 필요하네요? 이 상태에 빠지면서 정의가 되지않고 터져버리는거죠.&lt;/p&gt;
&lt;p&gt;이러한 문제가 바로 순환 참조의 문제입니다.
이러한 문제를 해결하기 위해서는 Late Import, 아니면 코드 구조 자체를 바꿔버리는 방법도 있긴 합니다.&lt;/p&gt;
&lt;p&gt;Late import는 필요할 때 동적으로 그때그때 불러오거나, 아예 다 실행되고나서(이미 사용할 클래스가 정의된 후)에 불러오는 것을 말하고
구조 자체를 바꾼다는건 말 그대로 저런 형태의 코드를 짜지 않는 제일 좋은 상황을 말합니다.&lt;/p&gt;
&lt;p&gt;엔진 만들다가 구조 개편하는데 정말 많은 시간을 쏟고 있는 것 같습니다. ㅠㅜㅜㅠ&lt;/p&gt;
</content:encoded></item><item><title>[DEV] mac에서 tkinter를 사용할 때 주의점!</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/etc/tkinter_issue_mac/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/etc/tkinter_issue_mac/</guid><description>Python: No module named _tkinter on Mac</description><pubDate>Sun, 02 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;_tkinter는 python에서 제공하는 GUI 모듈입니다. 그런데 제가 이번에 프로젝트를 진행하면서, 발견한 문제!&lt;/p&gt;
&lt;p&gt;아닛ㅅ tkinter가 pip list | grep tkinter가 걸려도 문제가 발생되는 것!&lt;/p&gt;
&lt;p&gt;아마 개발하시면 모두 있으실 brew 패키지 매니저에서 &lt;code&gt;tcl-tk&lt;/code&gt;을 install 하신 후 환경을 재설치하는게
가장 빠른 방법인 것으로 알게 되었습니다. 일부 환경 변수를 직접 넣어줘도 되긴하는데.. 솔직히 귀찮기도 하고..&lt;/p&gt;
&lt;p&gt;그냥 현재 버전에서 &lt;code&gt;pip freeze &amp;gt; requirements.txt&lt;/code&gt; 이걸로 남기고 지웠다가 다시 합시다!&lt;/p&gt;
&lt;p&gt;저 같은 경우 pyenv를 사용해서 한 버전을 아예 다시 지우고 재설치했더니 작동하는 것을 확인했습니다.&lt;/p&gt;
&lt;p&gt;:::note[해결법]&lt;/p&gt;
&lt;p&gt;&lt;code&gt;brew install tcl-tk&lt;/code&gt; 이후
python 환경을 제거 후 재설치한다.&lt;/p&gt;
&lt;p&gt;pip freeze 사용해서 설치된 패키지 리스트라도 빼놓고 하는 것을 추천한다.&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;필자 환경&lt;/p&gt;
&lt;p&gt;Mac Pro(M2 max), Ram 32GB 입니둥.&lt;/p&gt;
</content:encoded></item><item><title>[DEV] ComPyEngine 5 - Collider 기초</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/colldier/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/colldier/</guid><description>이제 오브젝트 끼리의 충돌처리를 해보자!</description><pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;class TestMovement(Component):
    def __init__(self, game_object):
        super().__init__(&quot;Test&quot;, game_object)

    def update(self, dt):
        if InputManager().get_key(pygame.K_LEFT):
            self.game_object.position[0]-=10
        if InputManager().get_key(pygame.K_RIGHT):
            self.game_object.position[0]+=10
        if InputManager().get_key(pygame.K_UP):
            self.game_object.position[1]-=10
        if InputManager().get_key(pygame.K_DOWN):
            self.game_object.position[1]+=10

    def on_collision_enter(self, other):
        obj = other.game_object

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;커스텀 컴포넌트를 만들어주고요. 이거를 이제 player 오브젝트에 넣어서 움직이는지 확인해볼겁니다.&lt;/p&gt;
&lt;p&gt;또한 완성된 콜라이더가 잘 작동하는지 두 객체에 콜라이더 컴포넌트를 넣어서 작동이 되는지 확인하구요.&lt;/p&gt;
&lt;p&gt;이것을 아래 찍은 영상으로 보여드리겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/feUbxK4XYFs?si=1tllmEy2o4D3oVPw&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;이제는 에디터 패널을 잘 만드는 것으로 다음 목표를 정하고 하겠습니다!&lt;/p&gt;
</content:encoded></item><item><title>[DEV] ComPyEngine 7 - Collision System</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/collisionsystem/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/collisionsystem/</guid><description>충돌 처리를 구현해보자(벽뚫 절대 막아)</description><pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;사실 Collision system은 지금 구현에서 object의 rotation도 만들어둔터라 디게 많은 얘기를 해야해서 아예 따로 빼고,
여기는 그냥 개발하면서 뭐가 어려웠는지 등만 말하고 넘어가겠습니다.&lt;/p&gt;
&lt;p&gt;일단 충돌 지점까지 알아내는 것은 문제가 없었는데,
이동하는 방향과 어떤 곳에서 그리고 어느정도의 힘으로 들어갔는지를 이를 통해서 튕겨나가는 세기를 조절하는 과정에서
detecting에 약간의 오류가 있었는데 그걸 찾는데 너무 많은 시간을 쏟았습니다.&lt;/p&gt;
&lt;p&gt;normal direction이 -로 반대인데 이게 - 곱해지면서 객체들끼리(심지어 한명은 static 시켜놓고 했는데도)
막 엉키더라구요. 그래서 로그 찍어보니까 무슨 방향이 계쏙 왓다갔다 거리는데 처음에는 update 주기에서 각 객체별로 다시 계산하고 횟수가 많아서 이러나?
하고 제한을 뒀을때는 뭔 이상하게 뚝?하고 통과를 해버리질않나. 하튼 그렇게 클로드와 Chat-GPT와 함께 토론을 하며
제 코드가 어디서 이상한 오류를 범했는지 찾아내버렸고!! 그래서 고친 후 잘 작동하게 되었습니다.&lt;/p&gt;
&lt;p&gt;블로그 여러개 보면서 어떤 구조가 편할까 생각하는 시간도 오래 걸렸던 것 같네요.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/3I4o47XQ4zs?si=hBK39Y3RPzoGQe1v&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[DEV] ComPyEngine 3 - Component with SpriteRenderer</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/component/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/component/</guid><description>SpriteRenderer로 랜더링 레츠고야</description><pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이제 좀 눈으로 뭔가를 보고 싶어졌습니다.
그래서 빨리 뭔가 하려고 했는데, 원래 유니티에서도 씬에서 보려면 Renderer를 가진 컴포넌트를 넣어줘야 뭐가 보입니다.
그걸 따라 만들어볼겁니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    def render(self, surface, camera):
        if self.sprite is None:
            return

        transform = self.game_object
        screen_x, screen_y = camera.world_to_screen(
            transform.position[0],
            transform.position[1]
        )

        if camera.zoom != 1.0:
            scaled_sprite = pygame.transform.scale(
                self.sprite,
                (
                    int(self.sprite.get_width() * camera.zoom),
                    int(self.sprite.get_height() * camera.zoom)
                )
            )
            sprite_to_render = scaled_sprite
        else:
            sprite_to_render = self.sprite

        if hasattr(transform, &apos;rotation&apos;) and transform.rotation != 0:
            sprite_to_render = pygame.transform.rotate(sprite_to_render, transform.rotation)

        if hasattr(transform, &apos;scale&apos;) and transform.scale != (1, 1):
            sprite_to_render = pygame.transform.scale(
                sprite_to_render,
                (
                    int(sprite_to_render.get_width() * transform.scale[0]),
                    int(sprite_to_render.get_height() * transform.scale[1])
                )
            )

        rect = sprite_to_render.get_rect(center=(screen_x, screen_y))

        if not surface.get_rect().colliderect(rect):
            return

        if self.color != (255, 255, 255):
            tinted_sprite = sprite_to_render.copy()
            tinted_sprite.fill(self.color, special_flags=pygame.BLEND_MULT)
            surface.blit(tinted_sprite, rect)
        else:
            surface.blit(sprite_to_render, rect)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드처럼 랜더링을 때릴건데, 잘 작동하는지는 모르겠지만, 일단 기본적인 줌 상태가 아닌거는 잘 작동하니까 놔두고 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    def load_image(self, path):
        tmp = EngineManager().load_game_image(path)
        self.sprite = tmp

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;파일 매니저에서 가지고 있는 Image를 불러와서 sprite를 가져옵니다. 모든 오류에 대해서 핸들링을 하도록 노력했고, try문을
안 사용하는 것을 지향햇습니다.&lt;/p&gt;
&lt;p&gt;BaseRenderer를 상속한 SpriteRenderer를 이제 컴포넌트 시스템에 넣어서 이제 잘 작동하는지 확인하기 위해서 이제 기본적인 Rendering 시스템이
필요하겠네요! 설렙니다. 이제 곧 제 눈 앞에 오브젝트가 움직이는 것을 볼 수 있겠군요.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 컴포넌트 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# print(&quot;Component&quot;)
class Component:
    def __init__(self, name, game_object):
        self.game_object = game_object
        self.name = name
        self.enabled = True

    def update(self, dt):

        # fixed update까지는 필요도하지 않을 것으로 보인단 말이지.
        pass

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[DEV] ComPyEngine 2 - Core(Scene, Camera, GameObject)</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/core/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/core/</guid><description>유니티 씬 시스템 따라 만들기</description><pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;일단 게임 오브젝트가 기본적으로 있어야되고, 이 오브젝트에 여러가지 컴포넌트를 넣어서 꾸밀 수 있도록 하려고 합니다.&lt;/p&gt;
&lt;p&gt;게임 오브젝트는 기본적인 자신의 위치와 로테이션, 크기 및 이름 등을 가지게 될 것이구요. 그외 여러가지 유틸리티를 넣으면 되겠습니다.&lt;/p&gt;
&lt;p&gt;씬은 조금 복잡한데, 어디까지 이 친구의 역할을 잡아줘야하는지 감이 잘 안 잡히더라구요. 일단 한 씬에서 사용되는 리소스가 있으니, 캐싱하는 것을 씬한테 맡기려고하고
씬 안에 있는 object를 여기서 다루려고 합니다.
저장과 불러오기 기능이 있어야하는데, 이것은 추후에 추가하기로 하고 일단 작동 시키는 것부터 시작해봅시다.&lt;/p&gt;
&lt;p&gt;카메라는 어떤 위치에서 어느정도의 크기를 찍고 있는지 정도만 가지고 있으면 되겠습니다.&lt;/p&gt;
&lt;p&gt;게임 매니저가 필요해서 데코레이터로 싱글톤 클래스도 조금 만들어주었고, 매니저는 뭐 아시죠? 대충 처리 힘든거 다 때려박을겁니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./structure.png&quot; alt=&quot;file_structure&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 1] File 구조}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;결국 이렇게 완성되었고, 나중에 editor view 완성되면 나오는게 궁금하네요. 빨리 구현하러 가봐야겠습니다.&lt;/p&gt;
&lt;p&gt;먼저 코드는 조금 많이 진행이 되었는데, 구조에서 조금 많이 발전해서 Editor Window와 panel별로 위치 잡아서
랜더링 시스템은 기본적으로 구현했구요.&lt;/p&gt;
&lt;p&gt;Scene 시스템으로&lt;/p&gt;
</content:encoded></item><item><title>[DEV] ComPyEngine 6 - 인스팩터, 하이어라키 패널을 완성해보자</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/editor/inspector_h/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/editor/inspector_h/</guid><description>좀 유니티스러워지기 위해서 예쁘게 만들어보자</description><pubDate>Wed, 05 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./screen_shot.png&quot; alt=&quot;screenshot&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 1] 스크린샷}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;왼쪽이 하이어라키고, 오른쪽이 인스팩터 그리고 가운데가 씬입니다.
이때 하이어라키 창에서 어떤 오브젝트를 클릭하면, 인스팩터 창이랑 연동이 되어야하며 또 그냥 씬 창에서 오브젝트를 클릭해도
연동이 되도록 만들었습니다.&lt;/p&gt;
&lt;p&gt;한 윈도우에서 선택된 오브젝트는 하나라는 것을 알고 잇기에, 그냥 윈도우를 관리하는 놈한테 selected object 박고서 처리했습니다.&lt;/p&gt;
&lt;p&gt;정확히 작동하는 방식은 아래의 영상과 같이 작동합니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/hc5U4fl4nIM?si=xgaIiZX7l4fiB6cx&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[DEV] ComPyEngine 8 - 프로젝트 패널을 완성해보자</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/editor/project/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/editor/project/</guid><description>좀 유니티스러워지기 위해서 예쁘게 만들어보자</description><pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./screenshot.png&quot; alt=&quot;screenshot&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 1] 스크린샷}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;말 그대로 프로젝트 창을 만들어봤습니다.
이미지는 vector icon으로 google에 검색하면 나오는 그 v자 사이트에서 가져왓구요(좋아하는 사이트임)&lt;/p&gt;
&lt;p&gt;폴더를 들어갈 수도 있습니다. 이미지의 경우는 특별하게 미리보기가 가능한데, 이때 사이즈 및 세부사항을 확인 가능합니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/cksxlyYQBkA?si=kfwb24D10uKhd3rE&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[DEV] ComPyEngine 4 - Renderer 기초</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/rendering/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/rendering/</guid><description>이제 오브젝트를 화면에 찍오봐야지</description><pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Component 쪽에서 SpriteRenderer의 대충의 작동 원리를 공개했기 때문에, 그것을 토대로 어떻게 작동하는지 대강 설명드리겠습니다.&lt;/p&gt;
&lt;p&gt;랜더러 매니저를 따로 분리시켜놧는데, 게임 메인 루프에서 현재 올라와있는 Scene의 오브젝트 중 Renderer를 포함하는 오브젝트한테 랜더링을 시키는 작업을 진행합니다.&lt;/p&gt;
&lt;p&gt;EditorWindow라고 아예 큰 window를 생성하고, 이 안에 pygame_gui를 통해서 Panel을 생성해 그 안을 잘게 쪼개서 유니티 에디터처럼 만들 준비를 진행합니다.&lt;/p&gt;
&lt;p&gt;결국 그것을 이용해 완성된 그림은!!!&lt;/p&gt;
&lt;p&gt;이미지는&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scene = Scene(&quot;Level1&quot;)

player = GameObject(&quot;Player&quot;)
player.position = [100, 100]

player.add_component(TestMovement)
player.add_component(BoxCollider)
sprite = player.add_component(SpriteRenderer)
sprite.load_image(&quot;./Resources/test.png&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 식으로 컴포넌트 넣고, sprite도 넣을 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./bg.png&quot; alt=&quot;bg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 1] 초기 버전}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;이렇게 말이죠!!&lt;/p&gt;
&lt;p&gt;다음에는 영상으로 어떻게 작동하는지 찾아오겠씁니다.&lt;/p&gt;
</content:encoded></item><item><title>[DEV] ComPyEngine 9 - Scene을 저장하고, 불러오기!</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/scenesaveload/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/firststep/scenesaveload/</guid><pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;유니티에서 씬 시스템을 그대로 가져온다...&lt;/p&gt;
&lt;p&gt;&quot;이걸 구현하기 위해서는 일단 씬을 저장하는 것부터 해야됩니다. 유니티처럼 그럼 직렬화를 시켜볼까요?&quot;
이 말이 참 밉습니다.&lt;/p&gt;
&lt;p&gt;이 직렬화라는게 아무 생각없이 코드를 짠다면 진짜 정말 힘든 여정이 되버리더군요. 이번에는 오히려 구조 생각하는데 제일 공을 많이 들인거 같은데요.&lt;/p&gt;
&lt;p&gt;무튼 그뿐만 아니라 여러가지 데이터타입 들어오는거 생각해서 확장 고려해 작성하다보니 중간중간 멈칫거리고, 다시 짜고
무한 반복이었습니다. 또 완성해서 기분 좋게 다른거 하다가 중간에 또 터지면 문제 생기고 그리고 우선순위 관련해서 실행 순서가 엄청 중용한 부분이 있었는데 &lt;code&gt;__new__&lt;/code&gt; (이거 객체 미리 생성해놓고
attr 삽입할 때) 그거 고치는데도 좀 고생한 기억이 있네요.&lt;/p&gt;
&lt;p&gt;하튼 그러고 이제 다시 풀고 뭐 하는것도 문제가 많았지만 결국 해냈지 않습니꽈! 아래 영상에서 그 구현된 상태를 보시죠&lt;/p&gt;
&lt;p&gt;(컴포넌트, 오브젝트 따로 빼서 넣고 등 힘드러따)&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/T7Ollcu037E?si=MxQZi-U_CPFPRI3W&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[DEV] ComPyEngine 개발의 시작!</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/intro/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/dev/compyengine/intro/</guid><pubDate>Sat, 01 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;제가 python으로 강화학습을 게임으로 돌리려고 하니까 솔직히... 제가 직접 만든 게임을 돌리고 싶은데, 이미 있는
환경에서 개발하니 재미가 떨어지기 시작했습니다. 그래서! 강화학습용 게임을 쉽게 제작할 수 있는 게임 엔진(에디터)를
만들어서 배포하자!라는 생각으로 시작합니다.&lt;/p&gt;
&lt;p&gt;python 기반 게임 엔진 ComPyEngine!&lt;/p&gt;
&lt;p&gt;먼저 File 구조는 Unity를 따라 만들어보기 위해서 제 나름대로 분리해서 작성했습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./file_structure.png&quot; alt=&quot;file_structure&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 1] File 구조}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;이러한 구조에서 이제 Rendering system, component system 등을 구축하고, 유니티 에디터처럼 멋있는 것을 만들어보도록 하겠습니다.&lt;/p&gt;
</content:encoded></item><item><title>[RL] Q-Learning 이론</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/theory/q_learning/detail/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/theory/q_learning/detail/</guid><description>강화학습의 기초! 그 전설의 시작 Q-learning에 대해서 알아보자</description><pubDate>Tue, 07 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Q-Learning의 배경&lt;/h2&gt;
&lt;p&gt;1980년대 후반, 강화학습 연구에서 가장 큰 도전으로는 &quot;환경 모델을 모르는 상황에서도 최적 정책을 학습하는 것&quot;이였습니다.
이때까지만해도 사용되던 알고리즘이 Dynamic Programming(이하 DP)였는데, 이 DP는 전이 확률과 보상 함수가 완전히 알려진 경우에만
사용이 가능해서 실제 문제에 적용하기가 어려웠습니다.&lt;/p&gt;
&lt;p&gt;그래서 이러한 한계를 극복하기 위해서 model-free 알고리즘인 Q-learning을 개발하였습니다. 이 Q-learning은 model-free 강화학습의
가장 기초이며 핵심이 되는 알고리즘이 된답니다.&lt;/p&gt;
&lt;h2&gt;Q-learning 아이디어&lt;/h2&gt;
&lt;p&gt;환경 모델이 없는 상태에서 최적 정책을 학습할 수 있도록 하는 것이 아이디어입니다.
기존 DP에서 $$ P(s’|s,a) $$
와 보상 함수 R(s, a)를 필요로 했다면, Q-learning은 실제 경 (s, a, r, s’)만으로 Q-value를 업데이트합니다.&lt;/p&gt;
&lt;p&gt;그래서 Q-learning의 핵심은 행동-가치 함수 Q(s, a)를 직접 학습하는 것입니다. 이제부터 이 함수를 Q함수라고 부르겠습니다.&lt;/p&gt;
&lt;p&gt;이 Q함수는 상태 s에서 행동 a를 취한 후 최적 정책을 따를 때의 기대 누적 보을 나타냅니다.
&lt;img src=&quot;QFunction_image.png&quot; alt=&quot;Q-function&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$Q^&lt;em&gt;(s,a) = R(s,a) + \gamma \sum_{s&apos;} P(s&apos;|s,a) \max_{a&apos;} Q^&lt;/em&gt;(s&apos;,a&apos;)$$&lt;/p&gt;
&lt;p&gt;$$\text{벨만 최적 방정식}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;수식에 대해서 조금 더 설명을 더해보자면, $$P(s&apos;|s,a)$$은
전이확률로 상태 s에서 행동 a를 했을 때, 상태 s`가 될 확률을 말합니다.
$$\max_{a&apos;} Q^*(s&apos;,a&apos;)$$는 다음으로 될 상태 $$s&apos;$$에서 선택할 수 있는 행동 중에 가장 높은 Q-value를 의미합니다.&lt;/p&gt;
&lt;p&gt;그래서 $$\sum_{s&apos;} P(s&apos;|s,a) \max_{a&apos;} Q^*(s&apos;,a&apos;)$$ 이 부분은
다음 상태에서 얻을 수 있는 최적 가치의 기댓값으로 다음 상태에서 최선의 선택을 할 때 얻을 가치들의 확률적 평균으로 생각하면 되겠습니다.&lt;/p&gt;
&lt;p&gt;즉, 벨만 최적 방정식은 Q-value를 계산하는 수식으로 $$R(s,a)\text{(현재 즉시 제공되는 보상)}$$과 $$\gamma\text{(할인 인자, discount factor)}$$가 곱해진 미래 가치 기댓값과의 합을 의미합니다.&lt;/p&gt;
&lt;p&gt;Q-learning의 업데이트 규칙은 벨만 방정식과 시간차 학습(Temporal Difference Learning)을 기반으로 합니다.
조금 더 자세히 들어가자면, Q-learning의 업데이트 규칙은 벨만 최적 방정식의 해를 찾기 위해 시간차 학습을 활용하고,
매 스탭마다 TD error를 계산, Q-value를 업데이트 해갑니다. 이를 통해서 벨만 최적 방정식을 근사합니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$Q(s,a) \leftarrow Q(s,a) + \alpha\left[r + \gamma \max_{a&apos;} Q(s&apos;,a&apos;) - Q(s,a)\right]$$&lt;/p&gt;
&lt;p&gt;$$\text{Q-Learning 업데이트 규칙}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;업데이트 규칙에서는 $$\alpha$$는 학습률,$$r$$은 관찰된 즉시 보상,$$\gamma$$는 할인 인자이고, 대괄호 안의 항은 시간차 오차(TD Error)로, 현재 추정값과 실제 경험 사이의 차이를 나타냅니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br/&amp;gt;
&amp;lt;br/&amp;gt;&lt;/p&gt;
&lt;h2&gt;Q-learning 특징&lt;/h2&gt;
&lt;h3&gt;1. off-policy 학습 가능&lt;/h3&gt;
&lt;p&gt;off-policy 학습이 가능하다는 소리는 무슨 소리냐?
학습하려는 목표 정책과 실제 행동을 정하는 행동 정책이 다를 수 있음을 말합니다.&lt;/p&gt;
&lt;p&gt;좀 더 자세히 설명하자면,
Q-learning은 $$\epsilon$$-greedy와 같은 탐험적 정책을 사용해 데이터를 수집하면서도, greedy 정책에 대한 Q-value를 학습할 수 있다는 의미입니다.&lt;/p&gt;
&lt;h3&gt;2. 수렴성 조건&lt;/h3&gt;
&lt;p&gt;먼저 수렴성을 조사하기 위해서 학습률 조건을 따져봐야합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;상태-행동 쌍에 대한 학습률의 총합은 무한대로 발산하며, 학습률 제곱의 총합은 윤한한 값으로 수렴해야됩니다.&lt;/li&gt;
&lt;li&gt;모든 상태에서 모든 가능한 행동을 마훈히 반복적으로 경험해야됩니다.&lt;/li&gt;
&lt;li&gt;Q-value는 개별적인 상태-행동 쌍에 대해 테이블 형태(look-up table)로 표현 가능해야합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이정도가 있고요. 자세한 설명과 수식을 보고 싶으시면 아래 reference에 survey를 참고해주세요.
하튼 그리하여 이러한 조건 하에서 Q-learning은 확률 1로 최적 Q-function로 수렴합니다.&lt;/p&gt;
&lt;h3&gt;3. 탐험 전략&lt;/h3&gt;
&lt;p&gt;보통 예제나 많이 사용되는 알고리즘으로 꼽히는게 $$\epsilon$$-greedy입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ε-greedy 정책&lt;/strong&gt;: 확률 ε로 무작위 행동을, (1-ε)로 탐욕적 행동을 선택하는 알고리즘&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$
\pi(a|s) = \begin{cases}
1-\epsilon + \frac{\epsilon}{|A|} &amp;amp; \text{if } a = \arg\max_{a&apos;} Q(s,a&apos;) \
\frac{\epsilon}{|A|} &amp;amp; \text{otherwise}
\end{cases}
$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;식으로는 위와 같이 표현 가능합니다.&lt;/p&gt;
&lt;p&gt;그 외 정책들을 보고 싶으시면 똑같이 reference에서 survey를 참고해주세요.&lt;/p&gt;
&lt;h3&gt;4. 한계&lt;/h3&gt;
&lt;p&gt;한계로는 &lt;strong&gt;확장성 문제&lt;/strong&gt;, &lt;strong&gt;연속 공간의 한계&lt;/strong&gt;, &lt;strong&gt;샘플 효율성&lt;/strong&gt;, &lt;strong&gt;수렴 속도 문제&lt;/strong&gt;가 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;확장성 문제&lt;/strong&gt;: 상태 공간과 행동 공간이 클 경우 Q-table의 크기가 기하급수적으로 증가하여 메모리와 계산 시간 측면에서 실용적이지 않음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연속 공간의 한계&lt;/strong&gt;: 연속적인 상태나 행동 공간에서는 Q-table 표현 자체가 불가능하여 함수 근사가 필수적&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;샘플 효율성&lt;/strong&gt;: 모든 상태-행동 쌍을 충분히 탐험해야 하므로 샘플 효율성이 낮음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;수렴 속도 문제&lt;/strong&gt;: 이론적으로는 수렴이 보장되지만, 큰 상태 공간에서는 실용적인 시간 내에 수렴하지 않을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇게 정리할 수 있겠습니다.&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;p&gt;[1] J. H. Lee (compy), &quot;Reinforcement Learning: A Survey,&quot; compy&apos;s Blog, Aug. 10, 2025. [Online]. Available: &lt;a href=&quot;https://compy07.github.io/Blog/posts/ai/reinforcementlearning/ecosystemproject/surveypaper/&quot;&gt;URL&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[2] C. J. C. H. Watkins and P. Dayan, &quot;Technical Note: Q-Learning,&quot; Machine Learning, vol. 8, no. 3, pp. 279-292, May 1992, doi: 10.1023/A:1022676722315.&lt;/p&gt;
</content:encoded></item><item><title>[RL] Q-Learning 구현</title><link>https://compy07.github.io/Blog/posts/ai/reinforcementlearning/theory/q_learning/implementation/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/ai/reinforcementlearning/theory/q_learning/implementation/</guid><description>강화학습의 기초! 그 전설의 시작 Q-learning을 구현해보자</description><pubDate>Wed, 08 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Q-Learning의 기초적인 설명은 &lt;a href=&quot;https://compy07.github.io/Blog/posts/ai/reinforcementlearning/theory/q_learning/detail/&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;Q-learning에 대해서 배워봤으니, 이제 눈과 코드로 어떻게 된건지 볼 차례입니다.
예제부터 쭉 한번 작성해봅시다.&lt;/p&gt;
&lt;p&gt;먼저 Q-learning으로 저는 maze를 탈출하는 방법을 가르쳐볼겁니다.&lt;/p&gt;
&lt;p&gt;일단 먼저 시각화를 하기 위해서 pygame으로 틀을 짜볼까요??&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Grid:

    def __init__(self, x: int, y: int, grid_type: str = &quot;basic&quot;,
                 text: str = &quot;&quot;, color: tuple = (255, 255, 255)):
        self.x = x
        self.y = y
        self.grid_type = grid_type
        self.text = text
        self.color = color

        return


    def __str__(self):
        return f&quot;현재 위치 ({self.x}, {self.y}) [{self.color}]&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;하나의 격자 블럭을 이렇게 정의해봅시다.&lt;/p&gt;
&lt;p&gt;이거를 Block처럼 하나씩 쌓아서 Board라는 클래스로 미로를 한번 만들어봅시다.
위에서 설명한 Q-learning처럼 너무 큰 상태 공간은 정말 말도 안되게 학습 오래 걸리기 때문에 5*5로 해봅시다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; Board Class &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Board:
    def __init__(self, width: int, height: int, block_size: int):
        pygame.init()
        pygame.font.init()

        self.width = width
        self.height = height

        self.font = pygame.font.SysFont(&apos;Arial&apos;, 30)

        self.board = [[Grid(x, y) for x in range(width)] for y in range(height)]
        self.block_size = block_size
        self.padding = 20

        self.background = (255, 255, 255)

        # pygame section
        self.screen = pygame.display.set_mode(
            (block_size * width + self.padding * 2, block_size * height + self.padding * 2))

        self.update()
        return

    def set_grid(self, new: Grid):
        self.board[new.y][new.x] = new
        return

    def update(self):
        self.screen.fill(self.background)

        for y in range(self.height):
            for x in range(self.width):
                self.grid_update(x, y)

        self.draw_grid_lines()

        pygame.display.flip()
        # pygame.display.update()

        return

    def draw_grid_lines(self):
        line_color = (0, 0, 0)
        line_width = 1

        for x in range(self.width + 1):
            start_pos = (self.padding + x * self.block_size, self.padding)
            end_pos = (self.padding + x * self.block_size,
                       self.padding + self.height * self.block_size)
            pygame.draw.line(self.screen, line_color, start_pos, end_pos, line_width)

        for y in range(self.height + 1):
            start_pos = (self.padding, self.padding + y * self.block_size)
            end_pos = (self.padding + self.width * self.block_size,
                       self.padding + y * self.block_size)
            pygame.draw.line(self.screen, line_color, start_pos, end_pos, line_width)
        return

    def grid_update(self, x: int, y: int):
        block = self.board[y][x]
        if block is None: return
        # print(x, y, block)
        rect = pygame.draw.rect(self.screen, block.color,
                                (
                                    self.padding + x * self.block_size, self.padding + y * self.block_size,
                                    self.block_size,
                                    self.block_size))

        text = self.font.render(block.text, True, (255, 255, 255))
        text_rect = text.get_rect()
        text_rect.center = (self.padding + x * self.block_size + self.block_size // 2,
                            self.padding + y * self.block_size + self.block_size // 2)
        self.screen.blit(text, text_rect)

        return

    def valid_position(self, y, x):
        if 0 &amp;lt;= y &amp;lt; self.height and 0 &amp;lt;= x &amp;lt; self.width:
            return self.board[y][x].grid_type != &quot;wall&quot;
        return False

    def __copy__(self):
        result = Board(self.width, self.height, self.block_size)

        for y in range(self.height):
            for x in range(self.width):
                result.set_grid(self.board[y][x])
        return result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;일단 위와 같이 클래스 Board를 만들고, 이걸 통해서 시각화를 진행해 봅시다.&lt;/p&gt;
&lt;p&gt;간단한 maze를 만들어 보았습니다. 아래의 코드를 실행시키면!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; main 초기 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from CompyGrid import *
from Algorithm import *
import pygame


def initialize() -&amp;gt; Board:
    x = 5
    y = 5

    board = Board(x, y, 150)

    maze = [
        [1, 1, 1, 0, 0],
        [1, 0, 1, 1, 0],
        [1, 1, 0, 1, 1],
        [1, 0, 0, 1, 0],
        [1, 1, 1, 1, 1]
    ]

    for i in range(y):
        for j in range(x):
            state = maze[i][j]

            block = Grid(j, i, &quot;empty&quot; if state else &quot;wall&quot;, str(state), (0, 170, 200) if state else (175, 0, 0))
            board.set_grid(block)


    return board



board = initialize()
board.update()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            break;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;maze = [
        [1, 1, 1, 0, 0],
        [1, 0, 1, 1, 0],
        [1, 1, 0, 1, 1],
        [1, 0, 0, 1, 0],
        [1, 1, 1, 1, 1]
    ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./start_board.png&quot; alt=&quot;상태&quot; /&gt;
처럼 나오게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br/&amp;gt;
&amp;lt;br/&amp;gt;&lt;/p&gt;
&lt;p&gt;일단 Q-learning으로 진짜되나? 얼마나 잘 되는 것일까?를 보기 위해서 bruteforce 즉 랜덤으로 탈출하는
알고리즘을 작성해서 비교해봅시다!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; Bruteforce 알고리즘&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
import pygame
import random
import imageio
from CompyGrid.Grid import Grid


class Bruteforce:
    def __init__(self, output_path=&quot;bruteforce_path&quot;):
        self.output_path= output_path
        pass

    def bruteforce_escape(self, board, max_steps=100000, record=True):
        agent_pos = [0, 0]
        goal_pos = [board.width - 1, board.height - 1]
        steps = 0
        path = [agent_pos.copy()]

        clock = pygame.time.Clock()

        while agent_pos != goal_pos and steps &amp;lt; max_steps:
            action = random.randint(0, 3)  # 0=right, 1=left, 2=down, 3=up

            x, y = agent_pos[0], agent_pos[1]
            new_x, new_y = x, y

            if action == 0:  # right
                new_x = x + 1
            elif action == 1:  # left
                new_x = x - 1
            elif action == 2:  # down
                new_y = y + 1
            elif action == 3:  # up
                new_y = y - 1

            if board.valid_position(new_y, new_x):
                agent_pos = [new_x, new_y]
                path.append(agent_pos.copy())

            steps += 1

            if steps % 10 == 0:
                board.screen.fill(board.background)

                for grid_y in range(board.height):
                    for grid_x in range(board.width):
                        board.grid_update(grid_x, grid_y)

                pre = board.board[agent_pos[1]][agent_pos[0]]


                agent_grid = Grid(agent_pos[0], agent_pos[1],
                                  text=&quot;B&quot;, color=(255, 165, 0))
                board.set_grid(agent_grid)
                board.grid_update(agent_pos[0], agent_pos[1])

                goal_grid = Grid(goal_pos[0], goal_pos[1],
                                 grid_type=&quot;goal&quot;,
                                 text=&quot;G&quot;, color=(0, 255, 0))
                board.set_grid(goal_grid)
                board.grid_update(goal_pos[0], goal_pos[1])

                font = pygame.font.SysFont(&apos;Arial&apos;, 20)
                text = font.render(f&quot;Brute-force | Steps: {steps}&quot;, True, (255, 255, 255))
                board.screen.blit(text, (10, 10))

                pygame.display.flip()
                clock.tick(60)
                board.set_grid(pre)
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        return steps, path
            print(agent_pos, goal_pos)

        if agent_pos == goal_pos:
            print(f&quot;Bruteforce: 총 {steps} 스텝&quot;)
        else:
            print(f&quot;Bruteforce 실패&quot;)

        if record and agent_pos == goal_pos:
            self.save_path_as_gif(board, path, goal_pos, f&quot;{self.output_path}.gif&quot;, fps=30)

        return steps, path

    def save_path_as_gif(self, board, path, goal_pos, filename, fps=10):
        frames = []

        sample_rate = max(1, len(path) // 1000)
        sampled_path = path[::sample_rate]

        print(f&quot;총 {len(path)} 스텝 중 {len(sampled_path)} 프레임 녹화...&quot;)

        for i, pos in enumerate(sampled_path):
            board.screen.fill(board.background)

            for y in range(board.height):
                for x in range(board.width):
                    board.grid_update(x, y)

            for j in range(max(0, i - 50), i):
                if j &amp;lt; len(sampled_path):
                    trail_pos = sampled_path[j]
                    trail_grid = Grid(trail_pos[0], trail_pos[1],
                                      grid_type=&quot;trail&quot;,
                                      text=&quot;&quot;, color=(200, 200, 100))
                    board.set_grid(trail_grid)
                    board.grid_update(trail_pos[0], trail_pos[1])

            agent_grid = Grid(pos[0], pos[1],
                              grid_type=&quot;agent&quot;,
                              text=&quot;B&quot;, color=(255, 165, 0))
            board.set_grid(agent_grid)
            board.grid_update(pos[0], pos[1])

            goal_grid = Grid(goal_pos[0], goal_pos[1],
                             grid_type=&quot;goal&quot;,
                             text=&quot;G&quot;, color=(0, 255, 0))
            board.set_grid(goal_grid)
            board.grid_update(goal_pos[0], goal_pos[1])

            font = pygame.font.SysFont(&apos;Arial&apos;, 30)
            original_step = i * sample_rate
            text = font.render(f&quot;Brute-force | Step: {original_step}/{len(path)}&quot;,
                               True, (255, 255, 255))
            board.screen.blit(text, (10, 10))

            pygame.display.flip()

            frame = pygame.surfarray.array3d(board.screen)
            frame = frame.transpose([1, 0, 2])
            frames.append(frame)

        duration = 1000 / fps
        imageio.mimsave(filename, frames, duration=duration, loop=0)
        

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;이렇게 작성하여서, gif로 어떻게 되었는지 결과도 볼 수 있게 만들어봤습니다.&lt;/p&gt;
&lt;p&gt;이제 Q-learning을 작성해봅시다!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import copy
import pygame
from CompyGrid.Grid import Grid
import os
from PIL import Image
import imageio

import numpy as np


class QLearning:

    def __init__(self, n_states, n_actions, learning_rate=0.1, discount_factor=0.9, epsilon=0.2):
        &quot;&quot;&quot;
        n_states: 상태의 개수
        n_actions: 행동의 개수
        learning_rate: 학습률 (alpha)
        discount_factor: 할인율 (gamma)
        epsilon: 탐험 확률 (epsilon-greedy)
        &quot;&quot;&quot;

        self.n_states = n_states
        self.n_actions = n_actions
        self.lr = learning_rate
        self.gamma = discount_factor
        self.epsilon = epsilon

        self.q_table = np.zeros((n_states, n_actions))  # (상태 * 행동)



        self.directions = [&quot;right&quot;, &quot;left&quot;, &quot;down&quot;, &quot;up&quot;]

        return

    def get_action(self, state):  # epsilon greedy로 탐험과 활용
        if np.random.random() &amp;lt; self.epsilon:
            return np.random.randint(0, self.n_actions)
        else:
            return np.argmax(self.q_table[state, :])
    def update_q_table(self, state, action, reward, next_state):
        &quot;&quot;&quot;
        Q-table (Q-Learning의 핵심입니다)

        ------------

        state: 현재 상태
        action: 수행한 행동
        reward: 받은 보상
        next_state: 다음 상태
        &quot;&quot;&quot;
        # Q-Learning 업데이트 공식
        # Q(s,a) = Q(s,a) + α[r + γ*max(Q(s&apos;,a&apos;)) - Q(s,a)]

        current_q = self.q_table[state, action]
        max_next_q = np.max(self.q_table[next_state, :])

        new_q = current_q + self.lr * (reward + self.gamma * max_next_q - current_q)
        self.q_table[state, action] = new_q

    def theta_to_pi(self, theta):  # 현재 행동 확률 분포 반환
        # Softmax 함수 적용
        exp_theta = np.exp(theta)
        pi = exp_theta / np.sum(exp_theta, axis=1, keepdims=True)
        return pi
    def get_policy(self):  # 행동 반환해주는거 argmax Q(s, a)
        return np.argmax(self.q_table, axis=1)

    def train(self, board, episodes=1000, render_interval=100):
        for episode in range(episodes):
            agent_pos = [0, 0]
            goal_pos = [board.width - 1, board.height - 1]
            state = agent_pos[1] * board.width + agent_pos[0]

            done = False
            total_reward = 0
            steps = 0
            max_steps = board.width * board.height * 4
            path = [agent_pos.copy()] if render_interval &amp;gt; 0 and episode % render_interval == 0 else None

            while not done and steps &amp;lt; max_steps:
                action = self.get_action(state)


                x, y = agent_pos[0], agent_pos[1]
                new_x, new_y = x, y

                if action == 0:  # right
                    new_x = x + 1
                elif action == 1:  # left
                    new_x = x - 1
                elif action == 2:  # down
                    new_y = y + 1
                elif action == 3:  # up
                    new_y = y - 1


                if board.valid_position(new_y, new_x):
                    agent_pos = [new_x, new_y]
                    reward = 0
                else:
                    reward = 0

                next_state = agent_pos[1] * board.width + agent_pos[0]


                if agent_pos == goal_pos:
                    reward = 10
                    done = True


                self.update_q_table(state, action, reward, next_state)

                state = next_state
                total_reward += reward
                steps += 1


                if path is not None:
                    path.append(agent_pos.copy())


            if path is not None:
                self._render_path(board, path, goal_pos)

            if (episode + 1) % 100 == 0:
                print(f&quot;Episode {episode + 1}/{episodes}, &quot;
                      f&quot;Total Reward: {total_reward:.2f}, &quot;
                      f&quot;Steps: {steps}&quot;)

        return self.q_table



    def _render_path(self, board, path, goal_pos):
        for i, pos in enumerate(path):
            board.screen.fill(board.background)
            for y in range(board.height):
                for x in range(board.width):
                    board.grid_update(x, y)
            for prev_pos in path[:i]:
                trail_grid = Grid(prev_pos[0], prev_pos[1],
                                  grid_type=&quot;trail&quot;,
                                  text=&quot;&quot;, color=(150, 150, 255))
                board.set_grid(trail_grid)
                board.grid_update(prev_pos[0], prev_pos[1])


            agent_grid = Grid(pos[0], pos[1],
                              grid_type=&quot;agent&quot;,
                              text=&quot;A&quot;, color=(255, 0, 0))
            board.set_grid(agent_grid)
            board.grid_update(pos[0], pos[1])


            goal_grid = Grid(goal_pos[0], goal_pos[1],
                             grid_type=&quot;goal&quot;,
                             text=&quot;G&quot;, color=(0, 255, 0))
            board.set_grid(goal_grid)
            board.grid_update(goal_pos[0], goal_pos[1])

            pygame.display.flip()
            pygame.time.wait(100)

    def record_episode(self, board, output_path=&quot;agent_path&quot;, format=&quot;gif&quot;, fps=10):
        frames = []
        agent_pos = [0, 0]
        goal_pos = [board.width - 1, board.height - 1]
        state = agent_pos[1] * board.width + agent_pos[0]

        done = False
        steps = 0
        max_steps = board.width * board.height * 4
        path = [agent_pos.copy()]
        while not done and steps &amp;lt; max_steps:
            action = int(self.get_policy()[state])

            x, y = agent_pos[0], agent_pos[1]
            new_x, new_y = x, y

            if action == 0:  # right
                new_x = x + 1
            elif action == 1:  # left
                new_x = x - 1
            elif action == 2:  # down
                new_y = y + 1
            elif action == 3:  # up
                new_y = y - 1

            if board.valid_position(new_y, new_x):
                agent_pos = [new_x, new_y]
                path.append(agent_pos.copy())

            next_state = agent_pos[1] * board.width + agent_pos[0]
            done = (agent_pos == goal_pos)
            state = next_state
            steps += 1

        for i, pos in enumerate(path):
            board.screen.fill(board.background)
            for y in range(board.height):
                for x in range(board.width):
                    board.grid_update(x, y)


            for prev_pos in path[:i]:
                trail_grid = Grid(prev_pos[0], prev_pos[1],
                                  grid_type=&quot;trail&quot;,
                                  text=&quot;&quot;, color=(150, 150, 255))
                board.set_grid(trail_grid)
                board.grid_update(prev_pos[0], prev_pos[1])


            agent_grid = Grid(pos[0], pos[1],
                              grid_type=&quot;agent&quot;,
                              text=&quot;A&quot;, color=(255, 0, 0))
            board.set_grid(agent_grid)
            board.grid_update(pos[0], pos[1])


            goal_grid = Grid(goal_pos[0], goal_pos[1],
                             grid_type=&quot;goal&quot;,
                             text=&quot;G&quot;, color=(0, 255, 0))
            board.set_grid(goal_grid)
            board.grid_update(goal_pos[0], goal_pos[1])

            pygame.display.flip()


            frame = pygame.surfarray.array3d(board.screen)
            frame = frame.transpose([1, 0, 2])
            frames.append(frame)


        if format == &quot;gif&quot;:
            self._save_as_gif(frames, f&quot;{output_path}.gif&quot;, fps)
        return len(path)
    def _save_as_gif(self, frames, filepath, fps):
        duration = 1000 / fps  # ms
        imageio.mimsave(filepath, frames, duration=duration, loop=0)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;사실 블로그에 이론적 설명은 전 포스트에서 했었고, 여기서 또 설명하기 보다는 코드에 조금 설명을 적어 놓았습니다.&lt;/p&gt;
&lt;p&gt;이 흐름대로 쭉 보시면 이해하기 편할 것이라 생각했구요. 시각화 코드는 그냥 무시하셔도 전혀 지장이 없습니다. 왜냐면.. 코드가 좋지 않아요 ㅠ
그냥 어떻게든 시각화하고, 결과 뽑으려고 짠 코드라 그렇게 좋은 코드는 아닌것 같습니다.&lt;/p&gt;
&lt;p&gt;하튼 이렇게 완성된 코드를 가지고 이제 main에 가져와서 둘 다 결과를 뽑아봅시다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br/&amp;gt;
&amp;lt;br/&amp;gt;
&amp;lt;br/&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from CompyGrid import *
from Algorithm import *
import pygame


def initialize() -&amp;gt; Board:
    x = 5
    y = 5

    board = Board(x, y, 150)

    maze = [
        [1, 1, 1, 0, 0],
        [1, 0, 1, 1, 0],
        [1, 1, 0, 1, 1],
        [1, 0, 0, 1, 0],
        [1, 1, 1, 1, 1]
    ]

    for i in range(y):
        for j in range(x):
            state = maze[i][j]

            block = Grid(j, i, &quot;empty&quot; if state else &quot;wall&quot;, str(state), (0, 170, 200) if state else (175, 0, 0))
            board.set_grid(block)


    return board



board = initialize()
board.update()


bruteforce = Bruteforce()
bruteforce_steps, bruteforce_path = bruteforce.bruteforce_escape(
    board, max_steps=100000, record=True
)

board = initialize()
board.update()
n_states = 5 * 5
n_actions = 4
q_learning = QLearning(n_states, n_actions,
                       learning_rate=0.1,
                       discount_factor=0.95,
                       epsilon=0.15)

q_learning.train(board, episodes=10000, render_interval=0)
q_learning.record_episode(board, &quot;qlearning_path&quot;, format=&quot;gif&quot;, fps=5)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 실행을 시켜보면?!&lt;/p&gt;
&lt;p&gt;어떤 결과가 나올까요?&lt;/p&gt;
&lt;p&gt;먼저 브포의 결과를 살펴보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./bruteforce_path.gif&quot; alt=&quot;bruteforce_path_finding&quot; /&gt;&lt;/p&gt;
&lt;p&gt;이렇게 총 129 step을 밟아서 도착점에 도달했습니다. 음.. 정말 비효율적으로 도착했구요.&lt;/p&gt;
&lt;p&gt;&amp;lt;br/&amp;gt;&lt;/p&gt;
&lt;p&gt;이제는 Q-learning은 어떻게 했는지.. (학습은 코드와 같이 10000번 시켜서 다 보여주긴 힘들고, 마지막으로 학습된 상태에 어떻게 길을 찾았나만 보도록 하겠습니다.)&lt;/p&gt;
&lt;p&gt;자 그럼 결과를 보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./qlearning_path.gif&quot; alt=&quot;Q_learning_path_finding&quot; /&gt;&lt;/p&gt;
&lt;p&gt;총 8 step만에 도착했습니다.&lt;/p&gt;
&lt;p&gt;그래서 결과적으로 이런 결과가 나왔는데용?&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;알고리즘&lt;/th&gt;
&lt;th&gt;소요 Step&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bruteforce&lt;/td&gt;
&lt;td&gt;129&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q-Learning&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이렇게 Q-learning이 어떻게 동작하는지(코드로) 알아보았고요, 그 결과를 bruteforce와 비교해보았습니다.&lt;/p&gt;
&lt;p&gt;하지만 저번 포스팅에서도 말했지만, 이 board의 크기가 조금만 커져도 적용이 힘들다는 큰 단점이 존재하구요..&lt;/p&gt;
&lt;p&gt;현재 코드에서 되게 비효율적인 부분이 있을겁니다. 혼자 고쳐보셔도 되고, 아니면 아래 detail 열어서 확인해보시면 좋겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 비효율적인 부분 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                if board.valid_position(new_y, new_x):
                    agent_pos = [new_x, new_y]
                    reward = 0
                else:
                    reward = 0

                next_state = agent_pos[1] * board.width + agent_pos[0]


                if agent_pos == goal_pos:
                    reward = 10
                    done = True

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;지금보면, 이동이 불가능한 곳에 갔을 때 -1을 주고, 이동가능하면 +1을 주고 도착하면 엄청 큰 보상을 주면 더욱 빠르게 학습하겠죠?&lt;/p&gt;
&lt;p&gt;하지만 저는 그렇게 작성하지 않았습니다. 왜 그럴까요? Q-learning이 처음 도입되었을 때는 이론적으로
목표에 도달을 해야지, 그 목표에 대한 가치가 역으로 전파되면서 $$\text{discount factor}^(n-1) * \text{reward}$$로 전파되면서 가치가 업데이트 됩니다.
그걸 정확히 구현하기 위해서 저렇게 했는데요. 저게 사실 조금만 생각해도 문제가 많아 보입니다.
애초에 목표에 도달을 못하면 Q table이 업데이트가 안돼요. 즉, 목표에 도달하지 못하면 Q-table에 의미있는 업데이트가 발생하지 않습니다.
또 이상한 뻘짓하다가 결국에는 목표에 도달했는데, Q-learning에서는 그러한 뻘짓도 좋은거로 착각하는 문제도 있습니다.&lt;/p&gt;
&lt;p&gt;이런 문제들을 우리는&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;희소 보상 문제 (Sparse Reward Problem)&lt;/strong&gt;: 보상이 드물게 주어져 학습 신호가 부족한 문제&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신용 할당 문제 (Credit Assignment Problem)&lt;/strong&gt;: 어떤 과거 행동이 현재 보상에 기여했는지 판단하기 어려운 문제&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;라고 합니다.&lt;/p&gt;
&lt;p&gt;그래서 즉각적 보상을 주는 것으로 더 빨리 학습시킬 수 있는 방법이 있는 겁니당. 이게 현대에는 거의 당연시되게 적용되구요.&lt;/p&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[Algorithm] A* 알고리즘</title><link>https://compy07.github.io/Blog/posts/algorithms/astar/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/astar/</guid><description>A* algorithm with python</description><pubDate>Sat, 06 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;다익스트라 알고리즘보다 더 빠른거... 뭐 더 없을까???
바로 A*(에이스타)라는 알고리즘이 있다. 사실상 길찾기 알고리즘하면 제일 먼저 떠오르는 친구가 아닐까 싶다.
굉장히 유명하면서, 실제로도 많이쓰고 있다. 정말 놀랍게도 Unity 엔진에서 쓰는 NavMesh 또한 A* 기반으로 동작한다.
오래전에 만들어졌지만 아직까지도 많이 사용되고 있는 알고리즘인 만큼 배워두는 것은 좋을 것이다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;:::note[이 포스팅에서는]&lt;/p&gt;
&lt;p&gt;이 포스팅에서는 전적으로 A* 자체에 대해서만 자세히 다루고 있습니다.&lt;/p&gt;
&lt;p&gt;A*를 설명하기 위한 다른 개념들 Heuristic(휴리스틱), PriorityQueue(우선순위 큐) 등의 설명은 생략하고 있습니다.&lt;/p&gt;
&lt;p&gt;다른 개념의 설명은 다른 포스팅에 있습니다. 따로 참고해주세요!&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이제 A* 대해서 알아보도록 하자&amp;lt;br&amp;gt;
먼저 A*의 특징과 더불어 다른 길찾기 알고리즘들과의 차이점을 찾아보자.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;BFS&lt;/th&gt;
&lt;th&gt;Dijkstra&lt;/th&gt;
&lt;th&gt;A*&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;팀섹 그래프 형태&lt;/td&gt;
&lt;td&gt;가중치 X&lt;/td&gt;
&lt;td&gt;가중치 그래프&lt;/td&gt;
&lt;td&gt;가중치 그래프&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기반 알고리즘&lt;/td&gt;
&lt;td&gt;BruteForce(완전탐색)&lt;/td&gt;
&lt;td&gt;Greedy(탐욕)&lt;/td&gt;
&lt;td&gt;Greedy(탐욕)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특징&lt;/td&gt;
&lt;td&gt;시작 노드를 기준으로 모든 노드의 최단 경로 탐색&lt;/td&gt;
&lt;td&gt;시작 노드를 기준으로 모든 노드의 최단 경로 탐색&lt;/td&gt;
&lt;td&gt;시작 노드에서 목표 노드까지의 최단 경로만을 구함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;:::tip[질문]&lt;/p&gt;
&lt;p&gt;표를 보니 다익스트라와 A* 비슷하던데?&lt;/p&gt;
&lt;p&gt;시작 노드를 기점으로 모든 노드의 최단 경로를 탐색한다면 다익스트라가 A*보다 더 좋은 것 아닌가요?&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;이러한 질문이 자연스럽게 생길 것이다. 이것은 일단 다익스트라와 A*의 작동 방식을 이해하면 해소가 된다.&lt;/p&gt;
&lt;p&gt;먼저 다익스트라는 시작 노드로부터 시작해서 가장 최소 비용으로 도달한 노드에서 또 다시 도달한 노드를 시작 노드로 선정해서 다음 탐색 노드를 큐에 넣는 식으로 작동한다.&lt;/p&gt;
&lt;p&gt;그러나 A&lt;em&gt;는 이러한 방식과는 다른 점이 꽤 많다.
A&lt;/em&gt;는 다음으로 탐색하는 노드를 정하는 방식이 많이 다르다.&amp;lt;br&amp;gt;
다익스트라는 시작 노드로부터 현재 노드까지의 최소 비용이면 그 노드를 선택했다.&amp;lt;br&amp;gt;
그러나 A*는 &lt;strong&gt;시작 노드부터터 현재 노드까지의 비용 g(n)&lt;/strong&gt; 과 &lt;strong&gt;현재 노드부터 도착 노드까지의 예상 비용 h(n)&lt;/strong&gt;
이 둘을 더한 값 f(n)의 최소값의 노드를 다음 탐색 노드로 설정한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2&gt;f(n) = g(n) + h(n)&lt;/h2&gt;
&lt;/blockquote&gt;
&lt;p&gt;이러한 f(n)으로 다음 탐색 노드를 설정한다.&lt;/p&gt;
&lt;p&gt;h(n)은 에상 비용을 계산한 값인데... 이 예상 값을 구하는 함수를 Heuristic(휴리스틱) 함수라고 한다.
또한 휴리스틱을 사용하는 대부분의 알고리즘들은 휴리스틱 함수의 설계 방법에 따라서 알고리즘의 성능이 결정된다.
이 말인 즉 A*의 성능 또한 휴리스틱 함수에 따라서 효율이 달라진다는 것이다.&lt;/p&gt;
&lt;p&gt;일단 길찾기 알고리즘이기 때문에 보통은 단순히 노드와 노드 사이의 거리(평면 상에서)를 계산하는 함수를 사용한다.&amp;lt;br&amp;gt;
대표적으로 맨해튼 거리, 유클라디안 거리를 사용한다.&lt;/p&gt;
&lt;p&gt;:::tip[상식 up]&lt;/p&gt;
&lt;p&gt;맨해튼(Manhattan) 거리: 두 점 사이의 수평 및 수직 이동 거리의 합 = &lt;strong&gt;∣p₁−q₁∣+∣p₂−q₂∣+...+|pₙ-qₙ|&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;유클라디안(Euclidean) 거리: 두 점 사이의 수평 및 수직 이동 거리의 제곱의 합의 제곱근 = &lt;strong&gt;sqrt((p₁−q₁)² + (p₂−q₂)²+...+(pₙ-qₙ)²)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;해밍(Hamming) 거리: 맞춤법 검사와 같은 알고리즘에 많이 사용&amp;lt;br&amp;gt;
(이 거리는 그냥 상식으로 알아두자)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;!또한 맨해튼 거리는 항상 유클리드 거리보다 크거나 같다.!&lt;/strong&gt;
:::&lt;/p&gt;
&lt;p&gt;이 포스팅에서는 이상한 무리수를 다루고 싶지 않기때문에 맨해튼을 사용할 것이다.(숫자가 크긴해도 예시를 그렇게 큰 값을 다루지 않을거기 때문)&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;예시를 들어보겠다.
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/distance.png&quot; alt=&quot;distance_describe&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;일단 예시에서 사용될 한 블럭의 한 변은 10이라는 값으로 고정한다.
이때 대각선의 값은 피타고라스의 정리를 이용해 계산해보면 14.1421...&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/calc.png&quot; alt=&quot;calc&quot; /&gt;
&amp;lt;/center&amp;gt;
이러한 값이 나오는데 예시에서는 소수점을 모두 버림하고 14로 설정하였다.
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/example_describe_white.png&quot; alt=&quot;describe&quot; /&gt;
&amp;lt;/center&amp;gt;
이렇게 4가지의 블럭이 있다.
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/output.gif&quot; alt=&quot;process&quot; /&gt;
&amp;lt;/center&amp;gt;
위와 같은 과정을 통해서 목표에 도달하는데.. 이 값들을 보게된다면 뭔가 알 수도 있다.&lt;/p&gt;
&lt;p&gt;정말 놀랍게도 g(n)은 다익스트라와 똑같이 작동하는 값이다.
그러니 휴리스틱을 뺀다면? 다익스트라와 똑같은 결과를 뱉어낼 것이다.&lt;/p&gt;
&lt;p&gt;그리고 그림을 보면 알겠지만, 다익스트라와 다르게 퍼져나가지 않고 목표와 가까운 노드를 먼저 방문함으로써 쓸데없는 연산을 줄였다.
한번 비교해볼까?&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/dijkstra.gif&quot; alt=&quot;dijkstra_process&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/dijkstra_all.gif&quot; alt=&quot;dijkstra_process_2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;이렇듯 다익스트라는 우리가 모든 노드를 탐색하면서(퍼져나가면서) 탐색을하게 된다.&lt;/p&gt;
&lt;p&gt;&quot;우리는 저 목표의 최단 경로만 구하고 싶다!&quot; 이러면 바로 A*를 사용하면 된다.
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/output.gif&quot; alt=&quot;A*_process&quot; /&gt;
&amp;lt;/center&amp;gt;
이 얼마나 좋냐!&lt;/p&gt;
&lt;h2&gt;구현&lt;/h2&gt;
&lt;p&gt;A* 알고리즘을 이제 구현을 해보자!&lt;/p&gt;
&lt;p&gt;먼저 A*에서는 OpenList, ClosedList가 존재한다.&lt;/p&gt;
&lt;p&gt;일단 OpenList는 이제 방문할 노드들이 있는 리스티이며,
ClosedList는 이제 더이상 방문을 하지 않을 친구들을 넣는 리스트이다.&lt;/p&gt;
&lt;h3&gt;과정&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;초기화
&lt;ol&gt;
&lt;li&gt;시작 노드 생성&lt;/li&gt;
&lt;li&gt;OpenList(현재 우선순위 큐)에 push&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;탐색
&lt;ol&gt;
&lt;li&gt;OpenList에서 f(n)이 최소인 노드를 가져옴&lt;/li&gt;
&lt;li&gt;목적지에 도달을 확인함.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;갱신
&lt;ol&gt;
&lt;li&gt;현재 노드에서 갈 수 있는 노드들의 예상비용을 각각 계산한다.&lt;/li&gt;
&lt;li&gt;계산한 노드의 f(n)이 원래 업데이트 해놓았던 비용보다 적다면 다시 비용 업데이트, OpenList에 노드를 push&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;목적지에 도달할때까지 무한반복!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이제 구현된 시각화 코드와 시각화된 결과를 봐볼까요?&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/Astar_visual.gif&quot; alt=&quot;AStar_visual&quot; /&gt;
&amp;lt;/center&amp;gt;
파랑은 지나갈 수 잇는 곳이고, 초록은 탐색한 곳이며, 검은색은 찾은 최적의 경로입니다.&lt;/p&gt;
&lt;p&gt;일부로 저렇게 유도되도록 만들긴 했지만, 실제로 A*가 최적의 해를 주는건 아닙니다.
휴리스틱 기반으로 작동하니 당연하겠지만 위의 결과와 비슷하게, 이상하게 돌아가긴해도, 어느정도 괜찮은 값을 반환하긴 했습니다.&lt;/p&gt;
&lt;p&gt;다음에는 JPS와 미로 생성 알고리즘에 대해서 다뤄보겠습니다!! 시험들 화이팅하시궁!!
진짜 1일 1포스팅 끊긴거 디게 기분이 나쁘군용...ㅠㅜ&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 코드들 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;h2&gt;다익스트라 알고리즘&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;
win_width, win_height = 400, 400  # 가로 세로 400 400
rows, cols = 8, 17  # 세로 가로 / 행과 열
# 8*2 = 64 개의 grid
win = pygame.display.set_mode((win_width, win_height))  # (width, height)
pygame.display.set_caption(&quot;(그리드 &amp;amp; 다익스트라) 알고리즘&quot;)
grid_width, grid_height = win_width // cols, win_height // rows
#                         400 // 4,           400 // 4

target_flag = False  # 목표물이 설정되지 않은 상태
WHITE = (255, 255, 255)  # r, g, b


class Box:
    # constructor.. 박스가 박스이기 위한.. 필요한 것들이 뭐가 있나
    # __init__
    # 필요한것
    # x, y 좌표(위치)
    # start, goal, wall인지
    def __init__(self, x, y, block_width, block_height):
        self.x, self.y = x, y
        self.color = (217, 217, 217)

        self.cost = float(&apos;inf&apos;)

        self.width, self.height = block_width, block_height
        self.start, self.goal, self.wall = False, False, False
        self.path = False
        self.visited = False

    def setcolor(self, color): self.color = color

    def draw(self, mywin):
        # color = (0, 255, 0)
        pos = (self.x, self.y, self.width - 2.5, self.height - 2.5)
        pygame.draw.rect(mywin, self.color, pos)
import heapq
def dij(g, finish_x, finish_y, window):
    result_path = []
    result_cost = float(&apos;inf&apos;)
    q = [(0, 6, 7, [], g[6][7])]
    dx, dy = [0, 0, -1, 1], [1, -1, 0, 0]
    xlen, ylen = len(g), len(g[0])

    cnt = 0

    while q:
        cost, x, y, path, bx = heapq.heappop(q)
        if bx.wall == True or \
                result_cost &amp;lt;= cost or bx.cost &amp;lt;= cost: continue
        g[x][y].cost = cost
        bx.cost = cost
        if (x, y) == (finish_x, finish_y):
            if result_cost &amp;gt; cost:
                result_cost = cost
                result_path = path
        colored = []
        for i in range(4):
            nx, ny = x + dx[i], y + dy[i]
            if 0 &amp;lt;= nx &amp;lt; xlen and 0 &amp;lt;= ny &amp;lt; ylen:
                heapq.heappush(q, (cost + 1, nx, ny, path + [(x, y)], g[nx][ny]))
                colored.append((g[nx][ny], g[nx][ny].color))

        bx.setcolor((0, 224, 255))
        for b, c in colored:
            if c == (255,0,0): continue
            b.setcolor((51, 255, 0))

        for i in g:
            for j in i:
                j.draw(win)

        for b, c in colored: b.setcolor(c)

        pygame.image.save(window, &quot;./screen_to_blog/blog_&quot;+str(cnt)+&quot;.png&quot;)
        cnt+=1

        bx.setcolor((21, 106, 0))

        # win.fill(WHITE) #(r,g,b)
        # pygame.display.update(win)
        pygame.display.flip()
        pygame.time.delay(200)
    return result_path if result_cost != float(&apos;inf&apos;) else None


def solution():
    global target_flag
    # 그래픽 처리는 무조건 refresh 화면을 계속 찍는것
    g = []  # grids
    for i in range(cols):
        temp = []
        for j in range(rows):
            temp.append(Box(i * grid_width, j * grid_height, grid_width, grid_height))  # object 넣기
        g.append(temp)
    start_box = g[0][0]
    start_box.visited = True
    start_box.start = True
    start = False
    q = [start_box]
    target_pos = (0, 0)
    isRun = True
    while isRun:
        # 이벤트.. 이벤트 프로그래밍..  in cs
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()  # system
            elif event.type == pygame.MOUSEMOTION:
                position = pygame.mouse.get_pos()  # (x, y) 현재 마우스의 위치를 return함 tuple의 형태로
                x, y = position
                x, y = x // grid_width, y // grid_height
                if event.buttons[0]:  # 왼쪾 마우스가 클릭되면
                    print(f&quot;방해물 설정 중 : 좌표 x = {x}, y = {y}&quot;)
                    if (x, y) == target_pos or (x, y) == (0, 0): continue
                    g[x][y].setcolor((255, 0, 0))
                    g[x][y].wall = True
                # 1번은 마우스 휠
                elif event.buttons[2] and target_flag == False:  # 오른쪽 마우스 클릭되면
                    print(f&quot;목표물 설정 : 블럭 좌표 x = {x}, y = {y}&quot;)
                    g[x][y].setcolor((0, 0, 255))
                    g[x][y].goal = True
                    target_pos = (x, y)
                    target_flag = True
            elif event.type == pygame.KEYDOWN and target_flag == True:  # 키가 눌렸다면 실행
                print(&apos;알고리즘 시작&apos;)
                start = True
                g[6][7].start = True
                isRun = False
                nf = dij(g, target_pos[0], target_pos[1], win)
                if nf == None: return &quot;경로 없음&quot;
                else:
                    for (x,y) in nf:
                        g[x][y].setcolor((100,100,100))

        for i in g:
            for j in i:
                j.draw(win)

        # win.fill(WHITE) #(r,g,b)
        # pygame.display.update(win)
        pygame.display.flip()
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()  # system
        for i in g:
            for j in i:
                j.draw(win)
    return
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A* 알고리즘 코드&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Astar():
    def __init__(self, grid):
        self.grid = grid

    def heuristic_Diagonal_Distance(self, node, goal, D1=1, D2=2 ** 0.5):
        dx = abs(node.position[0] - goal.position[0])
        dy = abs(node.position[1] - goal.position[1])

        return D1 * (dx + dy) + (D2 - 2 * D1) * min(dx, dy)

    def heuristic_Euclidean_Distance(self, current, end):
        return ((current.position[0] - end.position[0]) ** 2)\
                + ((current.position[1] - end.position[1]) ** 2)

    def CalcNode(self, childNode, currentNode, endNode):
        childNode.g = currentNode.g + 1 + childNode.density ** 2
        childNode.h = self.heuristic_Euclidean_Distance(currentNode, endNode)
        childNode.f = childNode.g + childNode.h

    def NodeSet(self, node, g: int, f: int, h: int):
        node.g = g
        node.h = h
        node.f = f

    def getPath(self, node, board: list[list]):
        path = []
        while node.parent != None:
            y, x = node.position
            board[y][x] = node.f
            path.append(node.position)
            node = node.parent
        return path[::-1]

    def AStar(self, board: list[list], start: tuple, end: tuple, delay: int = 0):
        import heapq as hq
        from CompyLib.Algorithms import Node

        startNode = Node(None, start, board[start[0]][start[1]], 0)
        endNode = Node(None, end, board[end[0]][end[1]], 0)

        self.NodeSet(startNode, 0, 0, 0)

        openList: list[Node] = list()
        closedList: list[Node] = list()

        openList.append(startNode)
        dx, dy = [0, 0, -1, 1, -1, -1, 1, 1], [-1, 1, 0, 0, -1, 1, -1, 1]

        Visualization = self.grid != None
        self.CalcNode(startNode, startNode, endNode)
        while openList:
            currentNode: Node = hq.heappop(openList)
            closedList.append(currentNode)
            if Visualization:
                self.grid.changeBox(self.grid.getBoxbyIndex(currentNode.position[0],
                                                            currentNode.position[1]), &quot;GREEN&quot;)

            if endNode == currentNode:
                return self.getPath(currentNode, board)

            children: list[Node] = []

            for i in range(8):
                ny = currentNode.position[0] + dy[i]
                nx = currentNode.position[1] + dx[i]

                if 0 &amp;gt; ny or ny &amp;gt;= len(board) or 0 &amp;gt; nx or \
                        nx &amp;gt;= len(board[0]) \
                        or board[ny][nx] == 1:
                    continue
                children.append(Node(None, (ny, nx), board[ny][nx], 0))
            for child in children:
                if child in closedList: continue
                self.CalcNode(child, currentNode, endNode)
                # print(child.f, currentNode.f, currentNode, child)
                # if child.f &amp;gt; currentNode.f: continue or child.g + child.density &amp;gt; currentNode.g + 1: continue  # or
                # child.g + child.density &amp;lt;= currentNode.g + 1:
                flag: bool = False
                for node in openList:
                    if child == node and child.g &amp;gt; node.g:
                        flag = True
                        break
                if flag: continue

                child.parent = currentNode

                hq.heappush(openList, child)
            if Visualization: self.grid.boardUpdate(delay)

        return -1

&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;격자 생성&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;class Type:
    def __init__(self, screen: p.Surface = None, row: int = 5, col: int = 5):

        self.Running = True

        # Pygame 객체

        self.screen = screen
        self.row = row
        self.col = col

        if screen == None: self.Running = False

        # 기본 색 설정

        self.RED: tuple = (255, 0, 0)
        self.BLUE: tuple = (0, 0, 255)
        self.GREEN: tuple = (0, 255, 0)
        self.BLACK: tuple = (0, 0, 0)
        self.WHITE: tuple = (255, 255, 255)

        # 색 접근
        self.custom_color: dict = {&quot;RED&quot;: self.RED, &quot;BLUE&quot;: self.BLUE,
                                   &quot;GREEN&quot;: self.GREEN, &quot;BLACK&quot;: self.BLACK,
                                   &quot;WHITE&quot;: self.WHITE, &quot;ORANGE&quot;: (255, 95, 0),
                                   &quot;DARKORANGE&quot;: (255, 135, 0), &quot;GOLD&quot;: (255, 215, 0),
                                   &quot;YELLOW&quot;: (255, 255, 0), &quot;AQUA&quot;: (0, 255, 255),
                                   &quot;NAVYBLUE&quot;: (0, 0, 95)}
        self.color_idx: list = [&quot;BLUE&quot;, &quot;RED&quot;, &quot;GREEN&quot;, &quot;BLACK&quot;, &quot;WHITE&quot;, &quot;ORANGE&quot;,
                                &quot;DARKORANGE&quot;, &quot;GOLD&quot;, &quot;YELLOW&quot;, &quot;AQUA&quot;, &quot;NAVYBLUE&quot;]

    def update(self):
        return

    def changeColor(self, *inp):
        length: int = len(inp[0])

        # print(inp[0])

        inp = inp[0]

        if length &amp;lt; 2 or type(inp[1]) != str:
            return False

        if inp[1] not in self.custom_color.keys():

            if length &amp;lt; 3 or type(inp[2]) != tuple:
                return False

            self.custom_color[inp[1]] = inp[2]

        ret: str

        if type(inp[0]) == int:
            ret = self.color_idx[inp[0]]
            self.color_idx[inp[0]] = inp[1]
        elif type(inp[0]) == str:

            if inp[0] not in self.color_idx:
                return False

            ret = inp[0]
            self.color_idx[self.color_idx.index(inp[0])] = inp[1]
        else:
            return False

        return ret

    def addColor(self, name: str, color: tuple):

        if name == None or name == &quot;&quot;:
            return False

        self.custom_color[name] = color
        return True
        
        
class Grid(Type):

    def __init__(self, screen: p.Surface = None,
                 row: int = 5, col: int = 5,
                 space: float = -1.5):

        super().__init__(screen, row, col)

        self.space = space
        self.board: list = list()  # [[0 for i in range(row)] for i in range(col)]

        (sc_width, sc_height) = screen.get_size()

        self.bx_width = sc_width // row
        self.bx_height = sc_height // col

        for i in range(row):
            self.board.append(list())

            for j in range(col):
                self.board[i].append(Box(i * self.bx_width, j * self.bx_height,
                                         self.bx_width + space, self.bx_height + space))  # x, y, width, height, color

    def boardUpdate(self, delay: int):
        self.screen.fill(self.WHITE)

        for i in self.board:
            for j in i:
                j.draw(self.screen, p)
        p.display.flip()

        p.time.delay(delay)

    def Colors(self):
        cnt: int = 0

        for name, rgb in self.custom_color.items():
            cnt += 1
            print(f&quot;{name}의 rgb값은 {rgb}  &quot;, end=&apos;&apos;)
            if cnt // 3 == 1:
                cnt = 0
                print()

    def changeColor(self, *inp):
        res: str = super().changeColor(inp)
        if not res: return False
        for i in self.board:
            for box in i:
                if box.colorName != res: continue
                box.colorName = inp[1]
                box.color = self.custom_color[inp[1]]
        return True

    def changeBox(self, *inp):
        # x: int, y: int, color=0):
        box: Box

        if len(inp) &amp;gt; 2:
            x, y = inp[0], inp[1]
            box = self.board[y][x]

            if type(inp[2]) == int:
                color: int = inp[2]
            else:
                color: str = inp[2]
        else:
            box = inp[0]

            if type(inp[1]) == int:
                color: int = inp[1]
            else:
                color: str = inp[1]
        # print(inp, box, color)
        if type(color) == int:
            box.colorName = self.color_idx[color]
            box.color = self.custom_color[box.colorName]

        elif type(color) == str:
            box.colorName = color
            box.color = self.custom_color[color]

        else:
            return False
        return True

    def getBoxbyCoordinate(self, x: int, y: int):
        return self.board[x // self.bx_width][y // self.bx_height]

    def getBoxbyIndex(self, x: int, y: int):
        return self.board[y][x]

    def getBox(self, x: int, y: int):
        return self.getBoxbyCoordinate(x, y)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[Algorithm] BSP 알고리즘(맵 생성)</title><link>https://compy07.github.io/Blog/posts/algorithms/bsp/theory/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/bsp/theory/</guid><description>BSP algorithm with Mojo</description><pubDate>Sun, 16 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note[]&lt;/p&gt;
&lt;p&gt;이 포스트는 Only BSP algorithm 이론 파트입니다.&lt;/p&gt;
&lt;p&gt;혹시나 구현을 가져가고 싶으시다면 맨 아래에 있는 링크로 들어가시면 됩니다.
:::&lt;/p&gt;
&lt;h1&gt;BSP 알고리즘이란?&lt;/h1&gt;
&lt;p&gt;BSP(Binary Space Partitioning)는 재귀적으로 유클리드 공간을 초평면 상의 볼록 집합으로 분할하는 기법이다.
분할 과정으로 BSP 트리라 불리는 트리 구조가 만들어진다.&lt;/p&gt;
&lt;p&gt;근데 이게 왜 게임에 많이 쓰일까요?? 일단 유명해진 계기는 둠(Doom)이 요 BSP 알고리즘을 사용하면서 굉장히 유명해 졌습니당.&lt;/p&gt;
&lt;p&gt;자 BSP 알고리즘은 이제 랜더링에서도 사용이 되는데 사실 이번에 볼 것은 랜더링이 아니라 &quot;맵 생성&quot;을 해볼겁니다. (랜더링 관련해서는 다른 포스팅에서...)&lt;/p&gt;
&lt;p&gt;랜덤 맵 생성.. 정말 매력적인 친굽니다. 어릴적 나도 게임을 만들거야!! 했는데 레벨 디자인 가다보면 도대체 시중에 나와있는 게임처럼 어떻게 랜덤으로
맵을 만들까? 라는 의문이 진짜 해결이 안되다가 여러가지 알고리즘 BSP, WFC 등등 이런 애들을 배우면서 와 이게 되누 ㅋㅋ&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./source/first.png&quot; alt=&quot;first&quot; /&gt;&lt;/p&gt;
&lt;p&gt;하면서 해봤던 기억이 있는데요. 그때는 사실 구현이 다 야매였습니다. 진짜 어떻게든 구현한다! 그 마인드.. 그래서 나중에 다른 게임에서 사용하려면 많이 수정을 해야됐었습니다.&lt;/p&gt;
&lt;p&gt;그래서 그때의 시절에 있는 분들께 소개시켜드리고 싶은 알고리즘인데 게임 공부하다 보면 분명 한번쯤은 들어봤을 겁니당.&lt;/p&gt;
&lt;p&gt;그래서 원리부터 쭉 살펴보죠&lt;/p&gt;
&lt;h1&gt;알고리즘&lt;/h1&gt;
&lt;p&gt;다시한번 돌아보면서 시작해보죠!(이 포스트에서는 2차원에 대해서만 설명하겠습니다)&lt;/p&gt;
&lt;p&gt;BSP는 공간을 이진 트리 구조로 재귀적으로 분할하는 방식입니다.
큰 공간을 두 개의 작은 공간으로 나누고, 각각의 작은 공간을 다시 두 개로 나누는 과정을 반복합니다.
계속해서 하나의 판을 반-&amp;gt;반-&amp;gt;반으로 계속 자르는 것을 생각하시면 좋아요&lt;/p&gt;
&lt;p&gt;좀 더 자세히 설명해보죠!&lt;/p&gt;
&lt;h2&gt;공간 분할 과정&lt;/h2&gt;
&lt;h3&gt;1. 초기 공간 설정&lt;/h3&gt;
&lt;p&gt;저는 구현을 &lt;strong&gt;전체 맵을 하나의 직사각형 공간&lt;/strong&gt;으로 잡고서 하였습니다. 뭐 어떻게든 잡아도 되지만 보통 구현이 편한건 당연히 사각형입니다.&lt;/p&gt;
&lt;h3&gt;2. 재귀적 분할&lt;/h3&gt;
&lt;p&gt;이것도 구현에 따라 다르긴 하지만 일반적으로 사용하는 방법을 소개해드리겠습니다.(물론 저도 일반적인 방법으로 구현을 하였구요)&lt;/p&gt;
&lt;h4&gt;a) 분할 방향 결정:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;공간의 가로/세로 비율을 확인
&lt;ul&gt;
&lt;li&gt;너비가 더 크면 수직 분할 선호&lt;/li&gt;
&lt;li&gt;높이가 더 크면 수평 분할 선호&lt;/li&gt;
&lt;li&gt;비슷한 경우 무작위 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;b) 분할 위치 결정:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;일반적으로 공간의 40%~60% 지점에서 분할
&lt;ul&gt;
&lt;li&gt;완전히 중앙에서 분할하지 않는 이유는 &lt;strong&gt;다양한 크기의 공간을 만들기&lt;/strong&gt; 위함&lt;/li&gt;
&lt;li&gt;최소 크기 제한을 두어 &lt;strong&gt;너무 작은 공간이 생기지 않도록 함&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;목적들은 다 직관적으로 이해할만한 것들입니다.&lt;/p&gt;
&lt;h4&gt;3. 종료 조건&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;공간이 최소 크기에 도달&lt;/li&gt;
&lt;li&gt;정해진 분할 횟수에 도달&lt;/li&gt;
&lt;li&gt;가로/세로 비율이 적절한 범위에 들어옴&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./source/split_board.gif&quot; alt=&quot;이해를 돕기 위한 자료&quot; /&gt;&lt;/p&gt;
&lt;p&gt;[이해를 돕기 위한 자료]&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;방 생성 과정&lt;/h2&gt;
&lt;p&gt;:::note[여기서 말하는 노드란?]&lt;/p&gt;
&lt;p&gt;앞서 분할 과정에서 생긴 부분들을 노드로 생각하고, 그것을 트리 형태로 바라보면 Leaf node임.
그래서 여기서 말하는 노드는 leaf node를 말함.
:::&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;노드의 크기보다 약간 작게 방을 생성(이 노드가 분리된 공간)&lt;/li&gt;
&lt;li&gt;벽과의 최소 간격 유지&lt;/li&gt;
&lt;li&gt;방 크기는 정해진 범위 내에서 무작위로 결정&lt;/li&gt;
&lt;li&gt;보통 노드 중앙에 방을 배치&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./source/create_room.gif&quot; alt=&quot;이해를 돕기 위한 자료2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;[이해를 돕기 위한 자료2]&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;복도 생성 과정&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이웃한 방들을 우선적으로 연결&lt;/li&gt;
&lt;li&gt;L자 형태의 복도가 일반적&lt;/li&gt;
&lt;li&gt;모든 방이 최소한 하나의 다른 방과 연결되도록 보장&lt;/li&gt;
&lt;li&gt;선택적으로 순환 경로(루프) 추가 가능(그래서 여러개 나올 수도 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./source/load.png&quot; alt=&quot;이해를 돕기 위한 자료3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;[이해를 돕기 위한 자료3]&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;확장&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;불필요한 복도 제거&lt;/li&gt;
&lt;li&gt;막힌 공간 처리&lt;/li&gt;
&lt;li&gt;장식 요소 추가&lt;/li&gt;
&lt;li&gt;게임플레이를 위한 요소 배치&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;장/단점&lt;/h2&gt;
&lt;h3&gt;장점&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;구현이 비교적 단순하면서도 다양한 결과물 생성 가능(다른 맵 생성 알고리즘에 비해서 굉장히 쉬운편)&lt;/li&gt;
&lt;li&gt;자연스러운 공간 구획이 가능(이거는 구현에 따라 좀 달라지긴 합니다.)&lt;/li&gt;
&lt;li&gt;생성된 맵의 구조가 명확하고 탐색하기 쉬움&lt;/li&gt;
&lt;li&gt;확장성이 좋아 다양한 변형이 가능(1번 부가 설명이죠)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;단점&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;메모리 이슈(매번 노드를 분리하면서 많은 양의 데이터를 계속 저장함)&lt;/li&gt;
&lt;li&gt;맵을 생성하는 과정에서 꽤 많은 시간이 소모됨&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;구현 영상&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;800&quot; height=&quot;600&quot; src=&quot;https://www.youtube.com/embed/hMs-UJ-ZtuE?si=6l3FClcuqN9WyeQH&quot; title=&quot;BSP Algorithm&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;일단 구현하는게 쉽지만은 않았습니다. 원래 알고리즘은 전에 여러번 구현해보기도 해서 그런 것에는 문제가 없을 줄 알았는데... 이게 완벽히 파이썬처럼 작성하기도 하고, 다시 뭔가 oop도 따라하려고 하고
막 어찌저찌 스타일도 섞으면서 하게됐는데요.&lt;/p&gt;
&lt;p&gt;일단 이번에 느낀 것은 Mojo와 Python은 진짜 전혀 다른 언어라는거죠..&lt;/p&gt;
&lt;p&gt;일단 struct로 뭔가를 만들어서 List에 넣고 하려고해도. copyable, moveable을 무조건 넣어줘야 됐다는 것.
그리고 검사가 너무 깐깐합니다. 그래서 나중에 코드 보면서 트러블 슈팅할 때는 괜찮았어요. 오히려 이런 것에서 장점이 나오는 것 같아서 좋긴했는데..&lt;/p&gt;
&lt;p&gt;이 코드가 복잡한 코드도 아니고 그냥 한번 작성하고 보여주기 코드였는데 뭔가 쓸데없는 곳에서 계속 걸리니까 그런 장점보다는 오히려 짜증이 나기도 하더라구요.&lt;/p&gt;
&lt;p&gt;일단 ide가 없고, 자동 완성도 안됩니다. 현재 jetbrain에서 plugin이 있는데 구버전에다가... 성능이 안 좋아서 잘 못잡아요. 허구언날 빨간줄만 띄웁니다.&lt;/p&gt;
&lt;p&gt;그래서 그냥 기능 다 끄고 했구요. 자동 들여쓰기만 키고서 작성했습니다.&lt;/p&gt;
&lt;p&gt;그래서 시간이 오래걸린 것 같아요. 또 이번에 많은 것을 알아갑니다. Mojo에 대해서 많은 것을 알아가요..... 어후..&lt;/p&gt;
&lt;p&gt;또 로직에 문제가 없다고 생각했는데 네... 리스트 값이 변경이 안되고 주소 확인해 보니까 copy가 되더라구요? 그래서 오잉? 하고서 값을 reference로 받거나
아니면 그냥 리스트에 self.nodes[idx] 이거를 직접 사용하거나 이렇게 했어야 했다.&lt;/p&gt;
&lt;p&gt;저는 그냥 var node = ... 뺀 다음에 연산 다 끝나고 self.nodes[idx] = node로 처리를 했습니다. 이게 그 뻘짓 중에 그냥 고치기 쉬웠던 것들 중 하나구요..&lt;/p&gt;
&lt;p&gt;하나하나 쓰기는 좀 그렇고 네.. 힘들었습니다. 그래도 보람이 있었고, 재미있었습니다. 이제 이 언어를 가지고 인공지능을 좀 해보려고 하는데.. 일단
다른 경험으로 IDE를 만들어보려고 합니다. 진짜 힘든 길이겠지만 뭐... 해야죠 저는 이 언어를 쓰고싶은데&lt;/p&gt;
&lt;p&gt;네 여기까지 하소연이였구용.. 하튼 재미있었습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 구현 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;제가 구현한 코드 로직의 순서도입니다. 깔끔하게 정리되는 사이트가 없더라구용..
&lt;img src=&quot;./source/code.svg&quot; alt=&quot;순서도&quot; /&gt;&lt;/p&gt;
&lt;p&gt;또 스네이크로 작성해본 것도 좀 오랜만이네용!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from python import Python, PythonObject
import random
import time

struct Corridor(Copyable, Movable):
    var start_x: Int
    var start_y: Int
    var end_y: Int
    var end_x: Int
    var is_vertical: Bool

    fn __init__(mut self, start_x: Int, start_y: Int, end_x: Int, end_y: Int, is_vertical: Bool):
        self.start_x = start_x
        self.start_y = start_y
        self.end_x = end_x
        self.end_y = end_y
        self.is_vertical = is_vertical

    fn __copyinit__(mut self, other: Self):
        self.start_x = other.start_x
        self.start_y = other.start_y
        self.end_x = other.end_x
        self.end_y = other.end_y
        self.is_vertical = other.is_vertical

    fn __moveinit__(mut self, owned other: Self):
        self.start_x = other.start_x
        self.start_y = other.start_y
        self.end_x = other.end_x
        self.end_y = other.end_y
        self.is_vertical = other.is_vertical

struct BSPNode(Copyable, Movable):
    var x: Int
    var y: Int
    var width: Int
    var height: Int
    var min_size: Int

    var split_vertical: Bool
    var split_pos: Int
    var is_split: Bool
    var is_leaf: Bool

    var has_room: Bool
    var room_x: Int
    var room_y: Int
    var room_width: Int
    var room_height: Int

    var corridors: List[Corridor]

    fn __init__(mut self, x: Int, y: Int, width: Int, height: Int, min_size: Int):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.min_size = min_size

        self.split_vertical = False
        self.split_pos = 0
        self.is_split = False
        self.is_leaf = True

        self.has_room = False
        self.room_x = 0
        self.room_y = 0
        self.room_width = 0
        self.room_height = 0

        self.corridors = List[Corridor]()

    fn __copyinit__(mut self, other: Self):
        self.x = other.x
        self.y = other.y
        self.width = other.width
        self.height = other.height
        self.split_vertical = other.split_vertical
        self.split_pos = other.split_pos
        self.has_room = other.has_room
        self.room_x = other.room_x
        self.room_y = other.room_y
        self.room_width = other.room_width
        self.room_height = other.room_height
        self.is_split = other.is_split
        self.is_leaf = other.is_leaf
        self.min_size = other.min_size
        self.corridors = other.corridors

    fn __moveinit__(mut self, owned other: Self):
        self.x = other.x
        self.y = other.y
        self.width = other.width
        self.height = other.height
        self.split_vertical = other.split_vertical
        self.split_pos = other.split_pos
        self.has_room = other.has_room
        self.room_x = other.room_x
        self.room_y = other.room_y
        self.room_width = other.room_width
        self.room_height = other.room_height
        self.is_split = other.is_split
        self.is_leaf = other.is_leaf
        self.min_size = other.min_size
        self.corridors = other.corridors

    fn can_split(self) -&amp;gt; Bool:
        if self.is_split or not self.is_leaf:
            return False

        if self.width &amp;lt; self.min_size * 2 and self.height &amp;lt; self.min_size * 2:
            return False

        return True

    fn split(mut self) -&amp;gt; Bool:
        if not self.can_split():
            return False

        if self.width * 4 &amp;gt;= self.height * 5:
            self.split_vertical = True
        elif self.height * 4 &amp;gt;= self.width * 5:
            self.split_vertical = False
        else:
            self.split_vertical = random.random_si64(0, 1)[0] == 1

        if self.split_vertical:
            self.split_pos = self.x + Int(random.random_si64(
                self.width * 4 // 10,
                self.width * 6 // 10
            )[0])
        else:
            self.split_pos = self.y + Int(random.random_si64(
                self.height * 4 // 10,
                self.height * 6 // 10
            )[0])

        self.is_split = True
        self.is_leaf = False
        return True

    fn create_room(mut self) -&amp;gt; Bool:
        if not self.is_leaf or self.has_room:
            return False

        var padding = max(2, self.min_size // 4)
        var min_room_size = self.min_size

        var max_width = self.width - (padding * 2)
        var max_height = self.height - (padding * 2)
        var min_width = min(min_room_size, max_width)
        var min_height = min(min_room_size, max_height)

        if max_width &amp;lt; min_width or max_height &amp;lt; min_height:
            return False

        self.room_width = Int(random.random_si64(
            max(min_width, max_width // 2),
            max_width
        )[0])
        self.room_height = Int(random.random_si64(
            max(min_height, max_height // 2),
            max_height
        )[0])

        var available_x = self.width - self.room_width
        var available_y = self.height - self.room_height
        self.room_x = self.x + (available_x // 2)
        self.room_y = self.y + (available_y // 2)

        self.has_room = True
        return True

    fn get_room_center(self) -&amp;gt; Tuple[Int, Int]:
        return (
            self.room_x + self.room_width // 2,
            self.room_y + self.room_height // 2
        )

struct DungeonGenerator:
    var nodes: List[BSPNode]
    var stage: Int
    var current_idx: Int
    var min_room_size: Int
    var split_count: Int
    var max_splits: Int
    var corridors: List[Corridor]

    fn __init__(mut self, width: Int, height: Int, min_room_size: Int, max_splits: Int):
        self.nodes = List[BSPNode]()
        self.nodes.append(BSPNode(0, 0, width, height, min_room_size))
        self.stage = 0
        self.current_idx = 0
        self.min_room_size = min_room_size
        self.split_count = 0
        self.max_splits = max_splits
        self.corridors = List[Corridor]()

    fn connect_rooms(mut self):
        var leaf_nodes = List[BSPNode]()
        for idx in range(len(self.nodes)):
            var node = self.nodes[idx]
            if node.is_leaf and node.has_room:
                leaf_nodes.append(self.nodes[idx])

        if len(leaf_nodes) &amp;lt; 2:
            return

        for i in range(len(leaf_nodes)):
            for j in range(i + 1, len(leaf_nodes)):
                var room1 = leaf_nodes[i]
                var room2 = leaf_nodes[j]

                var center1 = room1.get_room_center()
                var center2 = room2.get_room_center()

                if random.random_si64(0, 1)[0] == 0:
                    var mid_y = center2[1]
                    self.corridors.append(Corridor(
                        center1[0], center1[1],
                        center1[0], mid_y,
                        True
                    ))
                    self.corridors.append(Corridor(
                        center1[0], mid_y,
                        center2[0], mid_y,
                        False
                    ))
                else:
                    var mid_x = center2[0]
                    self.corridors.append(Corridor(
                        center1[0], center1[1],
                        mid_x, center1[1],
                        False
                    ))
                    self.corridors.append(Corridor(
                        mid_x, center1[1],
                        mid_x, center2[1],
                        True
                    ))

    fn step(mut self) -&amp;gt; Bool:

        if self.stage == 0:
            if self.split_count &amp;gt;= self.max_splits:
                self.stage = 1
                self.current_idx = 0
                return True

            if self.current_idx &amp;lt; len(self.nodes):
                var node = self.nodes[self.current_idx]

                if node.split():
                    if node.split_vertical:
                        self.nodes.append(BSPNode(
                            node.x, node.y,
                            node.split_pos - node.x, node.height,
                            self.min_room_size
                        ))
                        self.nodes.append(BSPNode(
                            node.split_pos, node.y,
                            node.width - (node.split_pos - node.x), node.height,
                            self.min_room_size
                        ))
                    else:
                        self.nodes.append(BSPNode(
                            node.x, node.y,
                            node.width, node.split_pos - node.y,
                            self.min_room_size
                        ))
                        self.nodes.append(BSPNode(
                            node.x, node.split_pos,
                            node.width, node.height - (node.split_pos - node.y),
                            self.min_room_size
                        ))
                    self.split_count += 1
                    self.nodes[self.current_idx] = node
                self.current_idx += 1
                return True
            else:
                self.current_idx = 0
                return True

        elif self.stage == 1:
            if self.current_idx &amp;gt;= len(self.nodes):
                self.stage = 2
                self.current_idx = 0
                return True

            if self.nodes[self.current_idx].create_room():
                print(&quot;Created room in node&quot;, self.current_idx)

            self.current_idx += 1
            return True

        elif self.stage == 2:
            if self.current_idx == 0:
                self.connect_rooms()
                self.current_idx += 1
                return True
            return False

        return False
fn run_display(width: Int = 800, height: Int = 600, min_room_size: Int = 50, split_count: Int = 5) raises:
    pygame = Python.import_module(&quot;pygame&quot;)
    pygame.init()

    var window = pygame.display.set_mode((width, height))
    pygame.display.set_caption(&quot;Compy BSP&quot;)

    var generator = DungeonGenerator(width, height, min_room_size, split_count)
    var running = True

    while running:
        var event = pygame.event.poll()
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE or event.key == pygame.K_q:
                running = False
            elif event.key == pygame.K_SPACE:
                if not generator.step():
                    generator = DungeonGenerator(width, height, min_room_size, split_count)

        window.fill((0, 0, 0))
        for i in range(len(generator.corridors)):
                var corridor = generator.corridors[i]
                var start_x = corridor.start_x
                var start_y = corridor.start_y
                var end_x = corridor.end_x
                var end_y = corridor.end_y
                pygame.draw.line(
                    window,
                    (0, 0, 255),
                    (start_x, start_y),
                    (end_x, end_y),
                    3
                )
        for i in range(len(generator.nodes)):
            var node = generator.nodes[i]
            pygame.draw.rect(
                window,
                (255, 255, 255),
                (node.x, node.y, node.width, node.height),
                1
            )

            if node.is_split:
                if node.split_vertical:
                    pygame.draw.line(
                        window,
                        (255, 0, 0),
                        (node.split_pos, node.y),
                        (node.split_pos, node.y + node.height),
                        2
                    )
                else:
                    pygame.draw.line(
                        window,
                        (255, 0, 0),
                        (node.x, node.split_pos),
                        (node.x + node.width, node.split_pos),
                        2
                    )

            if node.has_room and node.is_leaf:
                pygame.draw.rect(
                    window,
                    (0, 255, 0),
                    (node.room_x, node.room_y, node.room_width, node.room_height),
                    0
                )




        var font = pygame.font.Font(None, 36)
        var stage_text = String(&quot;&quot;)

        if generator.stage == 0: stage_text = &quot;Phase 1: Splitting (&quot; + str(generator.split_count) + &quot;/&quot; + str(generator.max_splits) + &quot;)&quot;
        elif generator.stage == 1: stage_text = &quot;Phase 2: Creating Rooms&quot;
        else: stage_text = &quot;Phase 3: Connecting Rooms&quot;

        var text = font.render(stage_text, True, (255, 255, 0))
        var text_rect = text.get_rect()
        text_rect.midtop = (width // 2, 10)
        window.blit(text, text_rect)

        var help_text = &quot;Space: Next Step | ESC/Q: Quit&quot;
        var help_surface = font.render(help_text, True, (128, 128, 128))
        var help_rect = help_surface.get_rect()
        help_rect.midbottom = (width // 2, height - 10)
        window.blit(help_surface, help_rect)

        pygame.display.flip()
        time.sleep(0.1)

    pygame.quit()

fn main() raises:
    run_display(800, 600, 50, 8)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://compy07.github.io/Blog/posts/algorithms/bsp/unity/&quot;&gt;Unity로 확장해서 해커톤에 활용하기!&lt;/a&gt;&lt;/h3&gt;
&lt;hr /&gt;
&lt;h3&gt;Reference&lt;/h3&gt;
&lt;h4&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Binary_space_partitioning&quot;&gt;wiki&lt;/a&gt;&lt;/h4&gt;
&lt;h4&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9D%B4%EC%A7%84_%EA%B3%B5%EA%B0%84_%EB%B6%84%ED%95%A0%EB%B2%95&quot;&gt;wiki 2&lt;/a&gt;&lt;/h4&gt;
</content:encoded></item><item><title>[Algorithm] BSP 알고리즘(맵 생성) 유니티로 구현하기!(2D)</title><link>https://compy07.github.io/Blog/posts/algorithms/bsp/unity/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/bsp/unity/</guid><description>BSP algorithm with Unity, 자연스러운 스프라이트 배치로 멋있게 만들어보자!</description><pubDate>Sun, 16 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::warning[여기 잠깐!!]&lt;/p&gt;
&lt;p&gt;기본적인 알고리즘을 이해하는데 있어서 &lt;a href=&quot;https://compy07.github.io/Blog/posts/algorithms/bsp/theory/&quot;&gt;여기!&lt;/a&gt;를 참고해주세요!!
:::&lt;/p&gt;
&lt;h1&gt;BSP 알고리즘 구현으로 해커톤 날먹이 된다고!?!&lt;/h1&gt;
&lt;p&gt;사실 이건 장난이고, 그냥 있어보이게 구현할 수 있도록 구조를 다 만들어둬서 그냥 가져다 쓰시면 됩니다.&lt;/p&gt;
&lt;p&gt;물론 이 코드가 체계적으로 작성된 코드는 아니지만! 그래도 쓸만한 애들이라고 생각합니다(최적화는 안되어있는 편 ㅎㅎ)&lt;/p&gt;
&lt;p&gt;일단 활용하기 편하도록 Scriptable Object로 다 설정하도록 만들어 뒀습니다.&lt;/p&gt;
&lt;p&gt;:::warning[Scriptable Object 주의]&lt;/p&gt;
&lt;p&gt;이 Scriptable Object를 인게임 상에서 값을 수정하게 되면, editor 환경에서는 작동을 하나.. 나중에 build 이후로 가게되면
유니티에서 이제 최적화 과정을 거치면서 필요없는 prefabs 등 다른 리소스들을 다 없애는데 이때 &lt;strong&gt;Scriptable Object의 값이 변경되면 터집니다!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;조심하십쇼!
:::&lt;/p&gt;
&lt;h2&gt;기본적인 세팅&lt;/h2&gt;
&lt;p&gt;일단 저는 부가적으로 Layer를 사용합니다. 참고하실 분은 참고해주세용&lt;/p&gt;
&lt;h3&gt;1. 벽 세팅&lt;/h3&gt;
&lt;p&gt;기본적인 벽을 담을 스프라이트(2D Rect 정도?)를 기반으로 방향, 모서리 등의 위치에 배치할 스프라이트들을 넣으면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[CreateAssetMenu(fileName = &quot;WallTileSettings&quot;, menuName = &quot;Compy/Wall Tile Settings&quot;)]
public class WallTileSettings : ScriptableObject
{
    
    [System.Serializable]
    public class WallSprites
    {
        [Header(&quot;기본 벽 스프라이트&quot;)]
        [Tooltip(&quot;주변에 아무 연결이 없는 기본적인 벽 스프라이트&quot;)]
        public Sprite defaultWall;

        [Header(&quot;벽 테두리&quot;)]
        [Tooltip(&quot;아래쪽으로 연결된 상단 벽면&quot;)] // │ 의 윗부분
        public Sprite wallTop;
        [Tooltip(&quot;위쪽으로 연결된 하단 벽면&quot;)] // │ 의 아랫부분
        public Sprite wallBottom;
        [Tooltip(&quot;오른쪽으로 연결된 왼쪽 벽면&quot;)] // ─ 의 왼쪽
        public Sprite wallLeft;
        [Tooltip(&quot;왼쪽으로 연결된 오른쪽 벽면&quot;)] // ─ 의 오른쪽
        public Sprite wallRight;

        [Header(&quot;벽 모서리 - 외부&quot;)]
        [Tooltip(&quot;오른쪽과 아래로 연결된 외부 모서리&quot;)] // ┌ 모양
        public Sprite cornerOuterTopLeft;
        [Tooltip(&quot;왼쪽과 아래로 연결된 외부 모서리&quot;)] // ┐ 모양
        public Sprite cornerOuterTopRight;
        [Tooltip(&quot;오른쪽과 위로 연결된 외부 모서리&quot;)] // └ 모양
        public Sprite cornerOuterBottomLeft;
        [Tooltip(&quot;왼쪽과 위로 연결된 외부 모서리&quot;)] // ┘ 모양
        public Sprite cornerOuterBottomRight;

        [Header(&quot;벽 모서리 - 내부&quot;)]
        [Tooltip(&quot;위쪽과 왼쪽이 벽인 내부 모서리&quot;)] // ┘ 모양이지만 안쪽으로
        public Sprite cornerInnerTopLeft;
        [Tooltip(&quot;위쪽과 오른쪽이 벽인 내부 모서리&quot;)] // └ 모양이지만 안쪽으로
        public Sprite cornerInnerTopRight;
        [Tooltip(&quot;아래쪽과 왼쪽이 벽인 내부 모서리&quot;)] // ┐ 모양이지만 안쪽으로
        public Sprite cornerInnerBottomLeft;
        [Tooltip(&quot;아래쪽과 오른쪽이 벽인 내부 모서리&quot;)] // ┌ 모양이지만 안쪽으로
        public Sprite cornerInnerBottomRight;

        [Header(&quot;단독 벽&quot;)]
        [Tooltip(&quot;주변에 어떤 연결도 없는 단독으로 서있는 벽&quot;)]
        public Sprite singleWall;

        [Header(&quot;끝부분&quot;)]
        [Tooltip(&quot;위쪽 방향으로 돌출된 벽의 끝부분 (아래쪽만 연결)&quot;)] // ╽ 모양
        public Sprite endTop;
        [Tooltip(&quot;아래쪽 방향으로 돌출된 벽의 끝부분 (위쪽만 연결)&quot;)] // ╿ 모양
        public Sprite endBottom;
        [Tooltip(&quot;왼쪽 방향으로 돌출된 벽의 끝부분 (오른쪽만 연결)&quot;)] // ╾ 모양
        public Sprite endLeft;
        [Tooltip(&quot;오른쪽 방향으로 돌출된 벽의 끝부분 (왼쪽만 연결)&quot;)] // ╼ 모양
        public Sprite endRight;
    }

    [Header(&quot;벽 스프라이트 설정&quot;)]
    public WallSprites sprites;

    [Header(&quot;벽 렌더링 설정&quot;)]
    public string sortingLayerName = &quot;Wall&quot;;
    public int sortingOrder = 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Bsp Generation 세팅&lt;/h3&gt;
&lt;p&gt;bsp로 맵 생성을 할 때 사용될 변수들&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = &quot;MapSettings&quot;, menuName = &quot;Compy/Generator Settings&quot;)]
public class GeneratorSetting : ScriptableObject
{

    [Header(&quot;Map Size&quot;)]
    [Tooltip(&quot;전체 너비&quot;)]
    public int mapWidth = 100;
    
    [Tooltip(&quot;전체 높이&quot;)]
    public int mapHeight = 100;

    [Header(&quot;Room Generation&quot;)]
    [Tooltip(&quot;방의 최소 크기&quot;)]
    [Min(5)]
    public int minRoomSize = 10;
    
    [Tooltip(&quot;BSP 분할 최대 횟수&quot;)]
    [Range(1, 10)]
    public int maxIterations = 4;

    [Header(&quot;Room Constraints&quot;)]
    [Tooltip(&quot;방 사이의 최소 거리&quot;)]
    [Min(1)]
    public int minRoomDistance = 2;
    
    [Tooltip(&quot;방의 최대 크기 (전체 공간 대비 비율)&quot;)]
    [Range(0.3f, 0.8f)]
    public float maxRoomSizeRatio = 0.6f;

    [Header(&quot;Tile Settings&quot;)]
    [Tooltip(&quot;벽 타일 설정&quot;)]
    public WallTileSettings wallTileSettings;

    [Tooltip(&quot;기본 타일 프리팹 (스프라이트 렌더러와 콜라이더가 있는 빈 게임오브젝트)&quot;)]
    public GameObject tilePrefab;

    [Header(&quot;Prefabs&quot;)]
    [Tooltip(&quot;바닥 타일 프리팹&quot;)]
    public GameObject floorPrefab;

    [Header(&quot;Layer Settings&quot;)]
    [Tooltip(&quot;벽 레이어의 이름&quot;)]
    public string wallLayerName = &quot;Wall&quot;;

    [Header(&quot;Advanced Settings&quot;)]
    [Tooltip(&quot;통로 너비&quot;)]
    [Range(1, 5)]
    public int corridorWidth = 3;
    
    [Tooltip(&quot;방 생성 시도 최대 횟수&quot;)]
    [Min(1)]
    public int maxRoomPlacementAttempts = 10;

    [Tooltip(&quot;추가 통로 생성 확률 (0-1)&quot;)]
    [Range(0f, 1f)]
    public float extraCorridorChance = 0.2f;
    
    [Header(&quot;Structures&quot;)]
    [Tooltip(&quot;맵에 배치할 구조물들의 정의&quot;)]
    public List&amp;lt;CompyStructure&amp;gt; structures = new List&amp;lt;CompyStructure&amp;gt;();

    [Tooltip(&quot;구조물 배치 최대 시도 횟수&quot;)]
    [Min(1)]
    public int maxPlacementAttempts = 100;

    
    
    
    public void ValidateSettings()
    {
        mapWidth = Mathf.Max(mapWidth, minRoomSize * 2);
        mapHeight = Mathf.Max(mapHeight, minRoomSize * 2);

        minRoomSize = Mathf.Min(minRoomSize, Mathf.Min(mapWidth, mapHeight) / 2);
        
        corridorWidth = Mathf.Min(corridorWidth, minRoomSize - 2);
        minRoomDistance = Mathf.Max(minRoomDistance, corridorWidth);

        ValidateStructures();
    }


    private void ValidateStructures()
    {
        if (structures == null) return;

        foreach (var structure in structures)
        {
            if (structure == null) continue;
            structure.width = Mathf.Min(structure.width, mapWidth - 4);
            structure.height = Mathf.Min(structure.height, mapHeight - 4);

            structure.minCount = Mathf.Min(structure.minCount, structure.maxCount);

            if (structure.padding &amp;gt; 0) 
                structure.padding = Mathf.Min(structure.padding,
                    Mathf.Min(structure.width, structure.height) / 2);
            
        }
    }
    
    public GeneratorSetting Clone()
    {
        var clone = CreateInstance&amp;lt;GeneratorSetting&amp;gt;();        
        clone.mapWidth = this.mapWidth;
        clone.mapHeight = this.mapHeight;
        clone.minRoomSize = this.minRoomSize;
        clone.maxIterations = this.maxIterations;
        clone.minRoomDistance = this.minRoomDistance;
        clone.maxRoomSizeRatio = this.maxRoomSizeRatio;
        clone.wallTileSettings = this.wallTileSettings;
        clone.tilePrefab = this.tilePrefab;
        clone.floorPrefab = this.floorPrefab;
        clone.wallLayerName = this.wallLayerName;
        clone.corridorWidth = this.corridorWidth;
        clone.maxRoomPlacementAttempts = this.maxRoomPlacementAttempts;
        clone.extraCorridorChance = this.extraCorridorChance;

        clone.structures = new List&amp;lt;CompyStructure&amp;gt;(this.structures);
        clone.maxPlacementAttempts = this.maxPlacementAttempts;

        return clone;
    }

#if UNITY_EDITOR
    // 내가 한번 당하지 두번은 안 당할거임 ㄹㅇ

    private void OnValidate()
    {
        ValidateSettings();
    }
#endif
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;BSP Algorithm&lt;/h2&gt;
&lt;h3&gt;BSP Room&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;public class BSPRoom
{
    public int x, y, width, height;

    public BSPRoom(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;BSP Generator&lt;/h3&gt;
&lt;p&gt;기본적인 생성만 가볍게 만들어본 버전입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class BSPGenerator : MonoBehaviour{
    private enum WallType {
        Single,
        Top,
        Bottom,
        Left,
        Right,
        CornerOuterTopLeft,
        CornerOuterTopRight,
        CornerOuterBottomLeft,
        CornerOuterBottomRight,
        CornerInnerTopLeft,
        CornerInnerTopRight,
        CornerInnerBottomLeft,
        CornerInnerBottomRight,
        EndTop,
        EndBottom,
        EndLeft,
        EndRight,
        Default
    }
    private int mapWidth = 100;
    private int mapHeight = 100;
    private int minRoomSize = 10;
    private int maxIterations = 4; 
    
    private string wallLayerName = &quot;Wall&quot;;
    
    private GameObject wallPrefab; 
    private GameObject floorPrefab; 

    private List&amp;lt;BSPRoom&amp;gt; rooms = new List&amp;lt;BSPRoom&amp;gt;();
    private int[,] map;
    private int wallLayerId;

    private GeneratorSetting setting;
    
    private const bool debug = false;

    private bool generateDone;
    
    public void GeneratorInit(GeneratorSetting setting)
    {
        generateDone = false;
        InitWithSettings(setting);
        
        wallLayerId = LayerMask.NameToLayer(wallLayerName);
        
        if (wallLayerId == -1) {
            Debug.LogError($&quot;Layer &apos;{wallLayerName}&apos; not found! 이거 추가좀 이거 왜 안고쳐짐?&quot;);
            wallLayerId = 0;
        }

        this.setting = setting;
    }

    private void InitWithSettings(GeneratorSetting setting) {
        mapWidth = setting.mapWidth;
        mapHeight = setting.mapHeight;
        minRoomSize = setting.minRoomSize;
        maxIterations = setting.maxIterations;
        wallLayerName = setting.wallLayerName;
        floorPrefab = setting.floorPrefab;
    }
    
    public void GenerateMap() {
        map = new int[mapWidth, mapHeight];
        for (int x = 0; x &amp;lt; mapWidth; x++) {
            for (int y = 0; y &amp;lt; mapHeight; y++)
                map[x, y] = 1;
        }

        SplitSpace(0, 0, mapWidth, mapHeight, 0);
        
        ConnectRooms();
        
        InstantiateTiles();
        
        generateDone = true;
    }



    private void SplitSpace(int x, int y, int width, int height, int iteration)
    {
        if (iteration &amp;gt;= maxIterations || width &amp;lt; minRoomSize * 2 || height &amp;lt; minRoomSize * 2) {
            int roomWidth = Random.Range(minRoomSize, width - 4);
            int roomHeight = Random.Range(minRoomSize, height - 4);
            int roomX = x + Random.Range(2, width - roomWidth - 2);
            int roomY = y + Random.Range(2, height - roomHeight - 2);

            CreateRoom(roomX, roomY, roomWidth, roomHeight);
            rooms.Add(new BSPRoom(roomX, roomY, roomWidth, roomHeight));
            return;
        }

        bool splitHorizontal = Random.Range(0f, 1f) &amp;gt; 0.5f;
        if (width &amp;gt; height &amp;amp;&amp;amp; width / height &amp;gt;= 1.25f) splitHorizontal = false;
        else if (height &amp;gt; width &amp;amp;&amp;amp; height / width &amp;gt;= 1.25f) splitHorizontal = true;

        if (splitHorizontal) {
            int splitAt = Random.Range(minRoomSize, height - minRoomSize);
            SplitSpace(x, y, width, splitAt, iteration + 1);
            SplitSpace(x, y + splitAt, width, height - splitAt, iteration + 1);
        }
        else {
            int splitAt = Random.Range(minRoomSize, width - minRoomSize);
            SplitSpace(x, y, splitAt, height, iteration + 1);
            SplitSpace(x + splitAt, y, width - splitAt, height, iteration + 1);
        }
    }

    private void CreateRoom(int x, int y, int width, int height)
    {
        for (int i = x; i &amp;lt; x + width; i++) {
            for (int j = y; j &amp;lt; y + height; j++)
                if (i &amp;gt;= 0 &amp;amp;&amp;amp; i &amp;lt; mapWidth &amp;amp;&amp;amp; j &amp;gt;= 0 &amp;amp;&amp;amp; j &amp;lt; mapHeight) map[i, j] = 0;
        }
    }

    private void ConnectRooms() {
        for (int i = 0; i &amp;lt; rooms.Count - 1; i++) {
            BSPRoom roomA = rooms[i];
            BSPRoom roomB = rooms[i + 1];

            Vector2Int pointA = new Vector2Int(
                roomA.x + roomA.width / 2,
                roomA.y + roomA.height / 2
            );
            Vector2Int pointB = new Vector2Int(
                roomB.x + roomB.width / 2,
                roomB.y + roomB.height / 2
            );
            
            CreateCorridor(pointA.x, pointA.y, pointB.x, pointA.y);
            CreateCorridor(pointB.x, pointA.y, pointB.x, pointB.y);
        }
    }

    private void CreateCorridor(int startX, int startY, int endX, int endY) {
        int x = startX;
        int y = startY;

        while (x != endX || y != endY)
        {
            if (x &amp;lt; endX) x++;
            else if (x &amp;gt; endX) x--;
            
            if (y &amp;lt; endY) y++;
            else if (y &amp;gt; endY) y--;

            if (x &amp;gt;= 0 &amp;amp;&amp;amp; x &amp;lt; mapWidth &amp;amp;&amp;amp; y &amp;gt;= 0 &amp;amp;&amp;amp; y &amp;lt; mapHeight) {
                map[x, y] = 0;
                
                for (int i = -1; i &amp;lt;= 1; i++)
                {
                    for (int j = -1; j &amp;lt;= 1; j++)
                    {
                        int nx = x + i;
                        int ny = y + j;
                        if (nx &amp;gt;= 0 &amp;amp;&amp;amp; nx &amp;lt; mapWidth &amp;amp;&amp;amp; ny &amp;gt;= 0 &amp;amp;&amp;amp; ny &amp;lt; mapHeight)
                            map[nx, ny] = 0;
                    }
                }
            }
        }
    }

    private void InstantiateTiles()
    {
        GameObject wallParent = new GameObject(&quot;Walls&quot;);
        GameObject floorParent = new GameObject(&quot;Floors&quot;);
        wallParent.transform.parent = transform;
        floorParent.transform.parent = transform;

        wallParent.layer = LayerMask.NameToLayer(&quot;Wall&quot;);
        floorParent.layer = LayerMask.NameToLayer(&quot;Floor&quot;);

        for (int x = 0; x &amp;lt; mapWidth; x++)
        {
            for (int y = 0; y &amp;lt; mapHeight; y++)
            {
                Vector3 position = new Vector3(x, y, 0);
                if (map[x, y] == 1)
                {
                    GameObject wall = Instantiate(setting.tilePrefab, position, Quaternion.identity, wallParent.transform);
                    wall.layer = LayerMask.NameToLayer(&quot;Wall&quot;);

                    SpriteRenderer wallRenderer = wall.GetComponent&amp;lt;SpriteRenderer&amp;gt;();
                    if (wallRenderer != null)
                    {
                        WallType wallType = DetermineWallType(x, y);
                        ApplyWallSprite(wallRenderer, wallType);
                        
                        wallRenderer.sortingLayerName = setting.wallTileSettings.sortingLayerName;
                        wallRenderer.sortingOrder = setting.wallTileSettings.sortingOrder;
                    }

                    Collider2D collider = wall.GetComponent&amp;lt;Collider2D&amp;gt;();
                    if (collider != null)
                        collider.isTrigger = false;
                }
                else
                {
                    GameObject floor = Instantiate(setting.floorPrefab, position, Quaternion.identity, floorParent.transform);
                    floor.layer = LayerMask.NameToLayer(&quot;Floor&quot;);

                    SpriteRenderer floorRenderer = floor.GetComponent&amp;lt;SpriteRenderer&amp;gt;();
                    if (floorRenderer != null)
                    {
                        floorRenderer.sortingLayerName = &quot;Floor&quot;;
                        floorRenderer.sortingOrder = 0;
                    }
                }
            }
        }
    }

    private WallType DetermineWallType(int x, int y)
    {
        bool up = IsWall(x, y + 1);
        bool down = IsWall(x, y - 1);
        bool left = IsWall(x - 1, y);
        bool right = IsWall(x + 1, y);
        
        // 대각선 방향도 확인
        bool upLeft = IsWall(x - 1, y + 1);
        bool upRight = IsWall(x + 1, y + 1);
        bool downLeft = IsWall(x - 1, y - 1);
        bool downRight = IsWall(x + 1, y - 1);

        // 단독 벽
        if (!up &amp;amp;&amp;amp; !down &amp;amp;&amp;amp; !left &amp;amp;&amp;amp; !right)
            return WallType.Single;

        // 내부 모서리 검사 (벽이 안쪽으로 꺾이는 경우)
        if (up &amp;amp;&amp;amp; right &amp;amp;&amp;amp; !upRight) return WallType.CornerInnerTopRight;
        if (up &amp;amp;&amp;amp; left &amp;amp;&amp;amp; !upLeft) return WallType.CornerInnerTopLeft;
        if (down &amp;amp;&amp;amp; right &amp;amp;&amp;amp; !downRight) return WallType.CornerInnerBottomRight;
        if (down &amp;amp;&amp;amp; left &amp;amp;&amp;amp; !downLeft) return WallType.CornerInnerBottomLeft;

        // 외부 모서리 검사 (벽이 바깥쪽으로 꺾이는 경우)
        if (!up &amp;amp;&amp;amp; !left &amp;amp;&amp;amp; right &amp;amp;&amp;amp; down) return WallType.CornerOuterTopLeft;
        if (!up &amp;amp;&amp;amp; !right &amp;amp;&amp;amp; left &amp;amp;&amp;amp; down) return WallType.CornerOuterTopRight;
        if (!down &amp;amp;&amp;amp; !left &amp;amp;&amp;amp; right &amp;amp;&amp;amp; up) return WallType.CornerOuterBottomLeft;
        if (!down &amp;amp;&amp;amp; !right &amp;amp;&amp;amp; left &amp;amp;&amp;amp; up) return WallType.CornerOuterBottomRight;

        // 끝부분 검사
        if (up &amp;amp;&amp;amp; !down &amp;amp;&amp;amp; !left &amp;amp;&amp;amp; !right) return WallType.EndBottom;
        if (!up &amp;amp;&amp;amp; down &amp;amp;&amp;amp; !left &amp;amp;&amp;amp; !right) return WallType.EndTop;
        if (!up &amp;amp;&amp;amp; !down &amp;amp;&amp;amp; left &amp;amp;&amp;amp; !right) return WallType.EndRight;
        if (!up &amp;amp;&amp;amp; !down &amp;amp;&amp;amp; !left &amp;amp;&amp;amp; right) return WallType.EndLeft;

        // 테두리 검사
        if (!up &amp;amp;&amp;amp; down) return WallType.Top;
        if (up &amp;amp;&amp;amp; !down) return WallType.Bottom;
        if (!left &amp;amp;&amp;amp; right) return WallType.Left;
        if (left &amp;amp;&amp;amp; !right) return WallType.Right;

        return WallType.Default;
    }

    private void ApplyWallSprite(SpriteRenderer renderer, WallType type)
    {
        var sprites = setting.wallTileSettings.sprites;
        
        switch (type)
        {
            case WallType.Single:
                renderer.sprite = sprites.singleWall;
                break;
            case WallType.Top:
                renderer.sprite = sprites.wallTop;
                break;
            case WallType.Bottom:
                renderer.sprite = sprites.wallBottom;
                break;
            case WallType.Left:
                renderer.sprite = sprites.wallLeft;
                break;
            case WallType.Right:
                renderer.sprite = sprites.wallRight;
                break;
            case WallType.CornerOuterTopLeft:
                renderer.sprite = sprites.cornerOuterTopLeft;
                break;
            case WallType.CornerOuterTopRight:
                renderer.sprite = sprites.cornerOuterTopRight;
                break;
            case WallType.CornerOuterBottomLeft:
                renderer.sprite = sprites.cornerOuterBottomLeft;
                break;
            case WallType.CornerOuterBottomRight:
                renderer.sprite = sprites.cornerOuterBottomRight;
                break;
            case WallType.CornerInnerTopLeft:
                renderer.sprite = sprites.cornerInnerTopLeft;
                break;
            case WallType.CornerInnerTopRight:
                renderer.sprite = sprites.cornerInnerTopRight;
                break;
            case WallType.CornerInnerBottomLeft:
                renderer.sprite = sprites.cornerInnerBottomLeft;
                break;
            case WallType.CornerInnerBottomRight:
                renderer.sprite = sprites.cornerInnerBottomRight;
                break;
            case WallType.EndTop:
                renderer.sprite = sprites.endTop;
                break;
            case WallType.EndBottom:
                renderer.sprite = sprites.endBottom;
                break;
            case WallType.EndLeft:
                renderer.sprite = sprites.endLeft;
                break;
            case WallType.EndRight:
                renderer.sprite = sprites.endRight;
                break;
            default:
                renderer.sprite = sprites.defaultWall;
                break;
        }
        
    }

    private bool IsWall(int x, int y)
    {
        if (x &amp;lt; 0 || x &amp;gt;= mapWidth || y &amp;lt; 0 || y &amp;gt;= mapHeight)
            return false;
        return map[x, y] == 1;
    }

   

    public bool isDone()
    {
        return generateDone;
    }
    
    
    public BSPRoom GetRoomFromPosition(Vector2 position)
    {
        if (!generateDone)
        {
            Debug.LogWarning(&quot;Map generation is not complete. Cannot find room.&quot;);
            return null;
        }

        return rooms.FirstOrDefault(room =&amp;gt;
            position.x &amp;gt;= room.x &amp;amp;&amp;amp; position.x &amp;lt; room.x + room.width &amp;amp;&amp;amp;
            position.y &amp;gt;= room.y &amp;amp;&amp;amp; position.y &amp;lt; room.y + room.height);
    }


    public BSPRoom GetRoomFromTransform(Transform transform)
    {
        if (transform == null) return null;
        return GetRoomFromPosition(new Vector2(transform.position.x, transform.position.y));
    }
    
    
 

    


    private void OnDrawGizmos()
    {
        if (!debug) return;
        if (map == null) return;

        for (int x = 0; x &amp;lt; mapWidth; x++) {
            for (int y = 0; y &amp;lt; mapHeight; y++) {
                if (map[x, y] == 1)
                    Gizmos.color = Color.black;
                else
                    Gizmos.color = Color.white;
                Vector3 pos = new Vector3(x, y, 0);
                Gizmos.DrawCube(pos, Vector3.one);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
구현 영상&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/TNEAc0btgQs?si=UIujf8FiUD2AjY6Y&quot; title=&quot;Unity BSP&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;여기까지 기본적인 맵 생성이였구. 다음 포스트에서 아이템 배치와 다른 지형 배치 등을 다루는 코드를 이어서 작성하도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;수고하셨습니다.&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://compy07.github.io/Blog/posts/algorithms/bsp/theory/&quot;&gt;Compy Blog&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>[Algorithm] 벨만-포드 알고리즘(Bellman-Ford Algorithm)</title><link>https://compy07.github.io/Blog/posts/algorithms/bellman/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/bellman/</guid><description>벨만 포드 알고리즘에 대해서 알아보자</description><pubDate>Wed, 12 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;보통 많이 사용하는 그래프 탐색 알고리즘은 &lt;a href=&quot;https://compy07.github.io/Blog/posts/algorithms/dijkstrapathfinding/&quot;&gt;다익스트라&lt;/a&gt;가 있죠?&lt;/p&gt;
&lt;p&gt;그런데 다익스트라는 음수 가중치가 있을 때 사용하지 못합니다... 왜냐! 보통 작은 값들로 업데이트하는데 음수가 들어가버리면 무한 사이클이 발생하니까요..
이런 그리디한 특성 때문에 처리가 안되는데 벨만-포드는 그걸 처리할 수 있다니까요?!&lt;/p&gt;
&lt;p&gt;그래서 벨만-포드가 뭐냐!&lt;/p&gt;
&lt;h2&gt;벨만-포드 알고리즘&lt;/h2&gt;
&lt;p&gt;가중치가 있는 그래프에서 한 정점으로부터 다른 모든 정점까지의 최단 경로를 찾는 알고리즘!&lt;/p&gt;
&lt;p&gt;기본적인 아이디어&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;출발 정점의 거리를 0으로 초기화&lt;/li&gt;
&lt;li&gt;다른 모든 정점까지의 거리를 무한대로 초기화&lt;/li&gt;
&lt;li&gt;모든 간선에 대해 완화(relaxation) 작업을 수행하는 과정을 (정점 수 - 1)번 반복&lt;/li&gt;
&lt;li&gt;음수 사이클 검사를 위해 한 번 더 모든 간선을 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;완화(Relaxation) 연산&lt;/h3&gt;
&lt;p&gt;완화 연산은 벨만-포드 알고리즘의 핵심 작업입니다. 정점 u에서 정점 v로 가는 간선 (u, v)의 가중치가 w일 때...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if distance[u] + w &amp;lt; distance[v] then
distance[v] = distance[u] + w
predecessor[v] = u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 연산은 &quot;정점 u를 경유하여 정점 v로 가는 경로가 현재까지 알려진 v까지의 최단 경로보다 짧다면, 최단 경로를 갱신한다&quot;라는 의미!&lt;/p&gt;
&lt;p&gt;그럼 중요한 것들을 알아보죠!&lt;/p&gt;
&lt;h3&gt;시간 복잡도 및 공간 복잡도&lt;/h3&gt;
&lt;h4&gt;시간 복잡도&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;최악의 경우: O(VE)&lt;/li&gt;
&lt;li&gt;초기화: O(V)&lt;/li&gt;
&lt;li&gt;완화 작업: O(VE) (V-1번 반복하면서 각 반복마다 E개의 간선 검사)&lt;/li&gt;
&lt;li&gt;음수 사이클 검사: O(E)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;공간 복잡도&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;O(V + E)
&lt;ul&gt;
&lt;li&gt;거리 배열과 선행자 배열: O(V)&lt;/li&gt;
&lt;li&gt;그래프 표현: O(E)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;비교해볼까? with Dijkstra&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;특성&lt;/th&gt;
&lt;th&gt;벨만-포드&lt;/th&gt;
&lt;th&gt;다익스트라&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;시간 복잡도&lt;/td&gt;
&lt;td&gt;O(VE)&lt;/td&gt;
&lt;td&gt;O(E log V) (priority_queue 사용 시)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;음수 가중치&lt;/td&gt;
&lt;td&gt;처리 가능&lt;/td&gt;
&lt;td&gt;처리 불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;음수 사이클&lt;/td&gt;
&lt;td&gt;감지 가능(처리 가능)&lt;/td&gt;
&lt;td&gt;무한 루프 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구현 난이도&lt;/td&gt;
&lt;td&gt;상대적으로 간단함&lt;/td&gt;
&lt;td&gt;상대적으로 쬐끔 복잡함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;적합한 상황&lt;/td&gt;
&lt;td&gt;음수 가중치가 있는 상황 또는&lt;/td&gt;
&lt;td&gt;모든 가중치가 양수인 상황&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;그러면 약간 효율이 안 좋아보이긴 합니다.. 그죠? 그래서 최적화 기법들이 여럿 있는데요!&lt;/p&gt;
&lt;p&gt;일단 제일 유명한 것들부터 알아봅시다.&lt;/p&gt;
&lt;h3&gt;1. SPFA(Shortest Path Faster Algorithm)&lt;/h3&gt;
&lt;p&gt;벨만-포드의 변형으로, 큐를 사용하여 완화될 필요가 있는 정점만 처리합니다.&lt;/p&gt;
&lt;p&gt;이거는 사실 찾아보면서 알게된 것인데.. 이게 따로 이름이 있었네요..?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from collections import deque

def spfa(graph, source):
    distance = {vertex: float(&apos;infinity&apos;) for vertex in graph}
    predecessor = {vertex: None for vertex in graph}
    distance[source] = 0
    
    queue = deque([source])
    in_queue = {vertex: False for vertex in graph}
    in_queue[source] = True
    
    while queue:
        u = queue.popleft()
        in_queue[u] = False
        
        for v, weight in graph[u]:
            if distance[u] + weight &amp;lt; distance[v]:
                distance[v] = distance[u] + weight
                predecessor[v] = u
                if not in_queue[v]:
                    queue.append(v)
                    in_queue[v] = True
    
    # 음수 사이클 검사 부분 생략...
    
    return distance, predecessor
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 조기 종료(Early Stop)&lt;/h3&gt;
&lt;p&gt;음.. 무슨 인공지능 학습도 아니고.. ㅋㅋ
그냥 이거는 코드 짜다보면 보이니까 굳이 따로 보진 않겠습니다. 아마 코드를 작성하다보면 당연히 짜게 되실테니..&lt;/p&gt;
&lt;p&gt;그러면 좀 질문해볼만한 것들을 해볼까요??&lt;/p&gt;
&lt;h2&gt;조금 더 깊게..&lt;/h2&gt;
&lt;h3&gt;Q1: 벨만-포드 알고리즘의 (정점 수 - 1)번 반복이 필요한 이유는 무엇인가요?&lt;/h3&gt;
&lt;h4&gt;A: 최악의 경우, 한 정점에서 다른 정점으로 가는 최단 경로는 최대 (정점 수 - 1)개의 간선을 포함할 수 있습니다. 각 반복마다 최소한 하나의 정점에 대한 최단 경로가 확정되므로, (정점 수 - 1)번의 반복 후에는 모든 최단 경로가 확정됩니다.&lt;/h4&gt;
&lt;h3&gt;Q2: SPFA는 항상 벨만-포드보다 빠른가요?&lt;/h3&gt;
&lt;h4&gt;A: 평균적으로는 SPFA가 더 빠르지만, 최악의 경우에는 여전히 O(VE)의 시간 복잡도를 가집니다. 특정한 그래프 구조에서는 오히려 더 느릴 수도 있습니다. 물론!? 대부분 사용하는게 좋겠죠..? 최적화를 위해서는 말이죠&lt;/h4&gt;
&lt;hr /&gt;
&lt;p&gt;연습 문제를 떨구고 가겠습니다.&lt;/p&gt;
&lt;h4&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1865&quot;&gt;웜홀&lt;/a&gt; 이거 한번 츄라이 츄라이&lt;/h4&gt;
</content:encoded></item><item><title>[Algorithm] 다익스트라 알고리즘</title><link>https://compy07.github.io/Blog/posts/algorithms/dijkstrapathfinding/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/dijkstrapathfinding/</guid><description>Dijkstra algorithm with python</description><pubDate>Wed, 03 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note[주의]
이 포스팅은 &quot;큐(Queue), BFS(너비우선탐색), 그래프(Graph theory), 힙(Heap)&quot;에 대한 이해가 필요합니다.
:::&lt;/p&gt;
&lt;p&gt;일단 가중치가 없는 그래프에서는 DFS, BFS는 갈 수 있는 노드를 탐색하는 과정을 하는데, 다익스트라 알고리즘은 가장 빨리 도착할 수 있을 때, 얼마나 빠르게? 어떻게(경로)를 알 수 있다.
이것을 네비게이션으로 에를 든다면, 네비게이션은 길을 찾을 때 여러가지 변수를 고려한다. 이동거리, 시간, 경제적 비용, 혼잡도 등이 될 수 있다.
이런 변수들을 고려한 알고리즘이 바로 다익스트라 알고리즘이다. 즉 다시말해서 &lt;strong&gt;Dijkstra 알고리즘은 BFS알고리즘을 확장하여 사용하는 최소비용 경로를 탐색하는 알고리즘이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다익스트라 알고리즘은 특정 노드에서 갈 수 있는 모든 노드들까지의 최단 경로를 구하는 알고리즘이다.
각 노드들은 고유한 비용(cost), 부모(parent)를 가지고 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img_1.png&quot; alt=&quot;graph&quot; /&gt;
&amp;lt;br&amp;gt; 이 그래프를 가지고 설명하도록 하겠다.
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;우선 간선(edge) 옆에 써져있는 숫자를 비용이라고 하겠다.&lt;/p&gt;
&lt;p&gt;만약 시작 노드를 이정훈이라고 했을때, &quot;이정훈&quot;을 기준으로 박찬규는 최소 비용으로 4만큼 떨어져있다.&amp;lt;br&amp;gt;
만약 시작 노드를 유한별이라고 했을때, &quot;유한별&quot;을 기준으로 박찬규는 최소 비용으로 2만큼 떨어져있다.&lt;/p&gt;
&lt;p&gt;즉 비용 및 부모는 시작 노드에 따라서 결과가 바뀔 수 있다나느 것을 의미한다.&lt;/p&gt;
&lt;p&gt;큐를 이용해 현재 노드에서 갈 수 있는 모든 노드들을 큐에 넣는다. 큐에서 다시 넣었던 노드를 빼내고 현재의 가중치를 저장한다.
만약 다시 같은 노드를 방문한다면 이전에 기록했던 가중치와 비교하여 작다면 갱신하고, 아니면 무시한다.&lt;/p&gt;
&lt;p&gt;이때 위의 방법을 쓴다면 노드 수가 굉장히 많은 경우에는 비효율적일 것이다.&lt;/p&gt;
&lt;p&gt;계속해서 더 이상 확인이 필요없는 이미 방문한 노드를 방문비교하는데, 노드 수가 많다면 그 비교 횟수가 많아질 것이다.
즉 모든 노드를 돌면서 먼저 가중치 상관없이 노드를 방문하고 다시 비교하고 이동할 때마다 비교를 하기 때문에 효율이 너무 떨어진다.&lt;/p&gt;
&lt;p&gt;이런 문제를 해결하기 위해서 우선순위 큐를 사용하여 최적화한다. 이는 힙큐를 통해서 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;img_1.png&quot; alt=&quot;graph&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;아래의 표는 행은 순서, 열은 순서대로 Queue의 상태, 현재 Cost의 상태, Parents의 상태를 각각 나타낸다.&lt;/p&gt;
&lt;p&gt;오른쪽의 그래프를 Heap 큐를 이용해 다익스트라 알고리즘으로
유한별에서 이정훈을 탐색할 때의 과정이다.&lt;/p&gt;
&lt;p&gt;과정에서 중간에 끊기는데 이유는 목표 노드에 도착했기 때문에 다음 노드를 탐색할 필요 없이 코드가 끝난 상황이다.&lt;/p&gt;
&lt;p&gt;:::note[정의]&lt;/p&gt;
&lt;p&gt;inf = float(&apos;inf&apos;) # infinity&lt;/p&gt;
&lt;p&gt;Cost: 현재 자신의 최소 방문 비용을 저장하고 있는 리스트(또는 배열)
Paraents: 자신을 최소 비용으로 방문한 노드
:::&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Queue(Heap)&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Parents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;(0, 유한별)&lt;/td&gt;
&lt;td&gt;[None, inf, inf, inf, inf]&lt;/td&gt;
&lt;td&gt;[None, None, None, None, None]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;(1, 이재영), (2, 박찬규)&lt;/td&gt;
&lt;td&gt;[None, inf, inf, inf, inf]&lt;/td&gt;
&lt;td&gt;[None, None, None, None, None]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;(2, 박찬규), (4, 유채호)&lt;/td&gt;
&lt;td&gt;[None, inf, 1, inf, inf]&lt;/td&gt;
&lt;td&gt;[None, None, 유한별, None, None]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;(3, 유채호), (4, 유채호), (9, 이정훈)&lt;/td&gt;
&lt;td&gt;[None, 2, 1, inf, inf]&lt;/td&gt;
&lt;td&gt;[None, 유한별, 유한별, None, None]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;(4, 유채호), (6, 이정훈), (9, 이정훈)&lt;/td&gt;
&lt;td&gt;[None, 2, 1, inf, 3]&lt;/td&gt;
&lt;td&gt;[None, 유한별, 유한별, None, 박찬규]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;(6, 이정훈), (9, 이정훈)&lt;/td&gt;
&lt;td&gt;[None, 2, 1, inf, 3]&lt;/td&gt;
&lt;td&gt;[None, 유한별, 유한별, None, 박찬규]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;(9, 이정훈)&lt;/td&gt;
&lt;td&gt;[None, 2, 1, 6, 3]&lt;/td&gt;
&lt;td&gt;[None, 유한별, 유한별, 유채호, 박찬규]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그래서 결과는 유한별에서 이정훈으로 갈 때 최소비용은 6, 경로는: 유한별-&amp;gt;박찬규-&amp;gt;유채호-&amp;gt;이정훈이다.&lt;/p&gt;
&lt;p&gt;이제 요것을 코드로 다 구현을 해볼것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nodes = [&quot;유한별&quot;, &quot;박찬규&quot;, &quot;이정훈&quot;, &quot;이재영&quot;, &quot; 유채호&quot;]&amp;lt;br&amp;gt;
graph = [[(1, 2), (3, 1)], [(2, 7), (4, 1)], [], [(4, 3)], [(2, 3)]]  # node_idx, cost
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;저 그래프를 코드화 시켜보았다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이제 로직을 코드로 구현해보자&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def solution(start_node, target_node):
    global nodes, graph # 위에서 선언한 nodes와 graph를 가져옴

    inf = float(&apos;inf&apos;) #inf 무한 선언

    parents = [None, None, None, None, None] # 부모 리스트 선언
    cost = [inf, inf, inf, inf, inf] # 모든 노드들의 시작 노드를 기준으로 최적의 가중치를 저장할 리스트

    q = [(0, start_node)] # 큐 선언 및 초기화
    while q: # 큐가 비어있을 때까지 반복
        current_cost, node = heapq.heappop(q) # 현재 가중치, 현재 노드
        if current_cost &amp;gt; cost[node]: continue # 빙믄힐 노드의 등록된 최소 비용보다 크면 무시
        cost[node] = current_cost
        for idx, cost_ in graph[nodes.index(node)]: #현재 노드에서 갈 수 있는 노드와 그 가중치를 가져옴. (딕셔너리로 작성시 최적화 가능)
            if cost_ + current_cost &amp;lt; cost[idx]: # 현재 가중치와 움직일 노드의 가중치를 더한것이 가는 노드의 가중치보다 작으면 go
                parents[idx] = node # 움직일 노드의 부모를 현재 노드로 업데이트
                cost[idx] = cost_+current_cost
                heapq.heappush(q, (cost_+current_cost, nodes[idx])) # 힙큐 push
        if node == target_node: break # 목표 노드에 도착하면 break

    target = nodes.index(target_node)
    path = target_node+&quot; &quot; # path를 문자열로 이어 붙여 저장
    result_cost = cost[target]
    
    while parents[target]!=None: # 현재 노드의 부모가 None이 아닐때까지
        v = parents[target] != None #현재 노드의 부모가 None이 아닐때까지
        path += v+&quot; &quot; # 문자열 이어 붙이기
        target = nodes.index(v) # 현재 노드로 target을 저장
        
    return f&quot;최적의 경로: {path}, 도착 최소 비용은: {target_node} 입니다.&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;제가 다익스트라를 조금 시각적으로 볼 수 있도록 파이게임을 이용해서 시각화를 하였습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/3EHrev7RLGc?si=lSfNxgvhRVintCyG&quot; title=&quot;pathfinding&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;(0,0)이 시작점입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;초록색은 움직일 수 있는 땅&lt;/li&gt;
&lt;li&gt;하늘색은 탐색한 땅&lt;/li&gt;
&lt;li&gt;빨간색은 갈 수 없는 땅&lt;/li&gt;
&lt;li&gt;회색은 목표까지의 최적의 경로&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(시간도 된다면, A*도 해보겠습니다)&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 구현 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pygame, sys, os
# from tkinter import *

win_width, win_height = 400, 400  # 가로 세로 400 400
rows, cols = 12, 12  # 세로 가로 / 행과 열
win = pygame.display.set_mode((win_width, win_height))  # (width, height)
pygame.display.set_caption(&quot;(그리드 &amp;amp; 다익스트라) 알고리즘&quot;)
grid_width, grid_height = win_width // cols, win_height // rows

target_flag = False  # 목표물이 설정되지 않은 상태
WHITE = (255, 255, 255)  # r, g, b


class Box:
    # __init__
    # 필요한것
    # x, y 좌표(위치)
    # start, goal, wall인지
    def __init__(self, x, y, block_width, block_height):
        self.x, self.y = x, y
        self.color = (0, 255, 0)

        self.cost = float(&apos;inf&apos;)

        self.width, self.height = block_width, block_height
        self.start, self.goal, self.wall = False, False, False
        self.path = False
        self.visited = False
    def setcolor(self, color): self.color = color

    def draw(self, mywin):
        # color = (0, 255, 0)
        pos = (self.x, self.y, self.width - 2.5, self.height - 2.5)
        pygame.draw.rect(mywin, self.color, pos)
import heapq
def dij(g, finish_x, finish_y):
    result_path = []
    result_cost = float(&apos;inf&apos;)
    q = [(0, 0, 0, [], g[0][0])]
    dx, dy = [0, 0, -1, 1], [1, -1, 0, 0]
    xlen, ylen = len(g[0]), len(g)
    while q:
        cost, x, y, path, bx = heapq.heappop(q)
        if bx.wall == True or \
                result_cost &amp;lt;= cost or bx.cost &amp;lt;= cost: continue
        g[x][y].cost = cost
        bx.cost = cost
        if (x, y) == (finish_x, finish_y):
            if result_cost &amp;gt; cost:
                result_cost = cost
                result_path = path
        for i in range(4):
            nx, ny = x + dx[i], y + dy[i]
            if 0 &amp;lt;= nx &amp;lt; xlen and 0 &amp;lt;= ny &amp;lt; ylen:
                heapq.heappush(q, (cost + 1, nx, ny, path + [(x, y)], g[nx][ny]))
        bx.setcolor((0, 255, 255))
        for i in g:
            for j in i:
                j.draw(win)

        # win.fill(WHITE) #(r,g,b)
        # pygame.display.update(win)
        pygame.display.flip()
        pygame.time.delay(100)
    return result_path if result_cost != float(&apos;inf&apos;) else None

def makit():
    global target_flag
    # 그래픽 처리는 무조건 refresh 화면을 계속 찍는것
    g = []  # grids
    for i in range(cols):
        temp = []
        for j in range(rows):
            temp.append(Box(i * grid_width, j * grid_height, grid_width, grid_height))  # object 넣기
        g.append(temp)
    start_box = g[0][0]
    start_box.visited = True
    start_box.start = True
    start = False
    q = [start_box]
    target_pos = (0, 0)
    isRun = True
    while isRun:
        # 이벤트.. 이벤트 프로그래밍..  in cs
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()  # system
            elif event.type == pygame.MOUSEMOTION:
                position = pygame.mouse.get_pos()  # (x, y) 현재 마우스의 위치를 return함 tuple의 형태로
                x, y = position
                x, y = x // grid_width, y // grid_height
                if event.buttons[0]:  # 왼쪾 마우스가 클릭되면
                    print(f&quot;방해물 설정 중 : 좌표 x = {x}, y = {y}&quot;)
                    if (x, y) == target_pos or (x, y) == (0, 0): continue
                    g[x][y].setcolor((255, 0, 0))
                    g[x][y].wall = True
                # 1번은 마우스 휠
                elif event.buttons[2] and target_flag == False:  # 오른쪽 마우스 클릭되면
                    print(f&quot;목표물 설정 : 블럭 좌표 x = {x}, y = {y}&quot;)
                    g[x][y].setcolor((0, 0, 255))
                    g[x][y].goal = True
                    target_pos = (x, y)
                    target_flag = True
            elif event.type == pygame.KEYDOWN and target_flag == True:  # 키가 눌렸다면 실행
                print(&apos;알고리즘 시작&apos;)
                start = True
                g[0][0].start = True
                isRun = False
                nf = dij(g, target_pos[0], target_pos[1])
                if nf == None: return &quot;경로 없음&quot;
                else:
                    for (x,y) in nf:
                        g[x][y].setcolor((100,100,100))

        for i in g:
            for j in i:
                j.draw(win)

        # win.fill(WHITE) #(r,g,b)
        # pygame.display.update(win)
        pygame.display.flip()
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()  # system
        for i in g:
            for j in i:
                j.draw(win)
    return
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[Algorithm] Heuristic</title><link>https://compy07.github.io/Blog/posts/algorithms/heuristic/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/heuristic/</guid><description>알고리즘이 다가 아니라고?</description><pubDate>Fri, 10 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;보통 우리가 알고리즘을 짤 때, 하나를 당연하게 생각한다. 바로 &quot;언제나 정확히 최적화된 해를 반환한다&quot;라고 생각합니다.&lt;/p&gt;
&lt;p&gt;그러나 그렇지 않은 알고리즘들도 있는데, 그중 대표적인 것이 A*이다. 이 알고리즘은 &lt;strong&gt;휴리스틱&lt;/strong&gt;을 기반으로 작동합니다.&lt;/p&gt;
&lt;p&gt;휴리스틱은 도대체 뭐고... 알고리즘과의 차이는 뭘까? 이 포스팅에서 다뤄볼 것에요!
(약간 말투가 이 포스팅만 다를 수 있어요)&lt;/p&gt;
&lt;h1&gt;휴리스틱이란?&lt;/h1&gt;
&lt;p&gt;휴리스틱(Heuristic)은 최적의 해는 아니더라도, 어느정도 도움이 되는 값을 주는 친구라고 생각하면 됩니당
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;?? 알고리즘이랑 똑같은거 아님?&lt;/h2&gt;
&lt;p&gt;엄연한 차이가 있어요.
위에서도 강조하면서 왔듯이... 휴리스틱은 최적해를 보장하지 않는다..
하지만 알고리즘은 최적해를 보장하는 방법론입니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;한가지 예시를 들어보자.&amp;lt;br&amp;gt;
한번 축구로 예시를 들어볼게요
&amp;lt;br&amp;gt;
&lt;a href=&quot;https://youtu.be/KriBQVhsgZk?si=aj5ftQ4ezIyjPW8l&quot;&gt;이 영상&lt;/a&gt;을 보다가 문득 오... 휴리스틱이랑 알고리즘이랑 차이점을 다뤄봐도 재밌겠다. 라고 생각을하고 바로 포스팅하러 달려왔습니다.
또 제가 축구를 좋아하니.. 예시도 축구로 들어보도록 하죠.
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;일단 제가 수비라인에 있다고 생각을 해볼게요. 그러면 상대팀이 압박을 해옵니다. 이제 어디론가 패스를 해야겠죠?&lt;/p&gt;
&lt;p&gt;먼저 알고리즘적으로 생각을 해볼게요.
모든 팀원의 위치와 속도 벡터를 다 고려합니다. 여기서 속도 벡터는 방향까지 본다는 얘기구요.&amp;lt;br&amp;gt;
그리고 상태팀의 위치와 이동 경로도 알아야겠죠?&lt;br /&gt;
그리고 어느 발로 때렸을 때 성공 확률을 좀 계산을 해야겠구요.&lt;br /&gt;
이후에 최적의 패스 경로와 강도를 계산해서 패스를 합니다!&lt;/p&gt;
&lt;p&gt;이처럼 완벽한 최적해를 만들어내고 패스를 하죠!&lt;/p&gt;
&lt;p&gt;그럼 다음으로는 휴리스틱적으로 생각을 해볼게요?&lt;/p&gt;
&lt;p&gt;휴리스틱으로 한번 &quot;공간이 많이 보이는 곳을 패스한다&quot; 해보구요 안되면
&quot;압박이 적은 선수에게 패스한다&quot; 또 안되면 &quot;수비수 시선이 다른 곳으로 볼 때 패스한다&quot; 이런 느낌으로 진행이 되겠죠?&lt;/p&gt;
&lt;p&gt;이 예시가 적절하다고 생각은 들지는 않는데 한번 더 강조하지만&lt;/p&gt;
&lt;p&gt;최적해는 아니지만 이게 어느정도 도움이 된다. 최적해에 근접할 수 있도록 해준다 라고 생각하면 좋을 것 같아요.&lt;/p&gt;
&lt;p&gt;이제 영상을 보시면 나중에 인공지능으로 만들어진 클론들은 축구 경기에서 지게됩니다. 이처럼 좀 헤비한 연산이 많은 알고리즘 같은 경우에
real-time이 필요한 경우 알고리즘을 휴리스틱으로 대체하는 경우도 많다고 알고있는데요.&lt;/p&gt;
&lt;p&gt;선수들은 사실 많은 경험을 토대로 구축된 효율적인 휴리스틱을 뇌에 탑제하고 있다고 생각하시면 되는거죠.&lt;/p&gt;
&lt;p&gt;뭐 디게 단순하게 휴리스틱 써서 A* 구현한 포스팅이 있으니 이걸 한번 보고 오시면 코드로 구현하는데 어렵지 않으실 겁니다!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://compy07.github.io/Blog/posts/algorithms/astar/&quot;&gt;A* 알고리즘&lt;/a&gt; - COMPY07&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;여러분들도 뭔가 유튜브나 어떤 영상을 보고서 느껴본 차이점이나 흥미로운 것들이 있으셨나요?&amp;lt;br&amp;gt;
저는 또 재미있는걸 봐버렸습니다.&lt;/p&gt;
&lt;p&gt;다음 포스팅에 뵙도록 하죠!&lt;/p&gt;
</content:encoded></item><item><title>[Algorithm] 벡터(Vector)</title><link>https://compy07.github.io/Blog/posts/algorithms/math/vector/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/math/vector/</guid><description>컴퓨터 하신다구요? 코딩 좋아하시구요? 물리도 좋아하셔요? 근데 왜 벡터 안 하세요?</description><pubDate>Sun, 30 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;안녕하세요! 오늘은 수학의 핵심 개념인 &lt;strong&gt;벡터(Vector)&lt;/strong&gt; 에 대해 알아보려고 합니다.
벡터는 물리학, 컴퓨터 그래픽스, 공학에서 필수적! 그냥 컴퓨터 하시면 알아야될 놈이구요.
이번 포스팅에서는 벡터의 개념과 기본적으로 알아두면 많이 쓸 외적, 내적 등에 대해서 알아보겠습니다.&lt;/p&gt;
&lt;h2&gt;1. 벡터란 무엇인가?&lt;/h2&gt;
&lt;p&gt;벡터는 &lt;strong&gt;크기(magnitude)&lt;/strong&gt; 와 &lt;strong&gt;방향(direction)&lt;/strong&gt; 을 모두 가진 양을 의미합니다. 일상생활에서 예를 들자면&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;북쪽으로 5km&quot; (방향: 북쪽, 크기: 5km)&lt;/li&gt;
&lt;li&gt;&quot;시속 60km로 동쪽으로 이동&quot; (방향: 동쪽, 크기: 시속 60km)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이런 정보들이 모두 벡터로 표현될 수 있습니다.&lt;/p&gt;
&lt;p&gt;반면에 스칼라(scalar)는 크기만 있고 방향이 없는 양입니다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;온도: 25°C&lt;/li&gt;
&lt;li&gt;질량: 5kg&lt;/li&gt;
&lt;li&gt;시간: 3시간&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.1 벡터의 수학적 표현&lt;/h3&gt;
&lt;p&gt;수학적으로 벡터는 여러 방식으로 표현할 수 있습니다&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;화살표 표기법&lt;/strong&gt;: $$\vec{v}$$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;좌표 표기법&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;2차원: $$\mathbf{v} = (x, y)$$&lt;/li&gt;
&lt;li&gt;3차원: $$\mathbf{v} = (x, y, z)$$&lt;/li&gt;
&lt;li&gt;n차원: $$\mathbf{v} = (x_1, x_2, ..., x_n)$$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;1.2 벡터의 기본 연산&lt;/h3&gt;
&lt;h4&gt;벡터의 크기(norm)&lt;/h4&gt;
&lt;p&gt;벡터 $$\mathbf{v} = (x, y, z)$$의 &lt;strong&gt;크기&lt;/strong&gt;는 다음과 같이 계산합니다 &amp;lt;br/&amp;gt;
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$\huge |\mathbf{v}| = \sqrt{x^2 + y^2 + z^2}$$
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;벡터의 덧셈&lt;/h4&gt;
&lt;p&gt;두 벡터 $$\mathbf{a} = (a_1, a_2, a_3)$$와 $$\mathbf{b} = (b_1, b_2, b_3)$$의 덧셈 &amp;lt;br/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$\huge \mathbf{a} + \mathbf{b} = (a_1 + b_1, a_2 + b_2, a_3 + b_3)$$
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;addition.png&quot; alt=&quot;addition&quot; /&gt;
&amp;lt;br/&amp;gt; 출처 : &lt;a href=&quot;https://simagebank.net/wp/5216/?amp=1&quot;&gt;힘과 평형&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;덧셈의 기하학적 의미는 &lt;strong&gt;두 벡터를 이어붙이는 것&lt;/strong&gt; 입니다.
첫 번째 벡터의 끝에서 두 번째 벡터를 이어서 그리면, 시작점에서 최종 도착점까지의 벡터가 두 벡터의 합이 됩니다 .
이를 &quot;삼각형 법칙&quot; 또는 &quot;평행사변형 법칙&quot;이라고 합니다.
&amp;lt;br/&amp;gt; (이거는 아마 제가 배우는 당시 2015 교육과정에서는 물2 에서 배웠던 걸로 기억합니다.)&lt;/p&gt;
&lt;h4&gt;벡터의 뺄셈&lt;/h4&gt;
&lt;p&gt;두 벡터 $$\mathbf{a} = (a_1, a_2, a_3)$$와 $$\mathbf{b} = (b_1, b_2, b_3)$$의 뺄셈 &amp;lt;br/&amp;gt;
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$\huge \mathbf{a} - \mathbf{b} = (a_1 - b_1, a_2 - b_2, a_3 - b_3)$$
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;subtraction.png&quot; alt=&quot;subtraction&quot; /&gt;
&amp;lt;br/&amp;gt; 출처 : &lt;a href=&quot;https://blog.naver.com/nowedu1/220754491852&quot;&gt;벡터의 뺄셈&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;뺄셈의 기하학적 의미는 &lt;strong&gt;벡터 $$\mathbf{a}$$에서 벡터 $$\mathbf{b}$$로의 변화&lt;/strong&gt; 입니다.
두 점 사이의 방향과 거리를 나타내는 벡터를 구할 때 자주 사용되는 걸로 알고 있어요.&lt;/p&gt;
&lt;h4&gt;스칼라 곱셈&lt;/h4&gt;
&lt;p&gt;벡터 $$\mathbf{v}$$에 스칼라 $$k$$를 곱한 결과는&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$\huge k\mathbf{v} = (kx, ky, kz)$$
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;스칼라 곱셈은 벡터의 크기를 $$\mathbf{k}$$배 변화시킵니다.&lt;/p&gt;
&lt;h4&gt;단위 벡터(Unit Vector)&lt;/h4&gt;
&lt;p&gt;크기가 1인 벡터를 단위 벡터라고 합니다. 임의의 벡터 $$\mathbf{v}$$를 단위 벡터로 변환하는 과정을 &lt;strong&gt;정규화(normalization)&lt;/strong&gt; 라고 합니다. 아래와 같이 계산할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$\huge \hat{\mathbf{v}} = \frac{\mathbf{v}}{|\mathbf{v}|}$$
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;단위 벡터는 방향만 중요할 때 유용하게 사용됩니다. 이거는 아마 인공지능 아니면 게임 개발할 대 많이 보셨던 것들이라 익술할거라고 생각합니다.(물론 하셨던 분들에게만)&lt;/p&gt;
&lt;h2&gt;2. 벡터의 내적(Dot Product)&lt;/h2&gt;
&lt;p&gt;벡터의 내적은 두 벡터의 방향 관계와 크기를 모두 고려한 연산으로, 매우 중요한 개념입니다!&lt;/p&gt;
&lt;h3&gt;2.1 내적의 정의&lt;/h3&gt;
&lt;p&gt;두 벡터 $$\mathbf{a} = (a_1, a_2, a_3)$$와 $$\mathbf{b} = (b_1, b_2, b_3)$$의 내적은&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$\huge \mathbf{a} \cdot \mathbf{b} = a_1b_1 + a_2b_2 + a_3b_3$$
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;또는 크기와 각도를 이용해서..
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$\huge \mathbf{a} \cdot \mathbf{b} = |\mathbf{a}||\mathbf{b}|\cos\theta$$
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;여기서 $$\theta$$는 두 벡터 사이의 각도입니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 좀 더 깊게! &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;좀 더 공부좀 해오겠씁니다... ㅠ
&lt;a href=&quot;#&quot;&gt;//&lt;/a&gt;: # ()
&lt;a href=&quot;#&quot;&gt;//&lt;/a&gt;: # (### 2.2 내적의 기하학적 의미)&lt;/p&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;h2&gt;3. 벡터의 외적(Cross Product)&lt;/h2&gt;
&lt;p&gt;외적은 3차원 공간에서 두 벡터에 수직인 새로운 벡터를 생성하는 연산입니다.&lt;/p&gt;
&lt;h3&gt;3.1 외적의 정의&lt;/h3&gt;
&lt;p&gt;두 벡터 $$\mathbf{a} = (a_1, a_2, a_3)$$와 $$\mathbf{b} = (b_1, b_2, b_3)$$의 외적은&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$\huge \mathbf{a} \times \mathbf{b} = (a_2b_3 - a_3b_2, a_3b_1 - a_1b_3, a_1b_2 - a_2b_1)$$
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;행렬식으로도 계산할 수 있습니다&lt;/p&gt;
&lt;p&gt;$$
\huge \mathbf{a} \times \mathbf{b} =
\begin{vmatrix}
\mathbf{i} &amp;amp; \mathbf{j} &amp;amp; \mathbf{k} \
a_1 &amp;amp; a_2 &amp;amp; a_3 \
b_1 &amp;amp; b_2 &amp;amp; b_3
\end{vmatrix}
$$&lt;/p&gt;
&lt;p&gt;여기서 $$\mathbf{i}$$, $$\mathbf{j}$$, $$\mathbf{k}$$는 x, y, z축 방향의 단위 벡터입니다.&lt;/p&gt;
&lt;p&gt;또한 외적의 크기는 다음과 같이 계산할 수 있습니다
$$|\mathbf{a} \times \mathbf{b}| = |\mathbf{a}||\mathbf{b}|\sin\theta$$&lt;/p&gt;
&lt;p&gt;여기서 $$\theta$$는 두 벡터 사이의 각도입니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 좀 더 깊게! &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;좀 더 공부를...&lt;/p&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;지금 채워지지 않은 내용들은 추후에 업데이트 하겠습니다.&lt;/p&gt;
&lt;p&gt;사실 CCW 포스팅하려고 하다가 그것보다 벡터를 선행적으로 포스팅하는게 좋다고 생각되어 이렇게 오게되었는데.. 이거 하다보니 이제는 행렬도 해야겠다는 생각이 드네요!&lt;/p&gt;
&lt;p&gt;모두 수고하셨습니다!&lt;/p&gt;
</content:encoded></item><item><title>[Algorithm] 피보나치 수열의 재미있는 수학적 사실들 </title><link>https://compy07.github.io/Blog/posts/algorithms/math/docagnes_identity/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/math/docagnes_identity/</guid><description>피보나치 수열의 효율적인 처리..</description><pubDate>Sat, 15 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;제가 어제 풀었던 문제 중 &lt;a href=&quot;https://compy07.github.io/Blog/posts/boj/11444/&quot;&gt;피보나치 수 6&lt;/a&gt;라는 문제가 있었는데요. 제가 발견한 그 규칙이 사실 어떤 공식으로 이미 있더라구요!?!&lt;/p&gt;
&lt;p&gt;역시 있을 줄 알았습니다. 찾아보길 잘했어요.&lt;/p&gt;
&lt;p&gt;혹시나 더욱 많은 공식을 원하시다면 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98_%EC%88%98#:~:text=%EB%8B%A4%EC%9D%8C%EA%B3%BC%20%EA%B0%99%EC%9D%80%20%ED%95%AD%EB%93%B1%EC%8B%9D%EC%9D%B4%20%EC%84%B1%EB%A6%BD%ED%95%98%EB%A9%B0%2C%20%EC%9D%B4%EB%A5%BC%20%EB%8F%84%EA%B0%80%EB%89%B4,&apos;Ocagne&apos;s%20identity&quot;&gt;여기&lt;/a&gt;에서 확인 할 수 있어요!
디게 재밌으니 한번 다들 작성해보는 것도 괜찮을 것 같네요!&lt;/p&gt;
&lt;p&gt;일단 사실 도가뉴 항등식이라는 것을 배우려고 했으나 다른 것들도 재미있게 해볼 수 있을 것 같아서 한번 다 다뤄보려구요!&lt;/p&gt;
&lt;h1&gt;1. 카시니 항등식 (Cassini&apos;s Identity)&lt;/h1&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$
\LARGE F_{n+1}F_{n-1} - F_n^2 = (-1)^n
$$
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;증명&lt;/h2&gt;
&lt;h2&gt;기본 검증표&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;n&lt;/th&gt;
&lt;th&gt;계산식&lt;/th&gt;
&lt;th&gt;단계별 계산&lt;/th&gt;
&lt;th&gt;최종 결과&lt;/th&gt;
&lt;th&gt;$$ (-1)^n $$&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;F₄F₂ - F₃²&lt;/td&gt;
&lt;td&gt;3 × 1 - 2² = 3 - 4&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;(-1)³ = -1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;F₅F₃ - F₄²&lt;/td&gt;
&lt;td&gt;5 × 2 - 3² = 10 - 9&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;(-1)⁴ = 1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;확장된 검증표&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;n&lt;/th&gt;
&lt;th&gt;$$ F_{n-1} $$&lt;/th&gt;
&lt;th&gt;$$ F_n $$&lt;/th&gt;
&lt;th&gt;$$ F_{n+1} $$&lt;/th&gt;
&lt;th&gt;$$ F_{n+1}F_{n-1} - F_n² $$&lt;/th&gt;
&lt;th&gt;$$ (-1)^n $$&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 × 0 - 1² = -1&lt;/td&gt;
&lt;td&gt;(-1)¹ = -1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2 × 1 - 1² = 1&lt;/td&gt;
&lt;td&gt;(-1)² = 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3 × 1 - 2² = -1&lt;/td&gt;
&lt;td&gt;(-1)³ = -1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5 × 2 - 3² = 1&lt;/td&gt;
&lt;td&gt;(-1)⁴ = 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;8 × 3 - 5² = -1&lt;/td&gt;
&lt;td&gt;(-1)⁵ = -1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;$$
\LARGE
\begin{bmatrix}
F_{n+1} &amp;amp; F_n \
F_n &amp;amp; F_{n-1}
\end{bmatrix} =
\begin{bmatrix}
1 &amp;amp; 1 \
1 &amp;amp; 0
\end{bmatrix}^n
$$&lt;/p&gt;
&lt;p&gt;$$
\LARGE det\begin{bmatrix}
F_{n+1} &amp;amp; F_n \
F_n &amp;amp; F_{n-1}
\end{bmatrix} = F_{n+1}F_{n-1} - F_n^2 = (-1)^n
$$&lt;/p&gt;
&lt;p&gt;행렬식을 계산하는 것으로도 항등식을 증명할 수 있어요&lt;/p&gt;
&lt;p&gt;:::note[깨알 상식]&lt;/p&gt;
&lt;p&gt;&amp;lt;code&amp;gt;det&amp;lt;/code&amp;gt;은 2×2 행렬의 행렬식은 대각선 곱의 차이로 계산
:::&lt;/p&gt;
&lt;h1&gt;2. 도가뉴 항등식 (d&apos;Ocagne&apos;s Identity)&lt;/h1&gt;
&lt;p&gt;저는 일단 오늘 포스팅의 포커스를 도가뉴에 맞추기 때문에 더욱 좀 자세히 가져왔습니다.&lt;/p&gt;
&lt;p&gt;$$
\LARGE
F_{m+n} = F_m F_{n+1} + F_{m-1}F_n
$$&lt;/p&gt;
&lt;h2&gt;2.1. 행렬을 이용한 증명&lt;/h2&gt;
&lt;p&gt;$$
\begin{bmatrix}
F_{k+1} &amp;amp; F_k \
F_k &amp;amp; F_{k-1}
\end{bmatrix} =
\begin{bmatrix}
1 &amp;amp; 1 \
1 &amp;amp; 0
\end{bmatrix}^k
$$&lt;/p&gt;
&lt;p&gt;이제 k = m+n일 때를 생각해보면&lt;/p&gt;
&lt;p&gt;$$
\begin{bmatrix}
F_{m+n+1} &amp;amp; F_{m+n} \
F_{m+n} &amp;amp; F_{m+n-1}
\end{bmatrix} =
\begin{bmatrix}
1 &amp;amp; 1 \
1 &amp;amp; 0
\end{bmatrix}^{m+n}
$$&lt;/p&gt;
&lt;p&gt;여기서 중요한 성질은 행렬의 거듭제곱 법칙입니다요&lt;/p&gt;
&lt;p&gt;$$
\begin{bmatrix}
1 &amp;amp; 1 \
1 &amp;amp; 0
\end{bmatrix}^{m+n} =
\begin{bmatrix}
1 &amp;amp; 1 \
1 &amp;amp; 0
\end{bmatrix}^m
\begin{bmatrix}
1 &amp;amp; 1 \
1 &amp;amp; 0
\end{bmatrix}^n =
\begin{bmatrix}
F_{m+1} &amp;amp; F_m \
F_m &amp;amp; F_{m-1}
\end{bmatrix}
\begin{bmatrix}
F_{n+1} &amp;amp; F_n \
F_n &amp;amp; F_{n-1}
\end{bmatrix}
$$&lt;/p&gt;
&lt;p&gt;그래서 이제 각각의 행렬을 보면 아래오 같습니다.&lt;/p&gt;
&lt;p&gt;$$
\begin{bmatrix}
1 &amp;amp; 1 \
1 &amp;amp; 0
\end{bmatrix}^m =
\begin{bmatrix}
F_{m+1} &amp;amp; F_m \
F_m &amp;amp; F_{m-1}
\end{bmatrix}
$$&lt;/p&gt;
&lt;p&gt;$$
\begin{bmatrix}
1 &amp;amp; 1 \
1 &amp;amp; 0
\end{bmatrix}^n =
\begin{bmatrix}
F_{n+1} &amp;amp; F_n \
F_n &amp;amp; F_{n-1}
\end{bmatrix}
$$&lt;/p&gt;
&lt;p&gt;이 두 행렬을 곱하면&lt;/p&gt;
&lt;p&gt;$$
\begin{bmatrix}
F_{m+1}F_{n+1} + F_mF_n &amp;amp; F_{m+1}F_n + F_mF_{n-1} \
F_mF_{n+1} + F_{m-1}F_n &amp;amp; F_mF_n + F_{m-1}F_{n-1}
\end{bmatrix} =
\begin{bmatrix}
F_{m+n+1} &amp;amp; F_{m+n} \
F_{m+n} &amp;amp; F_{m+n-1}
\end{bmatrix}
$$&lt;/p&gt;
&lt;p&gt;결과적으로는 아래의 결과를 확인할 수 있어요!&lt;/p&gt;
&lt;p&gt;$$
\LARGE
F_{m+n} = F_m F_{n+1} + F_{m-1}F_n
$$&lt;/p&gt;
&lt;h2&gt;2.2. 수학적 귀납법을 이용한 증명&lt;/h2&gt;
&lt;p&gt;고정된 m에 대해 n에 대한 귀납법을 사용할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;먼저, 귀납법의 기본 단계(n=1)를 살펴봅시다&lt;/p&gt;
&lt;p&gt;좌변: $$ F_{m+1} = F_m + F_{m-1} $$ (피보나치 수열의 정의)&amp;lt;/br&amp;gt;
우변: $$ F_{m-1}F_1 + F_mF_2 = F_{m-1} + F_m $$ (F₁ = F₂ = 1이므로)&lt;/p&gt;
&lt;p&gt;다음으로, 귀납 가정(n=k)을 설정하고 봅시다&lt;/p&gt;
&lt;p&gt;$$F_{m+k} = F_{m-1}F_k + F_mF_{k+1}$$&lt;/p&gt;
&lt;p&gt;마지막으로, 귀납 단계(n=k+1)를 증명할 수 있어요&lt;/p&gt;
&lt;p&gt;$$F_{m+k+1} = F_{m+k} + F_{m+k-1}$$ (피보나치 수열의 정의)&lt;/p&gt;
&lt;p&gt;귀납 단계의 완전한 증명을 위해 더 자세히 전개 해봅시다&lt;/p&gt;
&lt;p&gt;$$
\begin{align*}
F_{m+k+1} &amp;amp;= F_{m+k} + F_{m+k-1} \
&amp;amp;= (F_{m-1}F_k + F_mF_{k+1}) + (F_{m-1}F_{k-1} + F_mF_k) \
&amp;amp;= F_{m-1}(F_k + F_{k-1}) + F_m(F_{k+1} + F_k) \
&amp;amp;= F_{m-1}F_{k+1} + F_mF_{k+2} \
\end{align*}
$$&lt;/p&gt;
&lt;p&gt;이렇게 귀납법의 모든 단계가 성립함을 보였으므로, 도가뉴 항등식이 모든 자연수 n에 대해 성립함이 증명된 겁니다.
수식과 증명 과정을 함께 살펴보면, 이 항등식이 피보나치 수열의 재귀적 성질을 어떻게 활용하는지 볼 수 있습니다. 굉장히 흥미로운디용 하ㅏ하하ㅏ하하&lt;/p&gt;
&lt;p&gt;$$
\Large
\begin{align*}
(F_k + F_{k-1}) &amp;amp;= F_{k+1} \
(F_{k+1} + F_k) &amp;amp;= F_{k+2}
\end{align*}
$$&lt;/p&gt;
&lt;h1&gt;3. 음의 정수 확장&lt;/h1&gt;
&lt;p&gt;일반적으로 피보나치 수열은 양의 정수 인덱스에 대해 정의됩니다&lt;/p&gt;
&lt;p&gt;$$F_n = F_{n-1} + F_{n-2}, \text{ where } F_0 = 0, F_1 = 1, F_2 = 1$$&lt;/p&gt;
&lt;p&gt;:::note[초기값]&lt;/p&gt;
&lt;p&gt;&amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;은 초기값 설정
:::&lt;/p&gt;
&lt;p&gt;하지만 이 점화식을 음의 정수 인덱스로도 확장할 수 있습니다. 음의 인덱스를 가진 피보나치 수는 아래의 공식을 따릅니다&lt;/p&gt;
&lt;p&gt;$$F_{-n} = (-1)^{n+1}F_n$$&lt;/p&gt;
&lt;p&gt;이것이 어떻게 작동하는지 이해하기 위해, 피보나치 수열의 점화식을 역방향으로 생각해봅시다.&lt;/p&gt;
&lt;p&gt;기존 점화식을 변형해보면&lt;/p&gt;
&lt;p&gt;$$F_{n-2} = F_n - F_{n-1}$$&lt;/p&gt;
&lt;p&gt;이를 이용해 F₀부터 시작하여 음의 인덱스의 값들을 계산할 수 있습니다&lt;/p&gt;
&lt;p&gt;F₀ = 0&amp;lt;/br&amp;gt;
F₁ = 1&amp;lt;/br&amp;gt;
F₋₁ = F₁ - F₀ = 1 - 0 = 1&amp;lt;/br&amp;gt;
F₋₂ = F₀ - F₋₁ = 0 - 1 = -1&amp;lt;/br&amp;gt;
F₋₃ = F₋₁ - F₋₂ = 1 - (-1) = 2&amp;lt;/br&amp;gt;
F₋₄ = F₋₂ - F₋₃ = -1 - 2 = -3&amp;lt;/br&amp;gt;
F₋₅ = F₋₃ - F₋₄ = 2 - (-3) = 5&amp;lt;/br&amp;gt;&lt;/p&gt;
&lt;p&gt;이렇게 확장된 전체 수열을 나열하면&lt;/p&gt;
&lt;p&gt;$$..., 8, -5, 3, -2, 1, -1, 0, 1, 1, 2, 3, 5, 8, ...$$&lt;/p&gt;
&lt;p&gt;타란!&lt;/p&gt;
&lt;h1&gt;4. 급수 공식들&lt;/h1&gt;
&lt;h2&gt;4.1. 합 공식&lt;/h2&gt;
&lt;p&gt;처음 n개의 피보나치 수를 더한 값&lt;/p&gt;
&lt;p&gt;$$
\Large
\sum_{k=0}^n F_k = F_{n+2} - 1
$$&lt;/p&gt;
&lt;h2&gt;4.2. 교대합 공식&lt;/h2&gt;
&lt;p&gt;피보나치 수들을 번갈아가며 더하고 빼는 경우의 합&lt;/p&gt;
&lt;p&gt;$$
\Large
\sum_{k=0}^n (-1)^{k+1}F_k = (-1)^{n+1}F_{n-1} + 1
$$&lt;/p&gt;
&lt;h2&gt;4.3. 제곱합 공식&lt;/h2&gt;
&lt;p&gt;피보나치 수들의 제곱의 합&lt;/p&gt;
&lt;p&gt;$$
\Large
\sum_{k=0}^n F_k^2 = F_nF_{n+1}
$$&lt;/p&gt;
&lt;h2&gt;4.4. 세제곱합 공식&lt;/h2&gt;
&lt;p&gt;피보나치 수들 세제곱의 합&lt;/p&gt;
&lt;p&gt;$$
\Large
\sum_{k=0}^n F_k^3 = \frac{F_{3n+2} + (-1)^{n+1}6F_{n-1} + 5}{10}
$$&lt;/p&gt;
&lt;h2&gt;4.5. 참고할만한 공식&lt;/h2&gt;
&lt;p&gt;:::note[기본적 공식]&lt;/p&gt;
&lt;h3&gt;홀수번째 항들의 합&lt;/h3&gt;
&lt;p&gt;$$
\Large
\sum_{k=1}^{n} F_{2k-1} = F_{2n}
$$&lt;/p&gt;
&lt;h3&gt;짝수번째 항들의 합&lt;/h3&gt;
&lt;p&gt;$$
\Large
\sum_{k=0}^{n} F_{2k} = F_{2n+1} - 1
$$&lt;/p&gt;
&lt;h3&gt;3의 배수번째 항들의 합&lt;/h3&gt;
&lt;p&gt;$$
\Large
\sum_{k=0}^{n} F_{3k} = \frac{F_{3n+2} - 1}{2}
$$&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;그 외의 공식도 있고, 특성도 있는데 그건 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98_%EC%88%98#:~:text=%EB%8B%A4%EC%9D%8C%EA%B3%BC%20%EA%B0%99%EC%9D%80%20%ED%95%AD%EB%93%B1%EC%8B%9D%EC%9D%B4%20%EC%84%B1%EB%A6%BD%ED%95%98%EB%A9%B0%2C%20%EC%9D%B4%EB%A5%BC%20%EB%8F%84%EA%B0%80%EB%89%B4,&apos;Ocagne&apos;s%20identity&quot;&gt;여기&lt;/a&gt;를 참고해서 보시면 되겠습니다.&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h1&gt;5. 그 외 기억해 둘만한 성질들&lt;/h1&gt;
&lt;h2&gt;5.1. 비네 공식 (Binet&apos;s Formula)&lt;/h2&gt;
&lt;p&gt;무리수인 √5와 황금비(φ)를 사용하여 항상 정수인 피보나치 수를 정확히 계산&lt;/p&gt;
&lt;p&gt;:::note[깨알 상식]&lt;/p&gt;
&lt;p&gt;황금비에 대해서 먼저 알아야 되더라구요!&lt;/p&gt;
&lt;p&gt;φ = (1 + √5) / 2 ≈ 1.618034&amp;lt;/br&amp;gt;
수식에서는 이렇게 정의하고 있는데요
φ² = φ + 1 이런 것도 만족한다는 것이지요 허헣&lt;/p&gt;
&lt;p&gt;φ¹ = φ&amp;lt;/br&amp;gt;
φ² = φ + 1&amp;lt;/br&amp;gt;
φ³ = φ² × φ = (φ + 1)φ = φ² + φ = (φ + 1) + φ = 2φ + 1&amp;lt;/br&amp;gt;
φ⁴ = φ³ × φ = (2φ + 1)φ = 2φ² + φ = 2(φ + 1) + φ = 3φ + 2&amp;lt;/br&amp;gt;&lt;/p&gt;
&lt;p&gt;그래서 이렇게 풀이할 수 있습니다.
:::&lt;/p&gt;
&lt;p&gt;정의를 먼저 해보겠습니다&lt;/p&gt;
&lt;p&gt;φ = (1 + √5) / 2 ≈ 1.618034 -&amp;gt; 황금비&lt;/p&gt;
&lt;p&gt;ψ = -1/φ ≈ -0.618034 -&amp;gt; 반황금비&lt;/p&gt;
&lt;p&gt;:::tip[]&lt;/p&gt;
&lt;p&gt;φⁿ은 항상 aφ + b 형태&amp;lt;/br&amp;gt;
ψⁿ도 같은 방식으로 cφ + d 형태&amp;lt;/br&amp;gt;
φ - ψ = √5&amp;lt;/br&amp;gt;&lt;/p&gt;
&lt;p&gt;그래서  (φⁿ - ψⁿ) / √5는 항상 정수
:::&lt;/p&gt;
&lt;p&gt;$$
\Large
F_n = \frac{\phi^n - \phi^{n}}{\sqrt{5}} = \frac{1}{\sqrt{5}}\left[\left(\frac{1 + \sqrt{5}}{2}\right)^n - \left(\frac{1 - \sqrt{5}}{2}\right)^n\right]
$$&lt;/p&gt;
&lt;p&gt;솔직히 이거 보면서 좀 놀라긴 했슴둥&lt;/p&gt;
&lt;p&gt;재밌네요 언제나 수학을 배우다 보면 진짜 흥미롭고 신기한 것들 투성이네요&lt;/p&gt;
&lt;h2&gt;5.2. 서로소 성질&lt;/h2&gt;
&lt;p&gt;연속된 피보나치 수들은 항상 서로소
$$
\Large
gcd(F_n, F_{n+1}) = 1
$$&lt;/p&gt;
&lt;h2&gt;5.3. 황금비, 극한&lt;/h2&gt;
&lt;p&gt;피보나치 수열의 연속된 두 항의 비는 황금비(φ)에 수렴&lt;/p&gt;
&lt;p&gt;$$
\Large
\lim_{n \to \infty} \frac{F_{n+1}}{F_n} = \phi = \frac{1 + \sqrt{5}}{2} \approx 1.618034
$$&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;정말 놀랍게도 이런 공식들이 있다는데 신기하네요!!&lt;/p&gt;
&lt;p&gt;코딩, ps 하다보면 정말 많이 보게되는 수열 피보나치 수열인데 이런 것들을 모르고 있었다는 것도 좀 웃기긴 합니다 ㅋㅋㅌㅎㅋㅎ&lt;/p&gt;
&lt;p&gt;그 외로 더욱 자세한 설명은 나중에 알고리즘 구현 때 다 깊이 다뤄볼게용.. 저도 이게 지금 정리한걸 풀어쓰고 있기도하고, 다 잘 알고 있는게 아니여서 더욱 자세히 쓰다가는 오류를 범할 것 같아 여기까지 마무리하겠스빈다.&lt;/p&gt;
&lt;p&gt;나중에는 오일러 피 함수와 gcd 어떻게 효율적으로 다 할 수 있는지에 대해서 다뤄봐도 좋을 것 같네요!!&lt;/p&gt;
&lt;p&gt;아 그리고 여기에 나오는 공식으로 &lt;a href=&quot;https://compy07.github.io/Blog/posts/boj/11444/&quot;&gt;이 문제&lt;/a&gt;를 풀 수 있어요!
한번 다들 try try~~&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98_%EC%88%98#:~:text=%EB%8B%A4%EC%9D%8C%EA%B3%BC%20%EA%B0%99%EC%9D%80%20%ED%95%AD%EB%93%B1%EC%8B%9D%EC%9D%B4%20%EC%84%B1%EB%A6%BD%ED%95%98%EB%A9%B0%2C%20%EC%9D%B4%EB%A5%BC%20%EB%8F%84%EA%B0%80%EB%89%B4,&apos;Ocagne&apos;s%20identity&quot;&gt;wiki&lt;/a&gt;&lt;/h3&gt;
</content:encoded></item><item><title>[NYPC] 1차 1번 초밥</title><link>https://compy07.github.io/Blog/posts/algorithms/nypc/2024/first/1/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/nypc/2024/first/1/</guid><description>그냥 문제</description><pubDate>Wed, 06 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;pro1.png&quot; alt=&quot;pro&quot; /&gt;&lt;/p&gt;
&lt;p&gt;생각하면 풀리는 문제&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for i in range(int(input())):
    a, b = map(int, input().split())

    if b &amp;lt; a//3+(1 if a % 3 else 0) or a &amp;lt; b//3+(1 if b % 3 else 0): print(-1)
    else: print((a+b)//4 + (1 if ((a+b)%4) else 0))
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>[NYPC] 1차 2번 무한 길이 물풍선</title><link>https://compy07.github.io/Blog/posts/algorithms/nypc/2024/first/2/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/nypc/2024/first/2/</guid><description>그냥 문제</description><pubDate>Wed, 06 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;pro2.png&quot; alt=&quot;pro&quot; /&gt;&lt;/p&gt;
&lt;p&gt;딱히 설명이 필요하지 않은거 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def count_explosion_lines():
    N = int(input())
    x_coords = {}
    y_coords = {}

    for _ in range(N):
        x, y = map(int, input().split())
        x_coords[x] = x_coords.get(x, 0) + 1
        y_coords[y] = y_coords.get(y, 0) + 1

    vertical_lines = sum(1 for count in x_coords.values() if count &amp;gt;= 2)
    horizontal_lines = sum(1 for count in y_coords.values() if count &amp;gt;= 2)

    return vertical_lines + horizontal_lines


print(count_explosion_lines())
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>[NYPC] 1차 3번 커닝시티 헤어샵</title><link>https://compy07.github.io/Blog/posts/algorithms/nypc/2024/first/3/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/nypc/2024/first/3/</guid><description>그냥 문제</description><pubDate>Wed, 06 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;pro3.png&quot; alt=&quot;pro&quot; /&gt;&lt;/p&gt;
&lt;p&gt;기본적인 dp 문제&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def solution():
    n = int(input())

    time_table = list(map(int, input().split()))

    dp = [float(&apos;inf&apos;)] * (n+1)

    dp[0] = time_table[0]
    if n == 1: return dp[0]

    dp[1] = min(max(time_table[0], time_table[1]), dp[0] + time_table[1])

    for i in range(2, n):
        dp[i] = min(dp[i-1]+time_table[i], max(time_table[i], time_table[i-1]) + dp[i-2])
    return dp[n-1]
print(solution())
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>[NYPC] 1차 4번 오르락 내리락</title><link>https://compy07.github.io/Blog/posts/algorithms/nypc/2024/first/4/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/nypc/2024/first/4/</guid><description>그냥 문제</description><pubDate>Wed, 06 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;pro4.png&quot; alt=&quot;pro&quot; /&gt;&lt;/p&gt;
&lt;p&gt;기본적인 dp 문제2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def solution(T):
    n = len(T)
    if n &amp;lt;= 2: return n
    # dp[i][0]: i번째 원소로 끝나는 홀수 위치 증가 수열의 최대 길이
    # dp[i][1]: i번째 원소로 끝나는 짝수 위치 감소 수열의 최대 길이
    dp = [[1, 1] for _ in range(n)]

    dp[1] = [1, 2]
    max_length = 1
    # 0번째가 증가, 즉 홀수 -&amp;gt; -2
    # 1번째가 감소, 즉 짝수
    for i in range(2, n):
        if T[i - 2] &amp;lt; T[i]:
            dp[i][0] = dp[i - 1][1] + 1
            dp[i][1] = 2
        if T[i - 2] &amp;gt; T[i]:
            dp[i][1] = dp[i - 1][0] + 1
            # dp[i][0] = 2
        if T[i - 2] == T[i]:
            dp[i] = [1, 2]

        max_length = max([dp[i][0], dp[i][1], max_length])
        # print(dp)

    return max(max_length, 2)
input()
print(solution(list(map(int, input().split()))))

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>[NYPC] 1차 5번 게임</title><link>https://compy07.github.io/Blog/posts/algorithms/nypc/2024/first/5/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/nypc/2024/first/5/</guid><description>그냥 문제</description><pubDate>Wed, 06 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;pro5.png&quot; alt=&quot;pro&quot; /&gt;&lt;/p&gt;
&lt;p&gt;그냥 구현 문제&lt;/p&gt;
&lt;p&gt;5번까지는 그래도 빨리 풀긴 하였다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def play_game(s, k, n):
    s = list(s)
    zero, one = k, k
    # zc, oc = 0, 0
    # for i in range(n):
    #     if s[i] ==&apos;0&apos;: zc+=1
    #     else: oc += 1

    removed = set()
    zero_left, one_left = 0, 0
    for i in range(n):
        if s[i] == &apos;0&apos; and zero:
            zero -= 1
            zero_left = i
        elif s[i] == &apos;1&apos; and one:
            one -= 1
            one_left = i
    # print(len(removed), zero, one, removed)

    for i in range(n - 1, -1, -1):
        if zero and s[i] == &apos;1&apos;:
            removed.add(i)
            zero -= 1
        if one and s[i] == &apos;0&apos;:
            removed.add(i)
            one -= 1

        if not one and not zero: break


    result = []

    for i in range(min(zero_left, one_left)+1, n):
        if (s[i] == &apos;1&apos; and one_left &amp;gt;= i) or (s[i] == &apos;0&apos; and zero_left &amp;gt;= i) or i in removed: continue
        result.append(s[i])
    # print(len(result))
    return &apos;&apos;.join(result)
    
    
n, k = map(int, input().split())
s = input()
result = play_game(s, k, n)
if len(result) == 0: result=&apos;0&apos;
print(result)

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>[Algorithm] 알고리즘 1차시 수업</title><link>https://compy07.github.io/Blog/posts/algorithms/school/algorithm/1/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/algorithms/school/algorithm/1/</guid><description>기본 정렬(삽입, 버블, 선택)과 고급 정렬(머지, 퀵, 힙)에 대해서 알아보자. 알고리즘 1차시 과제</description><pubDate>Fri, 14 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;기본 정렬의 원리와 시간복잡도 비교&lt;/h1&gt;
&lt;h2&gt;선택 정렬 (Selection Sort)&lt;/h2&gt;
&lt;p&gt;:::tip[원리]&lt;/p&gt;
&lt;p&gt;선택 정렬은 매우 직관적인 정렬 알고리즘!
배열에서 가장 작은(또는 가장 큰) 원소를 찾아 첫 번째 위치와 교환한 후, 남은 부분 배열에서 같은 과정을 반복하는 방식으로 작동
:::&lt;/p&gt;
&lt;h4&gt;동작 과정&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;정렬되지 않은 부분에서 최솟값(또는 최댓값)을 찾기&lt;/li&gt;
&lt;li&gt;이 값을 현재 위치(정렬되지 않은 부분의 첫 번째 위치)와 교환&lt;/li&gt;
&lt;li&gt;정렬된 부분을 한 칸 확장하고, 정렬되지 않은 부분을 한 칸 축소&lt;/li&gt;
&lt;li&gt;정렬되지 않은 부분이 없어질 때까지 1-3 과정을 반복&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 예시 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void selectionSort(vector&amp;lt;int&amp;gt;&amp;amp; arr) {
    int n = arr.size();
    
    for (int i = 0; i &amp;lt; n-1; i++) {
        // 최솟값의 인덱스 찾기
        int min_idx = i;
        for (int j = i+1; j &amp;lt; n; j++) {
            if (arr[j] &amp;lt; arr[min_idx])
                min_idx = j;
        }
        
        // 최솟값을 현재 위치로 교환
        swap(arr[min_idx], arr[i]);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시간 복잡도: 항상 O(n²)&lt;/li&gt;
&lt;li&gt;공간 복잡도: O(1) (제자리 정렬)&lt;/li&gt;
&lt;li&gt;안정성: 불안정 (기본 구현에서)&lt;/li&gt;
&lt;li&gt;장점: 구현이 단순하고, 교환 연산 횟수가 적음 (최대 n-1회)&lt;/li&gt;
&lt;li&gt;단점: 배열 크기에 관계없이 항상 O(n²) 시간이 걸림&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;버블 정렬 (Bubble Sort)&lt;/h2&gt;
&lt;p&gt;:::tip[원리]&lt;/p&gt;
&lt;p&gt;버블 정렬은 인접한 두 원소를 비교하여 필요시 위치를 교환하는 과정을 반복!
매 패스마다 가장 큰(또는 작은) 원소가 배열의 끝으로 &quot;버블링(bubbling)&quot;되는 모습이 특징
:::&lt;/p&gt;
&lt;h4&gt;동작 과정&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;인접한 두 원소를 비교&lt;/li&gt;
&lt;li&gt;순서가 잘못되어 있으면 두 원소를 교환&lt;/li&gt;
&lt;li&gt;배열의 끝까지 이 과정을 반복 (첫 패스 후에는 가장 큰 원소가 마지막 위치에 정렬됨)&lt;/li&gt;
&lt;li&gt;배열 크기를 1 감소시키고 1-3 과정을 반복&lt;/li&gt;
&lt;li&gt;교환이 더 이상 일어나지 않으면 정렬이 완료!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 예시 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void bubbleSort(vector&amp;lt;int&amp;gt;&amp;amp; arr) {
    int n = arr.size();
    bool swapped;
    
    for (int i = 0; i &amp;lt; n-1; i++) {
        swapped = false;
        
        // 각 패스에서 인접 원소 비교 및 교환
        for (int j = 0; j &amp;lt; n-i-1; j++) {
            if (arr[j] &amp;gt; arr[j+1]) {
                swap(arr[j], arr[j+1]);
                swapped = true;
            }
        }
        
        // 교환이 일어나지 않았다면 이미 정렬된 상태
        if (!swapped)
            break;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시간 복잡도: 평균 및 최악 O(n²), 최선 O(n) (이미 정렬된 경우)&lt;/li&gt;
&lt;li&gt;공간 복잡도: O(1) (제자리 정렬)&lt;/li&gt;
&lt;li&gt;안정성: 안정적&lt;/li&gt;
&lt;li&gt;장점: 구현이 매우 간단하고, 이미 정렬된 데이터에 대해 최적화 가능&lt;/li&gt;
&lt;li&gt;단점: 대체로 다른 정렬 알고리즘보다 느림&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;삽입 정렬 (Insertion Sort)&lt;/h2&gt;
&lt;p&gt;:::tip[원리]&lt;/p&gt;
&lt;p&gt;삽입 정렬은 카드 게임을 할 때 손에 든 카드를 정렬하는 방식과 유사!
정렬되지 않은 부분에서 원소를 하나씩 가져와 이미 정렬된 부분의 적절한 위치에 삽입하는 방식으로 작동
:::&lt;/p&gt;
&lt;h4&gt;동작 과정&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;두 번째 원소부터 시작(첫 번째 원소는 이미 정렬된 것으로 간주)&lt;/li&gt;
&lt;li&gt;현재 원소를 이미 정렬된 부분 배열과 비교&lt;/li&gt;
&lt;li&gt;정렬된 배열에서 현재 원소보다 큰 모든 원소를 한 칸씩 오른쪽으로 이동&lt;/li&gt;
&lt;li&gt;적절한 위치에 현재 원소를 삽입&lt;/li&gt;
&lt;li&gt;배열의 모든 원소에 대해 2-4 과정을 반복&lt;/li&gt;
&lt;li&gt;완료!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 예시 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void insertionSort(vector&amp;lt;int&amp;gt;&amp;amp; arr) {
    int n = arr.size();
    
    for (int i = 1; i &amp;lt; n; i++) {
        int key = arr[i];
        int j = i - 1;
        
        // key보다 큰 원소들을 오른쪽으로 이동
        while (j &amp;gt;= 0 &amp;amp;&amp;amp; arr[j] &amp;gt; key) {
            arr[j + 1] = arr[j];
            j = j - 1;
        }
        arr[j + 1] = key;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시간 복잡도: 평균 및 최악 O(n²), 최선 O(n) (이미 정렬된 경우)&lt;/li&gt;
&lt;li&gt;공간 복잡도: O(1) (제자리 정렬)&lt;/li&gt;
&lt;li&gt;안정성: 안정적&lt;/li&gt;
&lt;li&gt;장점: 작은 데이터셋에서 효율적, 거의 정렬된 데이터에 매우 빠름, 온라인 알고리즘(데이터가 실시간으로 들어와도 정렬 가능)&lt;/li&gt;
&lt;li&gt;단점: 큰 데이터셋에서는 비효율적&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;종류&lt;/th&gt;
&lt;th&gt;평균 시간 복잡도&lt;/th&gt;
&lt;th&gt;최선 시간 복잡도&lt;/th&gt;
&lt;th&gt;최악 시간 복잡도&lt;/th&gt;
&lt;th&gt;공간 복잡도&lt;/th&gt;
&lt;th&gt;안정성&lt;/th&gt;
&lt;th&gt;적응성&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;선택 정렬&lt;/td&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;불안정&lt;/td&gt;
&lt;td&gt;아니오&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;버블 정렬&lt;/td&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;안정적&lt;/td&gt;
&lt;td&gt;예&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;삽입 정렬&lt;/td&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;안정적&lt;/td&gt;
&lt;td&gt;예&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;:::note[적응성?]&lt;/p&gt;
&lt;p&gt;이미 부분적으로 정렬된 배열에 대해 더 좋은 성능을 보이는지 여부
:::&lt;/p&gt;
&lt;p&gt;:::note[안정성?]&lt;/p&gt;
&lt;p&gt;선택 정렬을 예시로 들어보자&lt;/p&gt;
&lt;p&gt;선택 정렬은 불안정 정렬이다!
제일 작은 값을 찾은 후 교환(어느 위치에 있던지, 교환됨)하기 때문이다.
교환을 통해서 순서가 고려되지 못하기 때문에 안정적으로 정렬되지 않는다.(기본 구현에서는 이렇구 따로 고려하여 작성하면 해결 가능)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./df.gif&quot; alt=&quot;stable&quot; /&gt;&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h1&gt;고급 정렬의 원리와 시간복잡도 비교&lt;/h1&gt;
&lt;h2&gt;병합 정렬 (Merge Sort)&lt;/h2&gt;
&lt;p&gt;:::tip[원리]&lt;/p&gt;
&lt;p&gt;병합 정렬은 &apos;분할 정복(divide and conquer)&apos; 방식을 사용
배열을 절반으로 나누고, 각 절반을 재귀적으로 정렬한 다음, 정렬된 두 절반을 병합
:::&lt;/p&gt;
&lt;h4&gt;동작 과정&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;분할(Divide)
&lt;ul&gt;
&lt;li&gt;배열을 절반으로 계속해서 나눕니다. 이 과정은 더 이상 나눌 수 없을 때까지(요소가 1개만 남을 때까지) 재귀적으로 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정복(Conquer)
&lt;ul&gt;
&lt;li&gt;크기가 1인 부분 배열은 이미 정렬된 상태로 간주합니다. 이는 기저 사례(base case)입니다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;병합(Merge)
&lt;ul&gt;
&lt;li&gt;나누어진 부분 배열들을 정렬된 상태로 병합합니다&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;두 부분 배열의 첫 번째 요소부터 비교&lt;/li&gt;
&lt;li&gt;더 작은 값을 결과 배열에 넣고, 해당 배열의 포인터를 다음 요소로 이동&lt;/li&gt;
&lt;li&gt;이 과정을 한 배열이 모두 소진될 때까지 반복&lt;/li&gt;
&lt;li&gt;남은 요소들을 결과 배열에 차례대로 추가&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 예시 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int A[1&apos;000&apos;005], N;

void Merge(int s1, int e1, int s2, int e2){
    vector&amp;lt;int&amp;gt; tmp;
    int idx_1 = s1, idx_2 = s2;
    while (idx_1 &amp;lt;= e1 || idx_2 &amp;lt;= e2){
        if (idx_1 &amp;gt; e1) tmp.push_back(A[idx_2++]);
        else if (idx_2 &amp;gt; e2) tmp.push_back(A[idx_1++]);
        else if (A[idx_1] &amp;lt; A[idx_2]) tmp.push_back(A[idx_1++]);
        else tmp.push_back(A[idx_2++]);
    }
    
    for (int i = 0; i &amp;lt; tmp.size(); i++) A[s1 + i] = tmp[i];
}

void MergeSort(int s, int e){
    if (s == e) return;
    int mid = (s+e) / 2;
    MergeSort(s, mid);
    MergeSort(mid+1, e);
    Merge(s, mid, mid+1, e);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시간 복잡도: 항상 O(n log n)&lt;/li&gt;
&lt;li&gt;공간 복잡도: O(n)&lt;/li&gt;
&lt;li&gt;안정성: 안정적 (동일한 값의 상대적 순서 유지)&lt;/li&gt;
&lt;li&gt;최적 사용 사례: 연결 리스트 정렬, 대용량 데이터 정렬&lt;/li&gt;
&lt;li&gt;장점: 일관된 성능, 안정적, 데이터 분포에 영향 받지 않음&lt;/li&gt;
&lt;li&gt;단점: 추가 메모리 공간 필요, 작은 데이터셋에는 비효율적&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;퀵 정렬(Quick Sort)&lt;/h2&gt;
&lt;p&gt;:::tip[원리]&lt;/p&gt;
&lt;p&gt;퀵 정렬도 분할 정복 방식을 사용하지만, 피벗(pivot)이라는 요소를 기준으로 배열을 분할
피벗보다 작은 요소들은 왼쪽으로, 큰 요소들은 오른쪽으로 이동시킨 후 각 부분 배열을 재귀적으로 정렬
:::&lt;/p&gt;
&lt;h4&gt;동작 과정&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;피벗 선택(Select a Pivot)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;배열에서 하나의 요소를 피벗(pivot)으로 선택 피벗 선택&amp;lt;/br&amp;gt; 방법은 다양하지만, 일반적으로 첫 번째, 마지막, 또는 중간 요소를 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;분할(Partition)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;피벗을 기준으로 배열을 두 부분으로 재배치&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;피벗보다 작은 모든 요소는 피벗의 왼쪽으로 이동&lt;/li&gt;
&lt;li&gt;피벗보다 큰 모든 요소는 피벗의 오른쪽으로 이동&lt;/li&gt;
&lt;li&gt;분할 후 피벗은 최종 위치에 놓이게 됩니다&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;재귀적 정렬(Recursive Sort)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;피벗의 왼쪽과 오른쪽 부분 배열에 대해 1단계와 2단계를 재귀적으로 적용&amp;lt;/br&amp;gt;부분 배열의 크기가 1 이하일 때 재귀를 종료&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 예시 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```cpp
int partition(vector&amp;lt;int&amp;gt;&amp;amp; arr, int low, int high) {
    int pivot = arr[high];  // 피벗으로 마지막 요소 선택
    int i = low - 1;        // 더 작은 요소의 위치
    
    for (int j = low; j &amp;lt; high; j++) {
        // 현재 요소가 피벗보다 작거나 같으면
        if (arr[j] &amp;lt;= pivot) {
            i++;
            swap(arr[i], arr[j]);
        }
    }
    swap(arr[i + 1], arr[high]);
    return i + 1;  // 피벗의 최종 위치 반환
}

void quickSort(vector&amp;lt;int&amp;gt;&amp;amp; arr, int low, int high) {
    if (low &amp;lt; high) {
        // 배열 분할
        int pi = partition(arr, low, high);
        
        // 분할된 부분 배열 정렬
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시간 복잡도: 평균 O(n log n), 최악 O(n²)&lt;/li&gt;
&lt;li&gt;공간 복잡도: O(log n) (재귀 호출 스택)&lt;/li&gt;
&lt;li&gt;안정성: 불안정&lt;/li&gt;
&lt;li&gt;최적 사용 사례: 평균적인 케이스, 작은-중간 크기 배열&lt;/li&gt;
&lt;li&gt;장점: 실제 구현에서 빠른 성능, 작은 추가 메모리&lt;/li&gt;
&lt;li&gt;단점: 최악의 경우 성능 저하, 이미 정렬된 데이터에 취약&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;힙 정렬 (Heap Sort)&lt;/h2&gt;
&lt;p&gt;:::tip[원리]&lt;/p&gt;
&lt;p&gt;힙 정렬은 이진 힙 자료구조를 이용!
먼저 배열을 최대 힙(부모 노드가 자식 노드보다 크거나 같은 완전 이진 트리)으로 구성한 다음, 루트(최댓값)를 배열의 끝으로 이동시키고 힙 크기를 줄여가며 과정을 반복
:::&lt;/p&gt;
&lt;h4&gt;동작 과정&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;힙 구성(Build Heap)
&lt;ul&gt;
&lt;li&gt;주어진 배열을 최대 힙으로 구성&amp;lt;/br&amp;gt; 배열의 중간부터 시작하여 루트까지 각 노드에 대해 &apos;힙화(heapify)&apos; 연산을 수행함으로써 이루어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;요소 추출 및 힙 재구성(Extract and Rebuild)
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;힙의 루트(최댓값)를 배열의 마지막 위치와 교환&lt;/li&gt;
&lt;li&gt;힙의 크기를 1 감소(마지막 요소는 이제 정렬된 부분에 속합니다)&lt;/li&gt;
&lt;li&gt;새로운 루트에 대해 &apos;힙화&apos; 연산을 수행하여 최대 힙 속성을 복원&lt;/li&gt;
&lt;li&gt;힙의 크기가 1이 될 때까지 이 과정을 반복&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&apos;힙화(Heapify)&apos; 연산
&lt;ul&gt;
&lt;li&gt;&apos;힙화&apos;는 특정 노드부터 시작하여 해당 노드가 자식 노드보다 작다면 자식 중 가장 큰 노드와 교환하고, 이 과정을 재귀적으로 반복하는 연산&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 예시 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```cpp
void heapify(vector&amp;lt;int&amp;gt;&amp;amp; arr, int n, int i) {
    int largest = i;      // 루트를 가장 큰 값으로 초기화
    int left = 2 * i + 1; // 왼쪽 자식
    int right = 2 * i + 2; // 오른쪽 자식
    
    // 왼쪽 자식이 루트보다 크면
    if (left &amp;lt; n &amp;amp;&amp;amp; arr[left] &amp;gt; arr[largest])
        largest = left;
    
    // 오른쪽 자식이 지금까지의 가장 큰 값보다 크면
    if (right &amp;lt; n &amp;amp;&amp;amp; arr[right] &amp;gt; arr[largest])
        largest = right;
    
    // 최대값이 루트가 아니면
    if (largest != i) {
        swap(arr[i], arr[largest]);
        
        // 영향 받은 서브트리에 대해 재귀적으로 heapify 실행
        heapify(arr, n, largest);
    }
}

void heapSort(vector&amp;lt;int&amp;gt;&amp;amp; arr) {
    int n = arr.size();
    
    // 최대 힙 구성
    for (int i = n / 2 - 1; i &amp;gt;= 0; i--)
        heapify(arr, n, i);
    
    // 힙에서 요소를 하나씩 추출
    for (int i = n - 1; i &amp;gt; 0; i--) {
        // 현재 루트를 끝으로 이동
        swap(arr[0], arr[i]);
        
        // 줄어든 힙에 대해 heapify 호출
        heapify(arr, i, 0);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시간 복잡도: 항상 O(n log n)&lt;/li&gt;
&lt;li&gt;공간 복잡도: O(1) (제자리 정렬)&lt;/li&gt;
&lt;li&gt;안정성: 불안정&lt;/li&gt;
&lt;li&gt;최적 사용 사례: 메모리 제약이 있는 환경, 최악 시간 복잡도 보장이 필요한 경우&lt;/li&gt;
&lt;li&gt;장점: 추가 메모리 불필요, 최악의 경우도 O(n log n) 보장&lt;/li&gt;
&lt;li&gt;단점: 실제 구현에서 캐시 효율성이 낮음, 평균적으로 퀵 정렬보다 느림&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;종류&lt;/th&gt;
&lt;th&gt;평균 시간 복잡도&lt;/th&gt;
&lt;th&gt;최악 시간 복잡도&lt;/th&gt;
&lt;th&gt;공간 복잡도&lt;/th&gt;
&lt;th&gt;안정성&lt;/th&gt;
&lt;th&gt;제자리 정렬&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;병합 정렬&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;안정적&lt;/td&gt;
&lt;td&gt;아니오&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;퀵 정렬&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;td&gt;불안정&lt;/td&gt;
&lt;td&gt;예&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;힙 정렬&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;불안정&lt;/td&gt;
&lt;td&gt;예&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;그러면 이제 메인 과제 Radix Sort(기수 정렬)에 대해서 알아봅시다.&lt;/p&gt;
&lt;h1&gt;Radix Sort(기수 정렬)&lt;/h1&gt;
&lt;p&gt;라딕스 정렬은 비교 기반이 아닌 정렬 알고리즘으로, 데이터의 각 자릿수를 기준으로 정렬을 수행!
이 정렬 방식은 특히 정수나 문자열과 같이 자릿수가 있는 데이터에 효과적입니둥&lt;/p&gt;
&lt;h2&gt;작동 원리&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;데이터의 가장 작은 자릿수(일의 자리)부터 시작하여 가장 큰 자릿수까지 정렬&lt;/li&gt;
&lt;li&gt;각 자릿수별로 안정적인 정렬 알고리즘(보통 계수 정렬)을 사용&lt;/li&gt;
&lt;li&gt;각 단계에서 정렬된 결과는 다음 자릿수 정렬의 입력으로 사용&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;특징&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;시간 복잡도: &lt;strong&gt;O(d × n)&lt;/strong&gt;, 여기서 d는 최대 자릿수, n은 정렬할 요소의 개수입니다.&lt;/li&gt;
&lt;li&gt;공간 복잡도: &lt;strong&gt;O(n + k)&lt;/strong&gt;, 여기서 k는 숫자 체계의 기수(예: 십진수에서는 10)입니다.&lt;/li&gt;
&lt;li&gt;안정적인 정렬&lt;/li&gt;
&lt;li&gt;비교 기반 정렬이 아니므로 이론적 하한인 O(n log n)을 뛰어넘을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;

// 특정 자릿수를 기준으로 계수 정렬을 수행하는 함수
void countSort(vector&amp;lt;int&amp;gt;&amp;amp; arr, int exp) {
    int n = arr.size();
    vector&amp;lt;int&amp;gt; output(n); // 결과를 저장할 배열
    vector&amp;lt;int&amp;gt; count(10, 0); // 0-9의 발생 횟수를 저장할 배열
    
    // 현재 자릿수에 해당하는 숫자의 발생 횟수 계산
    for (int i = 0; i &amp;lt; n; i++) 
        count[(arr[i] / exp) % 10]++;
    
    
    // count 배열을 누적합으로 변환
    for (int i = 1; i &amp;lt; 10; i++) 
        count[i] += count[i - 1];
    
    
    // 뒤에서부터 순회하여 출력 배열 구성 (안정적 정렬을 위해)
    for (int i = n - 1; i &amp;gt;= 0; i--) {
        output[count[(arr[i] / exp) % 10] - 1] = arr[i];
        count[(arr[i] / exp) % 10]--;
    }
    
    // 원래 배열에 정렬된 결과 복사
    for (int i = 0; i &amp;lt; n; i++) 
        arr[i] = output[i];
    
}

void radixSort(vector&amp;lt;int&amp;gt;&amp;amp; arr) {
    // 배열에서 최댓값 찾기
    int maxVal = *max_element(arr.begin(), arr.end());
    
    // 각 자릿수에 대해 계수 정렬 수행
    for (int exp = 1; maxVal / exp &amp;gt; 0; exp *= 10) 
        countSort(arr, exp);
    
}

void print(const vector&amp;lt;int&amp;gt;&amp;amp; arr) {
    for (int num : arr) 
        cout &amp;lt;&amp;lt; num &amp;lt;&amp;lt; &quot; &quot;;
    
    cout &amp;lt;&amp;lt; endl;
}

int main() {
    vector&amp;lt;int&amp;gt; arr = {170, 45, 75, 90, 802, 24, 2, 66};
    
    cout &amp;lt;&amp;lt; &quot;정렬 전 배열: &quot;;
    print(arr);
    
    radixSort(arr);
    
    cout &amp;lt;&amp;lt; &quot;정렬 후 배열: &quot;;
    print(arr);
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;계수 정렬(countSort)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;특정 자릿수에 대해 계수 정렬을 수행&lt;/li&gt;
&lt;li&gt;해당 자릿수의 숫자(0-9)의 발생 횟수를 계산&lt;/li&gt;
&lt;li&gt;누적합을 이용해 각 숫자의 최종 위치를 결정&lt;/li&gt;
&lt;li&gt;안정적인 정렬을 위해 배열을 뒤에서부터 순회&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;라딕스 정렬(radixSort)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;배열에서 최댓값을 찾아 자릿수를 결정&lt;/li&gt;
&lt;li&gt;일의 자리부터 시작하여 각 자릿수에 대해 계수 정렬을 수행&lt;/li&gt;
&lt;li&gt;자릿수는 10의 거듭제곱으로 표현(exp = 1, 10, 100, ...)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;장점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;큰 범위의 정수를 효율적으로 정렬 가능&lt;/li&gt;
&lt;li&gt;자릿수가 제한적인 경우 O(n) 시간 복잡도로 동작할 수 있음&lt;/li&gt;
&lt;li&gt;비교 연산을 사용하지 않아 특정 상황에서 빠른 성능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;단점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;추가 메모리가 필요&lt;/li&gt;
&lt;li&gt;음수 처리를 위해서는 추가 로직이 필요&lt;/li&gt;
&lt;li&gt;부동 소수점 숫자를 정렬하려면 알고리즘을 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;감사합니다!&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 제곱 ㄴㄴ 수</title><link>https://compy07.github.io/Blog/posts/boj/1016/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1016/</guid><description>흠.. 분노의 제출로 5번 날림</description><pubDate>Tue, 24 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1016&quot;&gt;제곱 ㄴㄴ 수&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ min ≤ 1,000,000,000,000, min ≤ max ≤ min + 1,000,000)&lt;/td&gt;
&lt;td&gt;에라토스테네스의 체, Math&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src=&quot;./t.png&quot; alt=&quot;t&quot; /&gt;
아니 이렇게 나와서 범위를 저렇게 잡았더니 10%대에서 계속 틀린다...&lt;/p&gt;
&lt;p&gt;이유를 못 찾고서 왜 안되는 것이냐 하면서 벌에별거 다 바꿔보다가&lt;/p&gt;
&lt;p&gt;처음 제출에서 범위만 그냥 1&apos;000&apos;000 하고서 냈더니 AC를 맞음.&lt;/p&gt;
&lt;p&gt;머리에 총 맞은 것처럼 머리가 ㄹㅇ 띵했는데.. 그냥 직접 계산해서 넣지 말고 sqrt(M)로 해서 작업하도록 하자.&lt;/p&gt;
&lt;p&gt;괜히 이상한 짓 하다가 이렇게 된다...ㅠㅜ&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MAX 200000001
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
#include &amp;lt;math.h&amp;gt;
using ll = long long;
using namespace std;

// 707,106.7811865475
bool table[1&apos;000&apos;001];
bool visited[1&apos;000&apos;001];
ll real_max = 708&apos;000;


ll m, M;



int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    
    cin &amp;gt;&amp;gt; m &amp;gt;&amp;gt; M;
    
    
    ll limit = (ll)sqrt((double)M);
    // 아 직접 순회 범위 자체가 작구나!&amp;gt;!?!
    // 새로운 깨달음
    
    ll result = 0;
    
    
    for(ll i = 2; i &amp;lt;= limit; i++){
        if(table[i]) continue;
        
        for(ll j = i+i; j &amp;lt;= limit; j+=i) table[j] = true;
        
        
        // 난 개빡돌이노? visited 만들엇으면 그냥 해버리면 되는걸 왜 안 한거야
//         cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; (m / (i*i)) * (i*i) &amp;lt;&amp;lt; &quot;\n&quot;;
        for(ll j = ((ll)(m / (i*i))) * (i*i); j &amp;lt;= M; j += (i*i)){
            if(j &amp;lt; m || visited[j - m]) continue;
            
            visited[j - m] = true;
            result++;
        }
        
    }
    
    
    
    
    
    cout &amp;lt;&amp;lt; M - result - m + 1;

}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 가르침</title><link>https://compy07.github.io/Blog/posts/boj/1062/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1062/</guid><description>재활 19일차</description><pubDate>Sat, 22 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1062&quot;&gt;가르침&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 50&amp;lt;br/&amp;gt;1 ≤ K ≤ 26)&lt;/td&gt;
&lt;td&gt;BruteForce&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;와우.. 문제를 정말 신기하게 풀었습니다...&lt;/p&gt;
&lt;p&gt;이게 원래 vector, set 이런거 좀 안쓰고 해보려고 했는데 브포 돌리느라 어쩔 수없이 일단 썼는데&lt;/p&gt;
&lt;p&gt;이게 접근 방식에 있어서 정해는 맞는 거 같긴한데.. 그니까 N에 대해서 브포를 돌리는게 아니라 K개의 알파벳에 대해서 브포를 돌려서
시간을 최대한 아낀다.. 이런 전략인데 이게 무슨 빠르게 연산하기 위해서 비트마스킹을 하더라구요?!&lt;/p&gt;
&lt;p&gt;근데 저는 그걸 안 쓰고 해버려서.. 이게 맞는 접근인 것 같으면서도 이게 실력이 많이 부족하다는 것을 느꼈습니다.&lt;/p&gt;
&lt;p&gt;더 노력하겠습니다...ㅠㅜ&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
using namespace std;
using ll = long long;

bool visited[26];
int n, k;
string words[51];
vector&amp;lt;char&amp;gt; alph;
vector&amp;lt;set&amp;lt;char&amp;gt;&amp;gt; word_chars;
int countReadable() {
    int count = 0;
    for(int i = 0; i &amp;lt; n; i++) {
        bool canRead = true;
        for(char c : word_chars[i]) {
            if(!visited[c - &apos;a&apos;]) {
                canRead = false;
                break;
            }
        }
        if(canRead) count++;
    }
    return count;
}

int mysearch(int index, int learned) {
    if(learned == k || index &amp;gt;= alph.size()) return countReadable();
    
    int result = 0;
    
    visited[alph[index] - &apos;a&apos;] = true;
    result = max(result, mysearch(index + 1, learned + 1));
    visited[alph[index] - &apos;a&apos;] = false;
    
    result = max(result, mysearch(index + 1, learned));
    
    return result;
}

int solution() {
    if(k &amp;lt; 5) return 0;
    k -= 5;
    set&amp;lt;char&amp;gt; poss;
    word_chars.resize(n);
    visited[&apos;a&apos; - &apos;a&apos;] = true;
    visited[&apos;n&apos; - &apos;a&apos;] = true;
    visited[&apos;t&apos; - &apos;a&apos;] = true;
    visited[&apos;c&apos; - &apos;a&apos;] = true;
    visited[&apos;i&apos; - &apos;a&apos;] = true;
    for(int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; words[i];
        for(int j = 4; j &amp;lt; words[i].size() - 4; j++){
            char c = words[i][j];
            if(visited[c - &apos;a&apos;]) continue;
            poss.insert(c);
            word_chars[i].insert(c);
        }
    }
    alph = vector&amp;lt;char&amp;gt;(poss.begin(), poss.end());

    
    return mysearch(0, 0);
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;

    cout &amp;lt;&amp;lt; solution();

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 게임</title><link>https://compy07.github.io/Blog/posts/boj/1072/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1072/</guid><description>재활 2일차</description><pubDate>Sun, 02 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1072&quot;&gt;게임&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 &amp;lt;= X &amp;lt;= 1,000,000,000, 0 &amp;lt;= Y &amp;lt;= X)&lt;/td&gt;
&lt;td&gt;Binary Search(이분 탐색)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;김형택은 지금 몰래 Spider Solitaire(스파이더 카드놀이)를 하고 있다. 형택이는 이 게임을 이길 때도 있었지만, 질 때도 있었다. 누군가의 시선이 느껴진 형택이는 게임을 중단하고 코딩을 하기 시작했다. 의심을 피했다고 생각한 형택이는 다시 게임을 켰다. 그 때 형택이는 잠시 코딩을 하는 사이에 자신의 게임 실력이 눈에 띄게 향상된 것을 알았다.&lt;/p&gt;
&lt;p&gt;이제 형택이는 앞으로의 모든 게임에서 지지 않는다. 하지만, 형택이는 게임 기록을 삭제 할 수 없기 때에, 자신의 못하던 예전 기록이 현재 자신의 엄청난 실력을 증명하지 못한다고 생각했다.
게임 기록은 다음과 같이 생겼다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;게임 횟수 : X&lt;/li&gt;
&lt;li&gt;이긴 게임 : Y (Z%)&lt;/li&gt;
&lt;li&gt;Z는 형택이의 승률이고, 소수점은 버린다. 예를 들어, X=53, Y=47이라면, Z=88이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;X와 Y가 주어졌을 때, 형택이가 게임을 최소 몇 번 더 해야 Z가 변하는지 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;문제가 너무... 이분탐색 냄새가 심하게 난다. 바로 그냥 작성해보았고 당연히도 큰 범위에는 이분탐색이 가능하다면 바로 박는게 정배인듯 하다.. 하핳&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;


using ll = long long;
ll x, y;

int main() {
    cin &amp;gt;&amp;gt; x &amp;gt;&amp;gt; y;
    
    ll left = 0;
    ll right = 1&apos;000&apos;000&apos;000;
    
    ll standard = (y*100 / x);
    
    ll result = -1;
    
    while(left &amp;lt;= right){
        ll pivot = (left + right) / 2;
        if((ll)(((pivot + y)*100 / (x+pivot))) &amp;gt; standard){
            result = pivot;
            right = pivot-1;
        }else{
            left = pivot+1;
        }
    }
    
    cout &amp;lt;&amp;lt; result;
   return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 행렬</title><link>https://compy07.github.io/Blog/posts/boj/1080/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1080/</guid><description>풀이 용병 - 1</description><pubDate>Sun, 16 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1080&quot;&gt;행렬&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;1 ≤ N, M ≤ 50&lt;/td&gt;
&lt;td&gt;Greedy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;먼저 문제를 읽고서 제가 준비한 시각화를 한번 영상으로 확인해 봅시다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/SQnY4yyWFFc?si=angZT5clm0p4Q3LZ&quot; title=&quot;행렬&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;지금보면 어떤 알고리즘으로 가느냐.. 바로 3*3에서 제가 보고있는 것은 바로
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./board_1.png&quot; alt=&quot;example1&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;이 좌상단 입니다. 여기서 &lt;strong&gt;→, ↓&lt;/strong&gt; 을 우선순위로 오른쪽으로 갈 수 있다면 즉 m(가로)가 현재 위치보다 2이상 크다면 한번 검사를 하는데 현재 자기 자신 위치가 정답 행렬과 다르다면 반전 시킵니다.&lt;/p&gt;
&lt;p&gt;왜 이렇게 할까요??&lt;/p&gt;
&lt;p&gt;이유는 일단 탐색 방향이 절대로 왼쪽이나 위로 올라가지 않고 딱 한번 보고서 처리를 진행할 겁니다. 그때 정답을 맞추려면 하나도 틀리면 안되기에 내가 있는 위치는 이제 마지막으로 보는 순서겠죠? 지금 보고있다면 말이죠.&lt;/p&gt;
&lt;p&gt;그래서 내가 다르면 일단 반전을 시켜야 정답에 가까워질 수 있습니다. 만약 &quot;나는 한번에 처리해아한다고 생각해! 왜냐하면 문제에서 최소로 반전시킨 횟수를 반환해야 하니까!&quot;&lt;/p&gt;
&lt;p&gt;뭔가 한번에 처리될 수 있는 경우는 이미 위에서 내려오면서 됐을 겁니다. 만약 위에서 또 변경될 값이 있다면 다시 반전되겠지만, 무조건 우리는 될 수 있다에 가까워져야 되기 때문에 이렇게 반전을 하는거구요. 이 과정에서 이 경우가 최소가 될 수 밖에 없게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
using ll = long long;

int board[51][51];
int answer[51][51];
int n, m;

void change(int y, int x){
    for(int i = y; i &amp;lt; y+3; i++){
        for(int j = x; j &amp;lt; x+3; j++){

            board[i][j] = !board[i][j];
        }
    }
}


bool check(){

    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0;  j &amp;lt; m; j++){
            if(board[i][j] != answer[i][j]) return false;
        }}

    return true;
    }

    

int solve() {
   int result = 0;

    for(int i = 0; i &amp;lt; n-2; i++){
        for(int j = 0; j &amp;lt; m-2; j++){
        
            if(check()) return result;
            if(board[i][j] == answer[i][j])
                continue;
            change(i, j);
            result++;
        }}

   if(check()) return result;
   else return -1;
   
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    char tmp;
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m; j++){

            cin &amp;gt;&amp;gt; tmp;
            board[i][j] = tmp - &apos;0&apos;;
        }}

    
    for(int i = 0; i &amp;lt; n; i++){
        
        for(int j = 0; j &amp;lt; m; j++){
            cin &amp;gt;&amp;gt; tmp;
            answer[i][j] = tmp - &apos;0&apos;;
        }}

    cout &amp;lt;&amp;lt; solve();
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 팰린드롬?</title><link>https://compy07.github.io/Blog/posts/boj/10942/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/10942/</guid><description>재활 9일차 기초적인 DP 문제</description><pubDate>Tue, 11 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/10942&quot;&gt;팰린드롬?&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.5s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1≤ N ≤ 2,000&amp;lt;br/&amp;gt;1 ≤ M ≤ 1,000,000)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;일단 문제 자체가 최적화를 좀 해야된다는 생각을 만들게 하는 TimeLimit였다. 그런데 점화식 세우는게 어려운 문제가 아니였고,
사실 디게 직관적인 문제여서 금방 풀었던 것 같다.&lt;/p&gt;
&lt;p&gt;문제의 경우 그냥 자기 자신, 그리고 바로 2개씩 묶을 때 가능한 친구들 전처리하고, 이후에 바로 다 미리 전부 얻어둔 후에 입력으로
dp array에서 바로 빼오면 되는 문제다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

using ll = long long;
int n, m;


int board[2001];
bool dp[2001][2001];

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    cin &amp;gt;&amp;gt; n;
    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; board[i];
    
    
    // 2개씩 미리 해놓고, 다음에 차차 bottom up으로 처리하면 자동으로 다 dp 처리됨.
    
    for(int i = 1; i &amp;lt; n; i++){
        dp[i-1][i] = board[i] == board[i-1];
        dp[i-1][i-1] = 1;
        dp[i][i] = 1;
    }
    
    for(int i = 2; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; n-i; j++){
            if(board[j] != board[i+j]) continue;
            dp[j][j+i] = dp[j+1][j+i-1];
        }
    }
    
    
    cin &amp;gt;&amp;gt; m;
    int a, b;
    for(int i = 0; i &amp;lt; m; i++){
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;
        
        cout &amp;lt;&amp;lt; (dp[a-1][b-1] || dp[b-1][a-1]) &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    return 0;
}
// 1 2 1 3 1 2 1


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 행운쿠키 제작소</title><link>https://compy07.github.io/Blog/posts/boj/10982/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/10982/</guid><description>재활 12일차</description><pubDate>Mon, 06 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/10982&quot;&gt;행운쿠키 제작소&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 1,000)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;요즘 푸는 문제들이 전부 dp여서 좀 이제 다른거 풀긴 해야되는데... 랜디 중이여서 어쩔 수 없나봅니다.&lt;/p&gt;
&lt;p&gt;사실 몇 번 거르긴 했는데 수학은 저한테 너무 어려워요. 이상한 정수론 나오면 자력을 풀기가 너무 빡세서 넘기고 이러고 있어서..&lt;/p&gt;
&lt;p&gt;뭐 중간에 crypto 배운 짬바 어디갔나 싶지만.. ㄹㅇ 자력으로는 좀 힘들더라구요.&lt;/p&gt;
&lt;p&gt;하튼 이번 문제는 아이디어가 쉬운 편이었는데.. 생각해내는게 꽤 오래걸렸습니ㅏㄷ.&lt;/p&gt;
&lt;p&gt;포인트는 결국 순서는 아니고 그냥 점수에 포커스를 맞춰서 생각하면 금방 풀 수 있는 정도 인 것 같습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = unsigned long long;
using ld = long double;
//ll MOD = 1&apos;000&apos;000&apos;007;
//ll MOD = 1.8446744074E19;
int T;


ll dp[1001 * 101];
// 와 잠만 개크랙 생각남
// 그니까 사실 순서가 중요한게 아님 사실
// 그냥 점수가 중요한건데.. 지금보면
// 둘 중 하나를 선택하는 거자나..
// 이게 포인트야
// 하나를 다 먹었다고 가정을 해. 그담에 어떤걸 교체할건지를 정할거야.
// 근데 여기서 하나 더 나가자면.. 내가 가지고 있는 포인트도 알고, 바꿀 포인트를 알면 되는건데. 이때 한쪽 포인트가 I일 때 다른 오븐 포인트 j가 최소인 경우를 구해서
// 그거의 맥스를 찍으면 되는거구나 이렇게 하는게 아닐까?
//근데 구현을 이제 dp로 가능할거 같으넫

// dp 자체를 저 i에 대한걸로 하면 될듯?

ll solution(){
    int N;
    cin &amp;gt;&amp;gt; N;
    
    pair&amp;lt;int, int&amp;gt; lucks[1001];
    
    ll start = 0;
    for(int i = 0; i &amp;lt; N; i++){
        cin &amp;gt;&amp;gt; lucks[i].first &amp;gt;&amp;gt; lucks[i].second;
        start += lucks[i].first;
    }
    for(int i = 0; i &amp;lt; 101 * N; i++)
        dp[i] = LLONG_MAX;
    
    
    dp[start] = 0;
    
    
    for(int i = 0; i &amp;lt; N; i++){
        // 시간복잡도가 이게 맞는지 모르겠네..
        
        for(int j = 0; j &amp;lt; N * 101; j++){
            if(dp[j] == LLONG_MAX) continue;
            dp[j - lucks[i].first] = min(dp[j - lucks[i].first], dp[j] + lucks[i].second);
        }
    }
    
    ll result = LLONG_MAX;
    
    for(ll i = 0; i &amp;lt; 101 * N; i++)
        result = min(result, max(dp[i], i));
    
    
    return result;
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; T;
    
    while(T --&amp;gt; 0){
        cout &amp;lt;&amp;lt; solution() &amp;lt;&amp;lt; &quot;\n&quot;;
    }

    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 발전소</title><link>https://compy07.github.io/Blog/posts/boj/1102/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1102/</guid><description>재활 10일차</description><pubDate>Sat, 04 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1102&quot;&gt;발전소&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 16, 0 ≤ P ≤ N, 0 ≤ cost ≤ 36)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 간단한 bit masking dp였네용. 이제는 범위가 작기만하면 bit masking 생각나서 금방 접근하는거 같아요.
재밌었습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
using namespace std;
using ll = unsigned long long;
//ll MOD = 1&apos;000&apos;000&apos;007;
ll MOD = 1.8446744074E19;

int N;


int power_stations[20][20];


int dp[1 &amp;lt;&amp;lt; 16];
// 이것도 뭐 bit masking 때리면 되는 놈인데? 그러고 dp[i]는 그냥 코스트로 해보지 뭐

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; N;
    
    for(int i = 0; i &amp;lt; N; i++){
        for(int j = 0; j &amp;lt; N; j++)
            cin &amp;gt;&amp;gt; power_stations[i][j];
    }
    
    int state = 0;
    
    for(int i = 0; i &amp;lt; N; i++){
        char current;
        cin &amp;gt;&amp;gt; current;
        
        if(current == &apos;Y&apos;) state |= 1 &amp;lt;&amp;lt; i;
    }
    
    int p;
    cin &amp;gt;&amp;gt; p;
    
    for(int i = 0; i &amp;lt; 1 &amp;lt;&amp;lt; N; i++){
        dp[i] = 1000000000;
    }
    dp[state] = 0;
    
    for(int current = state; current &amp;lt; 1 &amp;lt;&amp;lt; N; current++){
        if(dp[current] == 1000000000) continue;
        for(int i = 0; i &amp;lt; N; i++){
            if(!(current &amp;amp; 1 &amp;lt;&amp;lt; i)) continue;
            for(int j = 0; j &amp;lt; N; j++){
                if(i == j) continue;
                dp[current | 1 &amp;lt;&amp;lt; j] = min(dp[current | 1 &amp;lt;&amp;lt; j], dp[current] + power_stations[i][j]);
            }
        }
        
    }
    
    int result = 1000000000;
    
    for(int i = 0; i &amp;lt; 1 &amp;lt;&amp;lt; N; i++){
        int cnt = 0;
        
        if(dp[i] == 1000000000) continue;
        for(int j = 0; j &amp;lt; N; j++){
            cnt += (i &amp;amp; (1 &amp;lt;&amp;lt; j)) ? 1 : 0;
        }
        
        if(cnt &amp;gt;= p) result = min(result, dp[i]);
        
    }
    
    if(result == 1000000000) cout &amp;lt;&amp;lt; -1;
    else cout &amp;lt;&amp;lt; result;
    
    
    
    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 리모컨</title><link>https://compy07.github.io/Blog/posts/boj/1107/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1107/</guid><description>재활 12일차 - 2</description><pubDate>Fri, 14 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1107&quot;&gt;리모컨&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1 ≤ X ≤ 20)&lt;/td&gt;
&lt;td&gt;BruteForce&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;문제는 별로 어렵진 않았지만 약간 시간이 걸렸습니다. 왜냐! 제 IDE가 바뀌었긴 때문에!! Xcode에서 vim으로 아예 갈아탔어용!! 확실히 연습이 더 잘되더라구요.&lt;/p&gt;
&lt;p&gt;또 자동완성이 없으니까 타자도 다시 원래 상태로 돌아가는 그 느낌! 아주 좋습니다.&lt;/p&gt;
&lt;p&gt;어차피 그냥 브포 문제라서 요정도 코드는 그냥 까겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./vim.png&quot; alt=&quot;vim&quot; /&gt;&lt;/p&gt;
&lt;p&gt;즐거운 ps 되십숑!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
using ll = long long;

int n, m;
bool buttons[10];

ll solve(int depth, ll current) {
     if(depth &amp;gt; 6) return abs(current - n) + depth;
    ll result = 10000000000;
    if(depth) result = abs(n - current) + depth;
     for(int i = 0; i &amp;lt; 10; i++){
        if(buttons[i]) continue;
      
         result = min(result, solve(depth+1, current*10+i));
        }
     return result;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cin &amp;gt;&amp;gt; n;
    cin &amp;gt;&amp;gt; m;

    int tmp;
    for(int i = 0; i &amp;lt; m; i++){
       cin &amp;gt;&amp;gt; tmp;
       buttons[tmp] = true;
    }
    
    

    ll result = min(llabs(n - 100), solve(0, 0));
    cout &amp;lt;&amp;lt; result;
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 피보나치 수의 제곱의 합</title><link>https://compy07.github.io/Blog/posts/boj/11440/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/11440/</guid><description>재활 6일차</description><pubDate>Tue, 30 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11440&quot;&gt;피보나치 수의 제곱의 합&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 1,000,000,000,000,000,000)&lt;/td&gt;
&lt;td&gt;도가뉴 항등식, Math&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;간단한 문제 증명은 &lt;a href=&quot;https://compy07.github.io/Blog/posts/algorithms/math/docagnes_identity/&quot;&gt;여기&lt;/a&gt;에서
확인하시면 됩니다.&lt;/p&gt;
&lt;p&gt;도가뉴 항등식을 활용해서 큰 피보나치 구하면 되구요. 이제 제곱의 합을 어떻게 구할까?를 생각해보면 됩니다.&lt;/p&gt;
&lt;p&gt;제 의식은 흐름은 아래와 같습니다:
&lt;img src=&quot;./last.png&quot; alt=&quot;first&quot; /&gt;
&lt;img src=&quot;./first.png&quot; alt=&quot;last&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
using namespace std;
using ll = long long;
ll MOD = 1&apos;000&apos;000&apos;007;

map&amp;lt;ll, ll&amp;gt; dp;



ll solution(ll n){
    if(dp.find(n) != dp.end()) return dp[n];
    ll result;
    
    ll a, b;
    a = solution(n/2);
    b = solution(n/2+1+(-2 *!(n%2)));
    
    if(n%2) result = ((a * a)%MOD + (b * b)%MOD)%MOD;
    else result = ((((2*b)%MOD + a)%MOD)*a)%MOD;
    dp[n] = result;
    
    return result;
}




int main() {
    ll n;
    cin &amp;gt;&amp;gt; n;
    
    dp[0] = 0;
    dp[1] = 1;
    dp[2] = 1;
    dp[3] = 2;
    
    ll a = solution(n);
    ll b = solution(n+1);
    
    // 이게 F_n*F_(n+1)이여서
    
    
    cout &amp;lt;&amp;lt; ((a * b) % MOD);
    

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 피보나치 수 6</title><link>https://compy07.github.io/Blog/posts/boj/11444/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/11444/</guid><description>재활 12일차 근데 이거 쉬운게 아닌데요...ㅠㅜ</description><pubDate>Fri, 14 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11444&quot;&gt;피보나치 수 6&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1≤ N ≤ 1,000,000,000,000,000,000)&lt;/td&gt;
&lt;td&gt;Dynamic Programming, Math&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;음.. 솔직히 저한테는 좀 어려웠습니다. 이게 어떻게든 될거라는 생각을 가지고서, 수를 쭉 나열해보고 하나씩 해보려고 했는데 다행히도 수학적으로 뭔가 수를 줄일때 보통 2에 집중하죠?&lt;/p&gt;
&lt;p&gt;그냥 대충 이분탐색, 이분트리 등등 속도를 높이기 위해서 반반 나눠가면서 하는 것들이 생각나더라구요. 또 이제 피보나치 수는 덧셈으로 이뤄져있으니까 뭔가 중간에 곱셈으로 다 처리하는게 가능하지 않을까?&lt;/p&gt;
&lt;p&gt;라고 생각해서 열심히 해봤는데 처음에는 당연히 보이지도 않고, 정말 힘들었습니다. 수식으로 증명을 해보려고 했는데도 일단 뭐가 맞는지도 몰라요.&lt;/p&gt;
&lt;p&gt;세상에 진짜 그러다가 하나가 보이기 시작합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;n이 현재 피보나치 수의 순서일 때,

a = (n/2)
b = (n/2+1)

fibo(n) = fibo(a)^2 + fibo(b)^2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;요런게 보입니다. 그래서 와 진짜 이게 되누 ㅋㅋ&lt;/p&gt;
&lt;p&gt;이러고 바로 해보려고 했다가 갑자기 다시 머리가 띵합니다... 이걸로는 TO(Time out)을 피할 수 없을거다.. 라고 생각이 들었거든요.&lt;/p&gt;
&lt;p&gt;그러고 다시 에.. 열심히 규칙을 찾았습니다...&lt;/p&gt;
&lt;p&gt;진짜 힘들었습니다... 역시 수학은 열심히 해놓는게 제일 좋은 거 같아요. ps도 열심히 해야겠지만 일단 수학도 열심히 해야겠습니다. 아 물리도요!&lt;/p&gt;
&lt;p&gt;제가 어떤 일이 있었는지.. 한 도화지 1장 정도 사용했는데요. 그렇게 막 많이 사용하진 않았고, 보통 머리로 그려서 해봤씁니다.&lt;/p&gt;
&lt;p&gt;그 외로 이제 한번 방문한 곳은 아예 바로 저장해서 바로 반환하도록 하면 TO를 피할 수 있어요!&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;img src=&quot;./solve.jpeg&quot; alt=&quot;solution_paper&quot; /&gt;
(또 제가 악필이라 막 많이 적으면서 하는거는 머리가 아플때 주로 쓰고 풉니다.)&lt;/h2&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;
그리고 Xcode로 하니까 contains가 있다고 하고, local에서는 정말 잘 돌아갔는데 무슨 boj에서는 터져서 바꿨네요.
다들 조심하십쇼.. Xcode include 안된 친구들도 있다고 표시하고, 잘 돌아가니까... 우씌...ㅠㅜ&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
using ll = long long;
ll MOD = 1&apos;000&apos;000&apos;007;

map&amp;lt;ll, ll&amp;gt; dp;



ll solution(ll n){
    if(dp.find(n) != dp.end()) return dp[n];
    ll result;
    
    ll a, b;
    a = solution(n/2);
    b = solution(n/2+1+(-2 *!(n%2)));
    if(n%2) result = ((a * a)%MOD + (b * b)%MOD)%MOD;
    else result = ((((2*b)%MOD + a)%MOD)*a)%MOD;
    dp[n] = result;
    return result;
}

int main() {
    ll n;
    cin &amp;gt;&amp;gt; n;
    
    
    dp[0] = 0;
    dp[1] = 1;
    dp[2] = 1;
    dp[3] = 2;
    
    cout &amp;lt;&amp;lt; solution(n);
    
    

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 구간 합 구하기 5</title><link>https://compy07.github.io/Blog/posts/boj/11660/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/11660/</guid><description>재활 15일차</description><pubDate>Mon, 17 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11660&quot;&gt;구간 합 구하기 5&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 1024, 1 ≤ M ≤ 100,000)&lt;/td&gt;
&lt;td&gt;Dynamic Programming, 구간합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이번 문제는 그냥 구간합 구하는 문제로 처음보는 분들은 연습하기 좋은 문제인 것 같습니다.&lt;/p&gt;
&lt;p&gt;그냥 지나오면서 다 더하고, 나중에 전에 값을 빼서 값을 알아오는 방법으로 값을 얻을 수 있습니다. Indexing을 사용하면 속도가 빠르기 때문에 아주 많이 사용되더라구요.&lt;/p&gt;
&lt;p&gt;한번 연습해보시면 좋을 듯 합니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
using ll = long long;
int board[1025][1025];
int n, m;



int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cin&amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    board[1][0] = 0;
    for(int i = 1; i &amp;lt;= n; i++){
        cin &amp;gt;&amp;gt; board[1][i];
        if(i) board[1][i] += board[1][i-1];
    }
    for(int i = 2; i &amp;lt;= n; i++){
        board[i][0] = board[i-1][n];
        for(int j = 1; j &amp;lt;= n; j++){
            cin &amp;gt;&amp;gt; board[i][j];
            board[i][j] += board[i][j-1];
        }
    }
    
    
    int x1, y1, x2, y2;
    while(m--&amp;gt;0){
        
        cin &amp;gt;&amp;gt; x1 &amp;gt;&amp;gt; y1 &amp;gt;&amp;gt; x2 &amp;gt;&amp;gt; y2;
        
        ll result = 0;
        for(int x = x1; x &amp;lt;= x2; x++)
            result += board[x][y2] - board[x][y1-1];
        
        cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 트리의 지름</title><link>https://compy07.github.io/Blog/posts/boj/1167/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1167/</guid><description>DFS를 이용한 그래프 탐색</description><pubDate>Fri, 28 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1167&quot;&gt;트리의 지름&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;트리의 지름이란, 트리에서 임의의 두 점 사이의 거리 중 가장 긴 것을 말한다. 트리의 지름을 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;문제는 너무나 간단합니다. 사실 말할 필요도 없이, 그냥 주어진 cost만을 가지고 길이를 더해가면서 측정하면 끝이기 때문입니다.&lt;/p&gt;
&lt;p&gt;입력을 제외한 핵심 DFS 코드만 살펴보고 프스트를 마무리하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; tree[100&apos;001];
bool visited[100&apos;001];
int result = 0;

int solution(int current, int length_){
   
    
    int length = 0;
    int child_max = 0;
    int child_second = 0;
    
    visited[current] = true;
    
    for(pair&amp;lt;int ,int&amp;gt; next : tree[current]){
        if(visited[next.first]) continue;
        int result = solution(next.first, child_max) + next.second;
        if(child_max &amp;lt; result){
            length = child_max+result;
            child_second = child_max;
            child_max = result;
        }else if(child_second &amp;lt; result){
            length = child_max + result;
            child_second = result;
        }
    }
    
    result = max({child_max+child_second,
                result,
                child_max + length_});
    
    return child_max;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;전역 변수&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tree&lt;/li&gt;
&lt;li&gt;말 그대로 트리의 정보를 담는 배열&lt;/li&gt;
&lt;li&gt;visited
&lt;ul&gt;
&lt;li&gt;방문한 노드인지 확인하는 배열&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;result
&lt;ul&gt;
&lt;li&gt;출력할 결과값&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;파라미터&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;current
&lt;ul&gt;
&lt;li&gt;현재 자신의 노드 위치&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;length_
&lt;ul&gt;
&lt;li&gt;전에 있던 노드에서 제일 깊게 탐색되었던 길이&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;지역 변수&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;length
&lt;ul&gt;
&lt;li&gt;일단 선언해 두고서, 쓰지 않은 변수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;child_max
&lt;ul&gt;
&lt;li&gt;자식 중에서 제일 먼 거리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;chlid_second
&lt;ul&gt;
&lt;li&gt;자식 중에서 두번째로 먼 거리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 코드는 정말 직관적이라 설명이 필요 없을 듯하다.&lt;/p&gt;
&lt;p&gt;overflow도 조심할 필요가 없었기 때문에 쉽게 풀이하였다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MAX 2&apos;100&apos;000&apos;000
#define limit 10&apos;000&apos;000&apos;000
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
#include &amp;lt;math.h&amp;gt;
#include &amp;lt;random&amp;gt;
using namespace std;

using ull = unsigned long long;
using ll = long long;

int n;

vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; tree[100&apos;001];
bool visited[100&apos;001];



int result = 0;

int solution(int current, int length_){
   
    
    int length = 0;
    int child_max = 0;
    int child_second = 0;
    
    visited[current] = true;
    
    for(pair&amp;lt;int ,int&amp;gt; next : tree[current]){
        if(visited[next.first]) continue;
        int result = solution(next.first, child_max) + next.second;
        if(child_max &amp;lt; result){
            length = child_max+result;
            child_second = child_max;
            child_max = result;
        }else if(child_second &amp;lt; result){
            length = child_max + result;
            child_second = result;
        }
    }
    
    result = max({child_max+child_second, result, child_max + length_});
    
    return child_max;
}


int main(){
    cin &amp;gt;&amp;gt; n;
    
    
    int current_node;
    for(int i = 0; i &amp;lt; n; i++){
        cin &amp;gt;&amp;gt; current_node;
        
        int node = 0, cost_;
        while(node != -1){
            cin &amp;gt;&amp;gt; node;
            if(node == -1) break;
            cin &amp;gt;&amp;gt; cost_;
            
            tree[current_node].push_back({node, cost_});
            tree[node].push_back({current_node, cost_});
            
        }
    }
    
    solution(1, 0);
    cout &amp;lt;&amp;lt; result;
    
    
    
    
    return 0;
}
    
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 배달</title><link>https://compy07.github.io/Blog/posts/boj/1175/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1175/</guid><description>재활 2일차</description><pubDate>Wed, 24 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1175&quot;&gt;배달&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N, M ≤ 50)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;간단한 BFS 문제이고, 조금 특이한 점은 각 방향에 대해서 visited 처리 정도가 있겠습니다.&lt;/p&gt;
&lt;p&gt;저는 조금 비효율적으로 작성했는데.. 학교에서 간단히 풀다 보니 일단 풀자라는 마인드로 구현해서 저렇게 되었구요..&lt;/p&gt;
&lt;p&gt;조금 더 생각해보면 훨씬 효율적인 풀이가 가능할 듯 합니다.. 한번 BFS 변형 문제 연습으로 풀어보면 좋을 것 같네요.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
using namespace std;

using ll = long long;

int n, m;


char board[101][101];

int visited[101][101][4][3];

vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; cs;

struct info {
    pair&amp;lt;int, int&amp;gt; pos;
    int cost;
    int getCost;
    int visited;
    int dir;
};

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    pair&amp;lt;int, int&amp;gt; start;
    
    for(int i = 1; i &amp;lt;= n; i++){
        for(int j = 1; j &amp;lt;= m; j++){
            char tmp;
            cin &amp;gt;&amp;gt; tmp;
            
            if(tmp == &apos;S&apos;) {
                start = {i, j};
                board[i][j] = &apos;.&apos;;
                
            }
            else if(tmp == &apos;C&apos;){
                cs.push_back({i, j});
            }
            else{
                board[i][j] = tmp;
            }
            
            
            for(int k = 0; k &amp;lt; 4; k++){
                visited[i][j][k][0] = 1000000000;
                visited[i][j][k][1] = 1000000000;
                visited[i][j][k][2] = 1000000000;
            }
//            visited[i][j][2] = 1000000000;
        }
    }
    
    
//    visited[start.first][start.second][0] = 0;
    
    queue&amp;lt;info&amp;gt; q;
    q.push({start, 0, 0, 0, -1});
    int result = 1000000000;

    int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
    
    while(!q.empty()){
        info information = q.front(); q.pop();
        for(int i = 0; i &amp;lt; 4; i++){
            if(information.dir == i) continue;
            int nx = information.pos.second + dx[i];
            int ny = information.pos.first + dy[i];
            if(0 &amp;lt; ny &amp;amp;&amp;amp; ny &amp;lt;= n &amp;amp;&amp;amp;
               0 &amp;lt; nx &amp;amp;&amp;amp; nx &amp;lt;= m &amp;amp;&amp;amp; board[ny][nx] != &apos;#&apos;){
                
                int currentGet = information.getCost;
                int currentIdx = information.visited;
                for(int idx = 0; idx &amp;lt; cs.size(); idx++){
                    if(cs[idx].first == ny &amp;amp;&amp;amp; cs[idx].second == nx){
                        currentIdx = idx+1;
                        if(idx+1 != information.visited){
                            currentGet++;
                            break;
                        }
                    }
                }
//                cout &amp;lt;&amp;lt; ny &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; nx &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; currentGet &amp;lt;&amp;lt; &quot;\n&quot;;
                
                
                
                if(currentGet == 2){
                    result = min(result, information.cost + 1);
                    continue;
                }
                
                if(visited[ny][nx][i][currentIdx] &amp;gt; information.cost + 1) {
                    visited[ny][nx][i][currentIdx] = information.cost + 1;
                    
                    
                    
                    q.push({{ny, nx}, information.cost + 1, currentGet, currentIdx, i});
                }
                
                
            }
            
        }
        
        
    }
    
    
//    
//    for(int i = 1; i &amp;lt;= n; i++){
//        for(int j = 1; j &amp;lt;= m; j++){
//            cout &amp;lt;&amp;lt; visited[i][j][1][1] &amp;lt;&amp;lt; &quot; &quot;;
////            result = min({visited[i][j][0][2], visited[i][j][1][2],
////                        visited[i][j][2][2], visited[i][j][3][2], result});
//        }
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//    }
    if(result == 1000000000) cout &amp;lt;&amp;lt; -1;
    else cout &amp;lt;&amp;lt; result;

    
    return 0;
}



/*
 
 3 9
 ...#..##.
 C...S..C.
 .#...#.#.
 
 
 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 부분수열의 합 2</title><link>https://compy07.github.io/Blog/posts/boj/1208/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1208/</guid><description>재활..</description><pubDate>Thu, 20 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1208&quot;&gt;부분수열의 합 2&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 40, 1 &amp;lt;= |S| &amp;lt;= 1,000,000)&lt;/td&gt;
&lt;td&gt;BinarySearch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이거는 아이디어가 중요한데.. 그냥 반반 쪼개서 2^20 * 2로 만들어서 생각하면 됩니다&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include&amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
using ll = long long;

int inf = 1e9;
int nums[41];

vector&amp;lt;ll&amp;gt; A, B;

void getSum(int current, int end, int current_sum, vector&amp;lt;ll&amp;gt;&amp;amp; container){
    container.push_back(current_sum);
    for(int i = current+1; i &amp;lt; end; i++)
        getSum(i, end, current_sum+nums[i], container);
    
}




void solution(){
    int n, s;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; s;
    
    ll result = 0;
    
    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; nums[i];
    
    
    if(n == 1){
        cout &amp;lt;&amp;lt; (s == nums[0]);
        return;
    }
    
    
    getSum(0, n/2, 0, A);
    getSum(0, n/2, nums[0], A);
    getSum(n/2, n, 0, B);
    getSum(n/2, n, nums[n/2], B);

    
    sort(A.begin(), A.end());
    A.erase(--upper_bound(A.begin(), A.end(), 0));
//    cout &amp;lt;&amp;lt; A.size() &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; B.size() &amp;lt;&amp;lt; &quot;\n&quot;;
    
    
    
    
    for(int i = 0; i &amp;lt; B.size(); i++){
        ll current = B[i];
        if(B[i] == s) result++;
        if(*lower_bound(A.begin(), A.end(), s - current) + current != s) continue;
        
        ll idx = upper_bound(A.begin(), A.end(), s - current) - A.begin();
        idx -= lower_bound(A.begin(), A.end(), s - current) - A.begin();
        
        result += idx;
    }
    
    
    cout &amp;lt;&amp;lt; result - (s == 0);
    
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    solution();
    return 0;
}



// -3 -2 -1 -3 -4 -5 -6 0
// 0 1 2 3 5 4 3 3 2 1 6 6

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 2048(Easy)</title><link>https://compy07.github.io/Blog/posts/boj/12100/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/12100/</guid><description>재활 11일차</description><pubDate>Thu, 13 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12100&quot;&gt;2048(Easy)&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1≤ N ≤ 20)&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;문제는 굉장히 재미있게 풀었습니다. 뭐 깊이 생각할게 없었던 것이 좀 아쉬웠지만, 그래도 게임을 구현하는 느낌으로 재미있게 풀었던 것 같습니다.&lt;/p&gt;
&lt;p&gt;저는 단순히 보드를 회전시키면서 한 방향으로 밀어버리기를 선택해서 풀었습니다.&lt;/p&gt;
&lt;p&gt;뭐 구현은 간단하기 때문에 딱히 설명은 필요없을 것 같네요.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
int n;

vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; rotate(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; board){
    
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; current(n, vector&amp;lt;int&amp;gt;(n));
    for (int i = 0; i &amp;lt; n; i++) {
        for (int j = 0; j &amp;lt; n; j++) {
            current[j][n-1-i] = board[i][j];
        }
    }
    return current;
}

vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; play(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; board){
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; current(n, vector&amp;lt;int&amp;gt;(n, 0));

    
    // 일단 다 당겨보자 한번에 해봐? 근데 위로 올리는거임

    for(int i = 0; i &amp;lt; n; i++){ // x
        int currentPos = 0; // current에 대한 현재 위치값.
        current[0][i] = board[0][i];
        for(int j = 1; j &amp;lt; n; j++){ // y
            int currentCost = current[currentPos][i];
            if(board[j][i] != 0){
                if(currentCost == 0) current[currentPos][i] = board[j][i];
                else{
                    if(currentCost == board[j][i]) current[currentPos][i]*=2;
                    else current[currentPos+1][i] = board[j][i];
                    currentPos++;
                }
            }
        }
    }

    return current;
}

int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; board, int depth){
    int result = 0;
    for(int i = 0; i &amp;lt; n*n; i++) result = max(result, board[i/n][i%n]);
    if(depth &amp;gt; 5) return result;
    
    
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; origin = board;
    
    for(int i = 0; i &amp;lt; 4; i++){
        board= play(origin);
        
        if(board != origin)
            result = max(result, solution(board, depth+1));
        
        origin = rotate(origin);
    }
    return result;
}


int main() {
    cin &amp;gt;&amp;gt; n;
    
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; board(n, vector&amp;lt;int&amp;gt;(n));
    for (int i = 0; i &amp;lt; n; i++) {
        for (int j = 0; j &amp;lt; n; j++) cin &amp;gt;&amp;gt; board[i][j];
    }
    
    cout &amp;lt;&amp;lt; solution(board, 1);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 쿨한 물건 구매</title><link>https://compy07.github.io/Blog/posts/boj/1214/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1214/</guid><description>재활 5일차</description><pubDate>Thu, 10 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1214&quot;&gt;쿨한 물건 구매&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.5s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ D, P, Q ≤ 10^9)&lt;/td&gt;
&lt;td&gt;Implementation, Math&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;음.. 접근 방법은 보자마자 생각을 해냈지만.. 이 시간을 어떻게 줄일까 생각을 하다가 열심히 방정식 가지구 으쌰으쌰 하니까 조건을 알아내서 풀었습니다.&lt;/p&gt;
&lt;p&gt;기본적인 아이디어는 큰 수의 개수를 확정짓는 하나의 값 N을 찾는다고 한다면 N을 확정하는 순간 다른 화폐의 개수도 확정되겠죠?
이거 이용해서 구현하면 됩니다. 이때 시간 초과를 피하기 위해서 범위를 효율적으로 계산하는게 진짜 중요한데 큰 값을 기준으로 N을 늘려가면서 [d + 작은값] 보다 안 크도록 유지해주면 됩니다.&lt;/p&gt;
&lt;p&gt;$$&lt;/p&gt;
&lt;p&gt;D \leq (N - P)Q + (M + Q)P&lt;/p&gt;
&lt;p&gt;$$&lt;/p&gt;
&lt;p&gt;위 식에서 $$N \geq P$$인 것을 알 수 있다.
이후에 이제 $$N \geq P$$에서 최대의 $$P$$의 값을 가져오게 된다면 이후에 더 올리는 것은 무의미하다.
왜냐하면 결국 조정되면서 현재 조정하는 값 대신에 다른 값이 작아지고 결국 0부터 왔다면 다시 방문했던 값을 보기 때문에 반복된다.
그래서 우리는 $$N &amp;lt; P$$를 알 수 있고, $$\text{limit} = p$$가 되는 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;참고: https://www.acmicpc.net/board/view/96265 &amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;random&amp;gt;
#include &amp;lt;cmath&amp;gt;
using namespace std;
using ll = long long;

ll d, p, q;
ll gcd(ll a, ll b){
    while(b != 0){
        ll r = a % b;
        a = b;
        b = r;
    }
    return a;
}
ll lcm(ll a, ll b){ return a * b / gcd(a, b); }


int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; d &amp;gt;&amp;gt; p &amp;gt;&amp;gt; q;
    
    if(p == 1 || q == 1) {
        cout &amp;lt;&amp;lt; d;
        return 0;
    }
    
     
    ll a = max(p, q), b = min(p, q);
    
    p = a;
    q = b;
    
    if(p%q == 0){
        if((ll)(d / q) * q == d) cout &amp;lt;&amp;lt; d;
        else cout &amp;lt;&amp;lt; (ll)(d / q) * q + q;
        return 0;
    }
    
    // 일단 p, q가 1인 경우가 제외됨으로 1e9까지 돌 ㅑfor문이 지금 1e9 / 2가 되버림 잠시만 시간이 1e5/5임 이게 맞음? 와 잠시만
    if(q == 2){
        if(d % q == 0) cout &amp;lt;&amp;lt; d;
        else{
            if(p % 2 == 0) cout &amp;lt;&amp;lt; d + 1;
            else{
                if(d - q &amp;gt;= p) cout &amp;lt;&amp;lt; d;
                else cout &amp;lt;&amp;lt; d + 1;
            }
        }
        
        return 0;
    }
    ll limit = q;
    ll result = min((ll)(d / q) * q + q, (ll)(d / p) * p + p);
    
    // 100 3 6
    //
    bool check = false;
    for(ll i = 0; i &amp;lt; limit ; i++){
        
        ll current = p * i;
        if(current &amp;gt; d + q) break;

        current += (ll)(abs(d - current) / q) * q;

        current = min(current + q, abs(d - current) % p == 0? d : (ll)((abs(d - current) / p)+1) * p + current);
        
        result = min(current, result);
//        cout &amp;lt;&amp;lt;&quot;result: &quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot; current: &quot; &amp;lt;&amp;lt; current &amp;lt;&amp;lt; &quot;\n&quot;;
        if(result == d) break;
    }
    cout &amp;lt;&amp;lt; result;
    

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 육각수</title><link>https://compy07.github.io/Blog/posts/boj/1229/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1229/</guid><description>재활 1일차</description><pubDate>Sun, 15 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1229&quot;&gt;육각수&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 1&apos;000&apos;000&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 전형적인 dp 문제인데, 단순한 연산이여서 1초를 10억으로 보고서 처리하면 간단히 풀 수 있는 문제&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;random&amp;gt;

#define mod 1000000007LL
#define MAX 1&apos;000&apos;000LL
using namespace std;
using ll = long long;

ll n;

ll dp[MAX+1];
vector&amp;lt;ll&amp;gt; nums;


int main(){
    cin &amp;gt;&amp;gt; n;
    
    
    dp[0] = 0;
    dp[1] = 1;
    nums.push_back(1);
    for(int i = 2; i &amp;lt;= n; i++){
        if(i == (nums.size() + 1) * 2 + nums.size()*2 - 1 + nums.back()) {
            nums.push_back(i);
            dp[i] = 1;
        }
        else{
            dp[i] = i;
            for(ll num : nums) dp[i] = min(dp[i], dp[i - num] + 1);
        }
    }
    

    cout &amp;lt;&amp;lt; dp[n];
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 주식왕 동호</title><link>https://compy07.github.io/Blog/posts/boj/1231/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1231/</guid><description>진짜 다시 제대로 시작한다.</description><pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1231&quot;&gt;주식왕 동호&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ C ≤ 50, &amp;lt;br/&amp;gt;2 ≤ D ≤ 10&amp;lt;br/&amp;gt;1 ≤ M ≤ 200,000)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;솔직히 처음에 dp점화식이나 접근 방식을 제대로 못 잡아서 많은 고민을 했다. 처음에는 day와 stock을 선택한 것에 대해서
설정하는 건 줄 알았는데, 생각해보니 그냥 말도 안되는 식이었고 뭘 어떻게 해야될까 생각하다가 중복 구매와 더불어 한번에 처리할 수 있는 방식 자체를
생각해내니 그냥 유레카 그 자체였다.&lt;/p&gt;
&lt;p&gt;충분히 고민하고 푸는 문제는 오랜만이라 그냥 문제가 goat였다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = long long;
using ld = long double;
ll MOD = 1&apos;000&apos;000&apos;000 + 7;

using namespace std;


int c, d, m;


int stocks[51][11];


// dp table을 만들 때 며칠인지 데이터가 무조건 필요하다.
// day가 10까지고, stock이 50까지고,
// dp table이 가져야할 것이 뭘까?
// 그리고 어디서 상태 전이가 발생해서 이거를 점화식으로 나타낼 수 있는가?
// day, 어떤 stock을 몇 개 샀는지 사실 이거는 bottom-up으로 할거면 상관이 없긴한데.
// 이를 최대한 시간 안에 만들려면 어떤 것을 다 구매하거나 뭐 이런 것처럼 아예 하나를 선택할 때 바로 몇 개를 사야되는게 좋은지를 증명해야됨.
// 자체로 greedy로 가게되면 문제가 되는게 논리적인 측면에서 조금의 증명으로 조금 엄밀해질 필요가 있따.

// 깊게 들어가면서 남은 돈을 다 투자하도록 만드는 방법도 있음.
// 그냥 for문으로 해결할 수 있는 방법 없을까?
// 일단 3중까지는 가능할거 같은데 4승까ㅣㅈ도 가능하네


int dp[500&apos;001];




int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; c &amp;gt;&amp;gt; d &amp;gt;&amp;gt; m;
    
    for(int i = 0; i &amp;lt; c; i++){
        for(int j = 0; j &amp;lt; d; j++){
            cin &amp;gt;&amp;gt; stocks[i][j];
        }
    }
    
    // 아 각 날마다 가지고 있는 돈에 대한 dp로 가져와서 진행하면 되겠다
    // dp[i] = max(dp[i], dp[i- 아 싸발 일어났따
    // think!
    for(int i = 1; i &amp;lt; d; i++){
        for(int _ = 0; _ &amp;lt;= 500&apos;001; _++) dp[_] = 0;
        
        for(int j = 0; j &amp;lt; c; j++){
            for(int k = stocks[j][i-1]; k &amp;lt; m+1; k++){
                dp[k] = max(dp[k], dp[k - stocks[j][i-1]] + stocks[j][i] - stocks[j][i-1]);
                
            }
            
        }
        m+= dp[m];
    }
    
    
    cout &amp;lt;&amp;lt; m;
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 좋다</title><link>https://compy07.github.io/Blog/posts/boj/1253/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1253/</guid><description>재활 7일차 - 2</description><pubDate>Fri, 07 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1253&quot;&gt;좋다&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;1 ≤ N ≤ 2,000 &amp;lt;br/&amp;gt; abs(A_i) ≤ 1,000,000,000&lt;/td&gt;
&lt;td&gt;Sort, Two Pointer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;문제 자체가 전형적인 binary search를 하는거였고, 그냥 투 포인터로 자기 자신 예외처리 후 AC&lt;/p&gt;
&lt;p&gt;ll을 unsigned로 해놔서 오류를 못 찾았었다..&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;
int n;
ll board[2001];

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    cin &amp;gt;&amp;gt; n;
    for(int i = 0; i &amp;lt; n; i++){
        cin &amp;gt;&amp;gt; board[i];
    }
    
    sort(board, board+n);
    
    int result = 0;
    if(n &amp;lt;= 2) {
        cout &amp;lt;&amp;lt; 0;
        return 0;
    }
    
    for(int i = 0; i &amp;lt; n; i++){
        
        int right = n-1 == i ? n-2 : n-1;
        int left = i == 0;
        
        while(left &amp;lt; right){
            ll current = board[left] + board[right];
            if(board[i] == current){
                result ++;

                break;
            }
            else if(board[i] &amp;lt; current){
                if(i == right-1) right--;
                right--;
            }else{
                if(i == left+1) left++;
                left++;
            }
        }
    }
    cout &amp;lt;&amp;lt; result;
    
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 할 수 있다</title><link>https://compy07.github.io/Blog/posts/boj/1287/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1287/</guid><description>재활 2일차</description><pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1287&quot;&gt;할 수 있다&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Big Integer, Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;일단 이 문제는 그냥 구현 문제라서 어려울 것은 딱히 없으나..........&lt;/p&gt;
&lt;p&gt;범위에 대한 계산 때문에 python을 쓰는게 압도적으로 유리한 것으로 보인다.
cpp로 하려면 Big Integer를 구현해야되는 대참사가 일어나고... 시간을 버리게 되고....&lt;/p&gt;
&lt;p&gt;빼기 순서를 잘못 적어서 또 날리게 되고...
조심하도록 하자.&lt;/p&gt;
&lt;p&gt;조금 빡센 구현 문제는 역시! 귀찮다! 재밌다! 아이디어 문제만큼이나 구현 문제도 생각보다 재미있는 것들이 많으니
한번 다들 풀어보시길~~ 물론 이 문제는 풀지 마세요 별로 좋은거 같지는 않아여... 플딱이가 말하는거라 한귀로 흘리시길.....&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
using namespace std;

string input;
class BigInteger {
private:
    std::vector&amp;lt;int&amp;gt; digits;
    bool negative;
    
    void removeLeadingZeros() {
        while (digits.size() &amp;gt; 1 &amp;amp;&amp;amp; digits.back() == 0) {
            digits.pop_back();
        }
        if (digits.size() == 1 &amp;amp;&amp;amp; digits[0] == 0) {
            negative = false;
        }
    }
    
    bool absLess(const BigInteger&amp;amp; other) const {
        if (digits.size() != other.digits.size()) {
            return digits.size() &amp;lt; other.digits.size();
        }
        for (int i = digits.size() - 1; i &amp;gt;= 0; i--) {
            if (digits[i] != other.digits[i]) {
                return digits[i] &amp;lt; other.digits[i];
            }
        }
        return false;
    }
    
    BigInteger absAdd(const BigInteger&amp;amp; other) const {
        BigInteger result;
        result.digits.clear();
        int carry = 0;
        int maxSize = std::max(digits.size(), other.digits.size());
        
        for (int i = 0; i &amp;lt; maxSize || carry; i++) {
            int sum = carry;
            if (i &amp;lt; digits.size()) sum += digits[i];
            if (i &amp;lt; other.digits.size()) sum += other.digits[i];
            result.digits.push_back(sum % 10);
            carry = sum / 10;
        }
        return result;
    }
    
    BigInteger absSubtract(const BigInteger&amp;amp; other) const {
        BigInteger result;
        result.digits.clear();
        int borrow = 0;
        
        for (int i = 0; i &amp;lt; digits.size(); i++) {
            int diff = digits[i] - borrow;
            if (i &amp;lt; other.digits.size()) diff -= other.digits[i];
            
            if (diff &amp;lt; 0) {
                diff += 10;
                borrow = 1;
            } else {
                borrow = 0;
            }
            result.digits.push_back(diff);
        }
        result.removeLeadingZeros();
        return result;
    }

public:
    BigInteger() : negative(false) {
        digits.push_back(0);
    }
    
    BigInteger(long long num) {
        negative = num &amp;lt; 0;
        if (negative) num = -num;
        
        if (num == 0) {
            digits.push_back(0);
        } else {
            while (num &amp;gt; 0) {
                digits.push_back(num % 10);
                num /= 10;
            }
        }
    }
    
    BigInteger(const std::string&amp;amp; str) {
        negative = false;
        int start = 0;
        if (str[0] == &apos;-&apos;) {
            negative = true;
            start = 1;
        }
        
        for (int i = str.length() - 1; i &amp;gt;= start; i--) {
            digits.push_back(str[i] - &apos;0&apos;);
        }
        removeLeadingZeros();
    }
    
    BigInteger operator+(const BigInteger&amp;amp; other) const {
        if (negative == other.negative) {
            BigInteger result = absAdd(other);
            result.negative = negative;
            return result;
        } else {
            if (absLess(other)) {
                BigInteger result = other.absSubtract(*this);
                result.negative = other.negative;
                return result;
            } else {
                BigInteger result = absSubtract(other);
                result.negative = negative;
                return result;
            }
        }
    }
    
    BigInteger operator-(const BigInteger&amp;amp; other) const {
        if (negative != other.negative) {
            BigInteger result = absAdd(other);
            result.negative = negative;
            return result;
        } else {
            if (absLess(other)) {
                BigInteger result = other.absSubtract(*this);
                result.negative = !negative;
                return result;
            } else {
                BigInteger result = absSubtract(other);
                result.negative = negative;
                return result;
            }
        }
    }
    
    BigInteger operator*(const BigInteger&amp;amp; other) const {
        BigInteger result;
        result.digits.assign(digits.size() + other.digits.size(), 0);
        
        for (int i = 0; i &amp;lt; digits.size(); i++) {
            for (int j = 0; j &amp;lt; other.digits.size(); j++) {
                result.digits[i + j] += digits[i] * other.digits[j];
                if (result.digits[i + j] &amp;gt;= 10) {
                    result.digits[i + j + 1] += result.digits[i + j] / 10;
                    result.digits[i + j] %= 10;
                }
            }
        }
        
        result.negative = negative != other.negative;
        result.removeLeadingZeros();
        return result;
    }
    
    std::pair&amp;lt;BigInteger, BigInteger&amp;gt; divmod(const BigInteger&amp;amp; other) const {
        if (other.digits.size() == 1 &amp;amp;&amp;amp; other.digits[0] == 0) {
            throw std::runtime_error(&quot;Division by zero&quot;);
        }
        
        BigInteger dividend = *this;
        BigInteger divisor = other;
        dividend.negative = false;
        divisor.negative = false;
        
        if (dividend.absLess(divisor)) {
            return {BigInteger(0), *this};
        }
        
        BigInteger quotient;
        quotient.digits.clear();
        BigInteger remainder;
        remainder.digits.clear();
        remainder.digits.push_back(0);
        
        for (int i = dividend.digits.size() - 1; i &amp;gt;= 0; i--) {
            remainder = remainder * BigInteger(10) + BigInteger(dividend.digits[i]);
            
            int count = 0;
            while (!remainder.absLess(divisor)) {
                remainder = remainder - divisor;
                count++;
            }
            quotient.digits.push_back(count);
        }
        
        std::reverse(quotient.digits.begin(), quotient.digits.end());
        quotient.removeLeadingZeros();
        remainder.removeLeadingZeros();
        
        quotient.negative = negative != other.negative;
        remainder.negative = negative;
        
        return {quotient, remainder};
    }
    
    BigInteger operator/(const BigInteger&amp;amp; other) const {
        return divmod(other).first;
    }
    
    BigInteger operator%(const BigInteger&amp;amp; other) const {
        return divmod(other).second;
    }
    
    BigInteger&amp;amp; operator=(const BigInteger&amp;amp; other) {
        digits = other.digits;
        negative = other.negative;
        return *this;
    }
    
    bool operator==(const BigInteger&amp;amp; other) const {
        return negative == other.negative &amp;amp;&amp;amp; digits == other.digits;
    }
    
    bool operator!=(const BigInteger&amp;amp; other) const {
        return !(*this == other);
    }
    
    bool operator&amp;lt;(const BigInteger&amp;amp; other) const {
        if (negative != other.negative) {
            return negative;
        }
        if (negative) {
            return other.absLess(*this);
        } else {
            return absLess(other);
        }
    }
    
    bool operator&amp;gt;(const BigInteger&amp;amp; other) const {
        return other &amp;lt; *this;
    }
    
    bool operator&amp;lt;=(const BigInteger&amp;amp; other) const {
        return *this &amp;lt; other || *this == other;
    }
    
    bool operator&amp;gt;=(const BigInteger&amp;amp; other) const {
        return *this &amp;gt; other || *this == other;
    }
    
    std::string toString() const {
        std::string result;
        if (negative) result += &quot;-&quot;;
        for (int i = digits.size() - 1; i &amp;gt;= 0; i--) {
            result += (digits[i] + &apos;0&apos;);
        }
        return result;
    }
    
    friend std::ostream&amp;amp; operator&amp;lt;&amp;lt;(std::ostream&amp;amp; os, const BigInteger&amp;amp; bi) {
        os &amp;lt;&amp;lt; bi.toString();
        return os;
    }
};

using ll = BigInteger;

struct info {
    bool isNum;
    ll num;
    char exp;
};

ll getNumber(int idx){
    string result = &quot;&quot;;
    for(; idx &amp;lt; input.size() &amp;amp;&amp;amp; &apos;0&apos; &amp;lt;= input[idx] &amp;amp;&amp;amp; input[idx] &amp;lt;= &apos;9&apos;; idx++) result += input[idx];
    
    return ll(result);
}

ll solution(){
    
    
    
    return 0LL;
}


int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; input;
    
    vector&amp;lt;info&amp;gt; expr;
    bool flag = false;
    for(int i = 0; i &amp;lt; input.size(); i++){
        
        info currentInfo;
        
        if(&apos;0&apos; &amp;lt;= input[i] &amp;amp;&amp;amp; input[i] &amp;lt;= &apos;9&apos;){
            
            
            if(!expr.empty() &amp;amp;&amp;amp; expr.back().isNum) {
                if(flag) {
                    cout &amp;lt;&amp;lt; &quot;ROCK&quot;;
                    return 0;
                }
                continue;
            }
            currentInfo = {true, getNumber(i)};
            
            if(!expr.empty()){
                ll into;
                if(expr.back().exp == &apos;*&apos;){
                    expr.pop_back();
                    into = expr.back().num * currentInfo.num;
                    expr.pop_back();
                    currentInfo.num = into;
                }else if(expr.back().exp == &apos;/&apos;){
                    expr.pop_back();
                    if(currentInfo.num == ll(0)){
                        cout &amp;lt;&amp;lt; &quot;ROCK&quot;;
                        return 0;
                    }
                    into = expr.back().num / currentInfo.num;
                    expr.pop_back();
                    currentInfo.num = into;
                }
            }
        }
        else currentInfo = {false, 0, input[i]};
        if ((!expr.empty() &amp;amp;&amp;amp; !expr.back().isNum &amp;amp;&amp;amp; !currentInfo.isNum &amp;amp;&amp;amp;
                    expr.back().exp != &apos;)&apos; &amp;amp;&amp;amp; currentInfo.exp != &apos;(&apos;)
                   || (expr.empty() &amp;amp;&amp;amp; !currentInfo.isNum &amp;amp;&amp;amp; currentInfo.exp != &apos;(&apos;)) {
                   
            cout &amp;lt;&amp;lt; &quot;ROCK&quot;;
            return 0;
        }
        flag = false;
        expr.push_back(currentInfo);
        
        if(expr.back().isNum) continue;
        
        if(expr.back().exp != &apos;)&apos;) continue;
        flag = true;
        expr.pop_back();
        ll currentNumber = expr.back().num;
        expr.pop_back();
        
        
        while(expr.back().exp != &apos;(&apos;){
            char operation = expr.back().exp; expr.pop_back();
            ll otherNumber = expr.back().num; expr.pop_back();
            
            switch (operation) {
                case &apos;+&apos;:
                    currentNumber = otherNumber + currentNumber;
                    break;
                case &apos;-&apos;:
                    
                    currentNumber = otherNumber - currentNumber;
                default:
                    break;
            }
            
            if(expr.empty()) {
                cout &amp;lt;&amp;lt; &quot;ROCK&quot;;
                return 0;
            }
            
        }
        expr.pop_back();
        
        if(!expr.empty()){
            if(expr.back().isNum) {
                cout &amp;lt;&amp;lt; &quot;ROCK&quot;;
                return 0;
            }
            
            
            if(expr.back().exp != &apos;(&apos; &amp;amp;&amp;amp; (expr.back().exp == &apos;*&apos; || expr.back().exp == &apos;/&apos;)){
                char operation = expr.back().exp; expr.pop_back();
                ll otherNumber = expr.back().num; expr.pop_back();
                
                
                switch (operation) {
                    case &apos;+&apos;:
                        currentNumber = otherNumber + currentNumber;
                        break;
                    case &apos;-&apos;:
                        currentNumber = otherNumber - currentNumber;
                        break;
                    case &apos;*&apos;:
                        currentNumber = currentNumber * otherNumber;
                        break;
                    case &apos;/&apos;:
                        if(currentNumber == ll(0)){
                            cout &amp;lt;&amp;lt; &quot;ROCK&quot;;
                            return 0;
                        }
                        currentNumber = otherNumber / currentNumber;
                        break;
                    default:
                        break;
                }
            }
        }
        
        expr.push_back({true, currentNumber});
        
        
    }
    if(!expr.back().isNum) {
        cout &amp;lt;&amp;lt; &quot;ROCK&quot;;
        return 0;
    }
//    for(int i = 0; i &amp;lt; expr.size(); i++){
//        if(expr[i].isNum)
//            cout &amp;lt;&amp;lt; expr[i].num &amp;lt;&amp;lt; &quot; &quot;;
//        else
//            cout &amp;lt;&amp;lt; expr[i].exp &amp;lt;&amp;lt; &quot; &quot;;
//    }
    
//    cout &amp;lt;&amp;lt; &quot;\n&quot;;
    ll result = expr.back().num;
    expr.pop_back();
    
    
    
    
    while(!expr.empty()){
        
        if(expr.back().isNum) {
            cout &amp;lt;&amp;lt; &quot;ROCK&quot;;
            return 0;
        }
        
        char operation = expr.back().exp; expr.pop_back();
        if(!expr.back().isNum) {
            cout &amp;lt;&amp;lt; &quot;ROCK&quot;;
            return 0;
        }
        ll otherNumber = expr.back().num; expr.pop_back();
        
        
        if(!expr.empty()){
            if(expr.back().isNum){
                cout &amp;lt;&amp;lt; &quot;ROCK&quot;;
                return 0;
                
            }
            
            if(expr.back().exp == &apos;-&apos;){
                if(operation == &apos;+&apos;){
                    operation = &apos;+&apos;;
                    result = result - otherNumber;
                }else if(operation == &apos;-&apos;){
                    operation = &apos;-&apos;;
                    result = otherNumber + result;
                    
                }
                continue;
            }
            
        }
        
        
        switch (operation) {
            case &apos;+&apos;:
                result = otherNumber + result;
                break;
            case &apos;-&apos;:
//                cout &amp;lt;&amp;lt; otherNumber &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
                result = otherNumber - result;
                break;
            default:
                break;
        }
        
    }
    
    
    std::cout &amp;lt;&amp;lt; std::fixed &amp;lt;&amp;lt; std::setprecision(0) &amp;lt;&amp;lt; result;
    
    
    
    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 할 일 정하기 1</title><link>https://compy07.github.io/Blog/posts/boj/1311/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1311/</guid><description>재활 13일차</description><pubDate>Tue, 07 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1311&quot;&gt;할 일 정하기 1&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;1 ≤ N ≤ 20, 1 ≤ b ≤ 10,000&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 바로 풀었던 dp 중 하나인 것 같습니다. 냄새가 너무 심하게나서 바로 bit mask로 접근했던거 같구요.(아마 최근에 bit mask를 많이 풀어서 바로 했던거 같아요)&lt;/p&gt;
&lt;p&gt;재미있게 풀었던 문제였습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = unsigned long long;
using ld = long double;
//ll MOD = 1&apos;000&apos;000&apos;007;
ll MOD = 1&apos;000&apos;000&apos;000;
//ll MOD = 1.8446744074E19;

int N;

int costs[21][21];

ll dp[1 &amp;lt;&amp;lt; 20];

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; N;
    for(int i = 0; i &amp;lt; N; i++){
        for(int j = 0; j &amp;lt; N; j++) cin &amp;gt;&amp;gt; costs[i][j];
    }
    
    for(int i = 0; i &amp;lt; 1 &amp;lt;&amp;lt; 20; i++) dp[i] = LLONG_MAX;
    dp[0] = 0;
    for(int i = 1; i &amp;lt; 1 &amp;lt;&amp;lt; 20; i++){
        
        int idx = -1;
        for(int j = 0; j &amp;lt; 20; j++) idx += (i &amp;amp; (1 &amp;lt;&amp;lt; j)) ? 1 : 0;
        
        for(int j = 0; j &amp;lt; 20; j++){
            int current = (1 &amp;lt;&amp;lt; j);
            if(!(i &amp;amp; current)) continue;
            
            int next = i - current;
            
            dp[i] = min(dp[next] + costs[idx][j], dp[i]);
            
        }
    }
    
    
    cout &amp;lt;&amp;lt; dp[(1 &amp;lt;&amp;lt; N) - 1];
    
    


    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 고층 빌딩</title><link>https://compy07.github.io/Blog/posts/boj/1328/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1328/</guid><description>재활 11일차</description><pubDate>Sun, 05 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1328&quot;&gt;고층 빌딩&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 100, 1 ≤ L, R ≤ N)&lt;/td&gt;
&lt;td&gt;Math&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;오랜만에 수학 문제여서.. 약간? 고민의 시간을 가졌습니다. 최근에 그래도 문제를 풀면서 그냥
머릿속에서만 말고 블로그 쓰는김에 패드로 좀 끄적이고 있는데요.&lt;/p&gt;
&lt;p&gt;제 의식의 흐름을 보여드리겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./1.png&quot; alt=&quot;1&quot; /&gt;
&lt;img src=&quot;./2.png&quot; alt=&quot;2&quot; /&gt;
&lt;img src=&quot;./3.png&quot; alt=&quot;3&quot; /&gt;
&lt;img src=&quot;./4.png&quot; alt=&quot;4&quot; /&gt;
&lt;img src=&quot;./5.png&quot; alt=&quot;5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;이 방식으로&lt;/p&gt;
&lt;p&gt;f(n,l,r)=f(n−1,l−1,r)+f(n−1,l,r−1)+(n−2)f(n−1,l,r)&lt;/p&gt;
&lt;p&gt;이렇게 유도가 되더라구요. 그렇게 그냥 코드를 작성하면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
using namespace std;
using ll = unsigned long long;
ll MOD = 1&apos;000&apos;000&apos;007;
//ll MOD = 1.8446744074E19;

int N, L, R;

ll dp[101][101][101];

ll solution(ll n, ll l, ll r){
    ll result = 0;
    if (l &amp;lt;= 0 || r &amp;lt;= 0 || l + r &amp;gt; n + 1) return 0;
    if(n == 1) return (l == 1 &amp;amp;&amp;amp; r == 1);
    
    if(dp[n][l][r] != -1) return dp[n][l][r];
    
    // solution(n+1, r, l) = solution(n, r-1, l) + solution(n, r, l-1) + solution(n, r, l) * n
    result = ((solution(n-1, l, r-1)%MOD + solution(n-1, l-1, r)%MOD)%MOD + (solution(n-1, l, r) * (n-2))%MOD)%MOD;
    dp[n][l][r] = result;
    return result;
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; L &amp;gt;&amp;gt; R;
    
    for(int i = 0; i &amp;lt; 101; i++){
        for(int j = 0; j &amp;lt; 101; j++){
            
            for(int k = 0; k &amp;lt; 101; k++){
                dp[i][j][k] = -1;
            }
        }
    }
    
    
    cout &amp;lt;&amp;lt; solution(N, L, R);
    
    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 풀자</title><link>https://compy07.github.io/Blog/posts/boj/1332/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1332/</guid><description>bruteforce 근데 약간의 억울함을 곁들인...</description><pubDate>Thu, 03 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1332&quot;&gt;풀자&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;유진이의 선생님은 유진이에게 몇 개의 문제를 풀라고 주었다. 유진이는 반드시 문제 1번을 먼저 풀어야 한다. 만약에 A번 문제를 풀었을 때, 유진이는 A+1번 문제를 풀거나, A+1번 문제를 건너뛰고 A+2번 문제를 푸는 것도 가능하다. 따라서, 1, 3, 4, 6과 같이 문제를 푸는 것은 가능하지만, 1, 3, 5, 8과 같이 문제를 푸는 것은 불가능하다.&lt;/p&gt;
&lt;p&gt;유진이는 문제를 풀면서 흥미를 느낀다. 입력으로 주어지는 P배열은 유진이가 각 문제를 풀 때 느끼는 흥미도를 수치화 한 값이다.&lt;/p&gt;
&lt;p&gt;유진이의 선생님은 유진이의 흥미도가 특정 범위내에 들면 문제를 푸는 것을 중지시키려고 한다. 만약 유진이가 지금까지 푼 문제의 흥미도의 최댓값과, 최솟값의 차이가 V보다 크거나 같으면 문제를 푸는 것을 중지하게 된다. 만약 이런 일이 절대 일어나지 않으면, 유진이는 문제를 다 풀게 된다. 유진이가 풀어야 하는 최소 문제수를 출력하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;일단 간단한 조건이 있다. 문제를 딱 보는 순간 배열 P의 max값과 min값의 차이가 V보다 작으면 무조건 N을 출력하면 된다. 이 조건이 굉장히 중요하다.&amp;lt;br&amp;gt;
나는 그냥 bruteforce 다 돌고나서 없으면 N을 반환하게 해놨더니 시간초가가 빻 ㅠㅜㅜ&lt;/p&gt;
&lt;p&gt;이 조건을 처음에 넣고서, 그냥 재귀로 풀이하였따.&lt;/p&gt;
&lt;p&gt;0번째는 무조건 풀어야 하기에 처음은 무조건 풀고서, 그 후에는 약간의 memo를 넣어서 시간을 아끼게 해줬어용&lt;/p&gt;
&lt;p&gt;좀 예쁜코드는 아닌거 가타여&lt;/p&gt;
&lt;p&gt;모드 즐거운 ps 하세용&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MAX 2100000000
#define LIMIT 10&apos;000&apos;000
#include &amp;lt;iostream&amp;gt;
#include&amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
using namespace std;
using ll = long long;

int n, v;
vector&amp;lt;int&amp;gt; problems;
int costs[1001];
int visited[1001][1001];
int result = MAX;

int limit = MAX;

void solution(int current, int interest_min, int interest_max, int count){
    if(current &amp;gt;= n || result &amp;lt;= count || current &amp;gt; limit) return;
    
    if(visited[current][count] &amp;gt; interest_max - interest_min) return;
    
    visited[current][count] = interest_max - interest_min;

    if(interest_max - interest_min &amp;gt;= v){
        result = min(count, result);
        limit = min(limit, current);
        return;
    }
    
    solution(current+1, min(interest_min, problems[current+1]), max(interest_max, problems[current+1]), count+1);
    if(current &amp;lt; n - 2) solution(current+2, min(interest_min, problems[current+2]), max(interest_max, problems[current+2]), count+1);
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; v;
    
    
    problems.resize(n+1);
    
    int mymin = MAX, mymax = -1;
    for(int i = 0; i &amp;lt; n; i++){
        cin&amp;gt;&amp;gt; problems[i]; costs[i] = MAX;
        if(mymin &amp;gt; problems[i]) mymin = problems[i];
        if(mymax &amp;lt; problems[i]) mymax = problems[i];
        for(int j = 0; j &amp;lt; n; j++)
            visited[i][j] = -1;
    }
    
    if(mymax - mymin &amp;gt;= v) solution(0, problems[0], problems[0], 1);

    if(result == MAX) cout &amp;lt;&amp;lt; n;
    else cout &amp;lt;&amp;lt; result;
    
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 다음 팰린드롬 수</title><link>https://compy07.github.io/Blog/posts/boj/1334/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1334/</guid><description>구현이다 구현!</description><pubDate>Thu, 03 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1334&quot;&gt;다음 팰린드롬 수&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 &amp;lt;= N &amp;lt;= 50)&lt;/td&gt;
&lt;td&gt;Implementation(구현)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;팰린드롬 수는 앞으로 읽어도, 뒤로 읽어도 같은 숫자이다. 101, 4, 6666와 같은 숫자는 팰린드롬 수이고, 10, 564, 15452와 같은 숫자는 아니다.
어떤 수 N이 주어질 때, N보다 큰 팰린드롬 수 중에서 가장 작은 수를 출력한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;참 요런 문제들이 진짜 설명이 간단하다. 우리의 목적은 주어진 수보다 큰 팰린드롬 수 중 작은 수를 출력하면 된다.&lt;/p&gt;
&lt;p&gt;나는 이번 문제 풀면서 cpp string 함수들에 대해서 많이 알게되었다. 구현의 왕이 되기 위해서 더욱 단련하자....&lt;/p&gt;
&lt;p&gt;(지금 학교에서 하고있는 100일 챌린지 때문에 1일 1포스팅(백준 또는 드림핵)을 하느라 설명이 거의 없는 점 죄송합니둥...)&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

string solution(string num) {
    int n = num.length();
    string left = num.substr(0, (n + 1) / 2);
    string right = string(left.rbegin() + (n % 2), left.rend());

    if (left + right &amp;lt;= num) {
        int mid = (n - 1) / 2;
        bool allNines = true;
        for (int i = 0; i &amp;lt;= mid; i++) {
            if (num[i] != &apos;9&apos;) {
                allNines = false;
                break;
            }
        }

        if (allNines) {
            return &quot;1&quot; + string(n - 1, &apos;0&apos;) + &quot;1&quot;;
        }

        for (int i = mid; i &amp;gt;= 0; i--) {
            if (num[i] &amp;lt; &apos;9&apos;) {
                num[i]++;
                break;
            }
            num[i] = &apos;0&apos;;
        }

        left = num.substr(0, (n + 1) / 2);
    }

    right = string(left.rbegin() + (n % 2), left.rend());
    return left + right;
}

int main() {
    string N;
    cin &amp;gt;&amp;gt; N;
    cout &amp;lt;&amp;lt; solution(N) &amp;lt;&amp;lt; &quot;\n&quot;;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 구슬 탈출</title><link>https://compy07.github.io/Blog/posts/boj/13459/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/13459/</guid><description>재활 12일차</description><pubDate>Mon, 17 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/13459&quot;&gt;구슬 탈출&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(3 ≤ N, M ≤ 10)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;아니 솔브드 스트릭이 끊겼는데 뭔 오륜지 모르겠네요... ㅠㅜ&lt;/p&gt;
&lt;p&gt;하튼 구슬 탈출 시리즈는 그냥 우려먹기 인듯합니다&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

int inf = 1e9+7;

int n ,m;

char board[11][11];
bool visited[11][11][2]; // 0 is red, 1 is blue



pair&amp;lt;pair&amp;lt;int, int&amp;gt;, bool&amp;gt; move(int dx, int dy, int y, int x, pair&amp;lt;int, int&amp;gt; current){
    queue&amp;lt;pair&amp;lt;int ,int&amp;gt;&amp;gt; q;
    q.push({y, x});
    
    pair&amp;lt;int, int&amp;gt; last;
    
    bool check = false;
    
    while(!q.empty()){
        
        auto [cy, cx] = q.front(); q.pop();
        last= {cy, cx};
        
        if(board[cy][cx] == &apos;O&apos;) return {last, false};
        
        if(cy == current.first &amp;amp;&amp;amp; cx == current.second) check = true;
        
        int nx = cx + dx;
        int ny = cy + dy;
        
        if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; board[ny][nx] != &apos;#&apos;) q.push({ny, nx});
        
    }
    
    last.first += dy * -1 *  check;
    last.second += dx * -1 *  check;
    
    
    return {last, check};
    
}

int solution(pair&amp;lt;int, int&amp;gt; red_ball, pair&amp;lt;int, int&amp;gt; blue_ball){
    
    queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; balls;
    
    
    int dy[] = {1, -1, 0, 0}, dx[] = {0, 0, 1, -1};
    
    
    // 근데 뭐가 먼저 움직이는가.. 이게 중요하거든 blue 부터
    
    balls.push(blue_ball);
    balls.push(red_ball);
    visited[red_ball.first][red_ball.second][0] = true;
    visited[blue_ball.first][blue_ball.second][1] = true;
    
    
    
    int step = 0;
    while(++step &amp;lt;= 10){
        int length = balls.size();
        
        
        bool hole_check = false;
        
        vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; update;
        
        while(length &amp;gt; 0){
            pair&amp;lt;int, int&amp;gt; blue = balls.front(); balls.pop();
            pair&amp;lt;int, int&amp;gt; red = balls.front(); balls.pop();

            for(int i = 0; i &amp;lt; 4; i++){
                auto [blue_move, blue_check] = move(dx[i], dy[i], blue.first, blue.second, red);
                
                if(board[blue_move.first][blue_move.second] == &apos;O&apos;) continue;
                
                auto [red_move, red_check] = move(dx[i], dy[i], red.first, red.second, blue);
                
                if(board[red_move.first][red_move.second] == &apos;O&apos;) return step;
                
                balls.push(blue_move);
                balls.push(red_move);
            }
            length-=2;
        }
        
//
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//        for(int i = 0; i &amp;lt; n; i++){
//            for(int j = 0; j &amp;lt; m; j++){
//                cout &amp;lt;&amp;lt; visited[i][j][0] &amp;lt;&amp;lt; &quot; &quot;;
//            }
//            cout &amp;lt;&amp;lt; &quot;\n&quot;;
//        }
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    
    return -1;
    
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    
    pair&amp;lt;int, int&amp;gt; red_ball, blue_ball, hole;
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m; j++){
            cin &amp;gt;&amp;gt; board[i][j];
            
            if(board[i][j] == &apos;B&apos;) blue_ball = {i, j};
            else if(board[i][j] == &apos;R&apos;) red_ball = {i, j};
            else if(board[i][j] == &apos;O&apos;) hole = {i, j};
        }
    }
    // 그렇네.. 허헣 아 그러면
    
    bool check = solution(red_ball, blue_ball) != -1;
    cout &amp;lt;&amp;lt; check;
    
    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 구슬 탈출 2</title><link>https://compy07.github.io/Blog/posts/boj/13460/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/13460/</guid><description>재활 11일차 - 2</description><pubDate>Sun, 16 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/13460&quot;&gt;구슬 탈출 2&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(3 ≤ N, M ≤ 10)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이 문제는 공을 굴릴 때 어떻게 같은 방향으로 움직인 상태에서 공이 나가고 막히고 등등 이거를 처리하는 &quot;순서&quot;만 잘 캐치해서 구현하면 쉽게 풀리는 BFS 기본 유형인 듯 보입니다.&lt;/p&gt;
&lt;p&gt;그 순서만 잘 골라서 처리하면 바로 풀 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;처음에 다르게 짜다가 구조를 갈아 엎어서 이상한 주석이나 안 쓰는 것들이 좀 많네요.. 허헣..&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

int inf = 1e9+7;

int n ,m;

char board[11][11];
bool visited[11][11][2]; // 0 is red, 1 is blue



pair&amp;lt;pair&amp;lt;int, int&amp;gt;, bool&amp;gt; move(int dx, int dy, int y, int x, pair&amp;lt;int, int&amp;gt; current){
    queue&amp;lt;pair&amp;lt;int ,int&amp;gt;&amp;gt; q;
    q.push({y, x});
    
    pair&amp;lt;int, int&amp;gt; last;
    
    bool check = false;
    
    while(!q.empty()){
        
        auto [cy, cx] = q.front(); q.pop();
        last= {cy, cx};
        
        if(board[cy][cx] == &apos;O&apos;) return {last, false};
        
        if(cy == current.first &amp;amp;&amp;amp; cx == current.second) check = true;
        
        int nx = cx + dx;
        int ny = cy + dy;
        
        if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; board[ny][nx] != &apos;#&apos;) q.push({ny, nx});
        
    }
    
    last.first += dy * -1 *  check;
    last.second += dx * -1 *  check;
    
    
    return {last, check};
    
}

int solution(pair&amp;lt;int, int&amp;gt; red_ball, pair&amp;lt;int, int&amp;gt; blue_ball){
    
    queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; balls;
    
    
    int dy[] = {1, -1, 0, 0}, dx[] = {0, 0, 1, -1};
    
    
    // 근데 뭐가 먼저 움직이는가.. 이게 중요하거든 blue 부터
    
    balls.push(blue_ball);
    balls.push(red_ball);
    visited[red_ball.first][red_ball.second][0] = true;
    visited[blue_ball.first][blue_ball.second][1] = true;
    
    
    
    int step = 0;
    while(++step &amp;lt;= 10){
        int length = balls.size();
        
        
        bool hole_check = false;
        
        vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; update;
        
        while(length &amp;gt; 0){
            pair&amp;lt;int, int&amp;gt; blue = balls.front(); balls.pop();
            pair&amp;lt;int, int&amp;gt; red = balls.front(); balls.pop();

            for(int i = 0; i &amp;lt; 4; i++){
                auto [blue_move, blue_check] = move(dx[i], dy[i], blue.first, blue.second, red);
                
                if(board[blue_move.first][blue_move.second] == &apos;O&apos;) continue;
                
                auto [red_move, red_check] = move(dx[i], dy[i], red.first, red.second, blue);
                
                if(board[red_move.first][red_move.second] == &apos;O&apos;) return step;
                
                balls.push(blue_move);
                balls.push(red_move);
            }
            length-=2;
        }
        
//
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//        for(int i = 0; i &amp;lt; n; i++){
//            for(int j = 0; j &amp;lt; m; j++){
//                cout &amp;lt;&amp;lt; visited[i][j][0] &amp;lt;&amp;lt; &quot; &quot;;
//            }
//            cout &amp;lt;&amp;lt; &quot;\n&quot;;
//        }
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    
    return -1;
    
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    
    pair&amp;lt;int, int&amp;gt; red_ball, blue_ball, hole;
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m; j++){
            cin &amp;gt;&amp;gt; board[i][j];
            
            if(board[i][j] == &apos;B&apos;) blue_ball = {i, j};
            else if(board[i][j] == &apos;R&apos;) red_ball = {i, j};
            else if(board[i][j] == &apos;O&apos;) hole = {i, j};
        }
    }
    // 그렇네.. 허헣 아 그러면
    
    cout &amp;lt;&amp;lt; solution(red_ball, blue_ball);
    
    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 무한 수열 1</title><link>https://compy07.github.io/Blog/posts/boj/1351/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1351/</guid><description>1351 문제입니다.</description><pubDate>Sun, 13 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1351&quot;&gt;무한 수열 1&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 &amp;lt;= N &amp;lt;= 10¹², 2&amp;lt;= P, Q &amp;lt;= 10⁹)&lt;/td&gt;
&lt;td&gt;Dynamic Programming(DP)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;mtable columnalign=&quot;left&quot;&amp;gt;
&amp;lt;mtr&amp;gt;
&amp;lt;mtd&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;0&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;=&amp;lt;/mo&amp;gt;
&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;
&amp;lt;/mtd&amp;gt;
&amp;lt;/mtr&amp;gt;
&amp;lt;mtr&amp;gt;
&amp;lt;mtd&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;=&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;
&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;mo&amp;gt;⌊&amp;lt;/mo&amp;gt;
&amp;lt;mfrac&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mi&amp;gt;P&amp;lt;/mi&amp;gt;
&amp;lt;/mfrac&amp;gt;
&amp;lt;mo&amp;gt;⌋&amp;lt;/mo&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;
&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;mo&amp;gt;⌊&amp;lt;/mo&amp;gt;
&amp;lt;mfrac&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mi&amp;gt;Q&amp;lt;/mi&amp;gt;
&amp;lt;/mfrac&amp;gt;
&amp;lt;mo&amp;gt;⌋&amp;lt;/mo&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;/msub&amp;gt;
&amp;lt;mtext&amp;gt; &amp;lt;/mtext&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;≥&amp;lt;/mo&amp;gt;
&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;/mtd&amp;gt;
&amp;lt;/mtr&amp;gt;
&amp;lt;/mtable&amp;gt;
&amp;lt;/math&amp;gt;&lt;/p&gt;
&lt;p&gt;N, P와 Q가 주어질 때, AN을 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이것은 깊게 생각할 필요없이 그냥 map을 이용해서 풀이했습니다. 그런데 2차원 dp로도 풀 수 있겠더라구요.&lt;/p&gt;
&lt;p&gt;효율적으로 풀이하려면 map보다는 array를 사용하는게 맞는 것 같습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;

using ll = long long;
map&amp;lt;ll, ll&amp;gt; dp;
ll N, Q, P;
ll solution(ll n){
    if(dp.find(n) != dp.end()) return dp[n];
    
    ll result = solution((ll)(n/Q)) + solution((ll)(n/P));
    dp.insert({n, result});
    return result;
}

int main(){
    dp.insert({0, 1});
    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; Q &amp;gt;&amp;gt; P;
    
    cout &amp;lt;&amp;lt; solution(N);
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 합과 곱</title><link>https://compy07.github.io/Blog/posts/boj/1353/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1353/</guid><description>재활 8일차</description><pubDate>Mon, 10 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1353&quot;&gt;합과 곱&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ S, P ≤ 1,000,000,000)&lt;/td&gt;
&lt;td&gt;Math&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;아... 솔직히 이거 접근이 잘 안돼서 좀 힘들었습니다.. 사실 이거 산술기하까지는 갔는데 그걸 이렇게 발전시키는 아이디어를 바로 생각을 해내질 못해서
많이 묶여있었네요...&lt;/p&gt;
&lt;p&gt;사실 문제는 그렇게 어려운편은 아니지만 좀 흥미롭게 풀 수 있었던 것 같아요.&lt;/p&gt;
&lt;p&gt;저의 아이디어는 이제 &quot;리스트에는 정수가 아닌 수가 포함될 수도 있다.&quot;이러한 문장이 문제에 존재했기 때문에
S/n으로 균일하게 나눈 후에.. 이거를 곱했을 때.. P보다 크거나 같은 수를 찾으면 되겠구나 생각했습니다.&lt;/p&gt;
&lt;p&gt;이때 여기까지 오면서 산술기하를 사용한 것이구요.&lt;/p&gt;
&lt;p&gt;:::tip[산술기하]&lt;/p&gt;
&lt;p&gt;$$
\text{산술기하 평균 부등식(AM-GM Inequality)}
$$&lt;/p&gt;
&lt;p&gt;두 양수 $$a$$와 $$b$$에 대해, 산술평균은 기하평균보다 크거나 같다는 것을 증명해보겠습니다&lt;/p&gt;
&lt;p&gt;$$
\frac{a + b}{2} \geq \sqrt{ab}
$$&lt;/p&gt;
&lt;p&gt;그리고 등호는 $$a = b$$일 때만 성립합니다&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;$$
\text{보통 수학시간에 배우는 기본적인 산술기하 평균 부등식}
$$&lt;/p&gt;
&lt;p&gt;$$(a - b)^2 \geq 0$$이라는 사실에서 시작합니다. 이것은 모든 실수의 제곱이 음이 아니기 때문입니다&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
(a - b)^2 &amp;amp;\geq 0\
a^2 - 2ab + b^2 &amp;amp;\geq 0\
a^2 + 2ab + b^2 &amp;amp;\geq 4ab\
(a + b)^2 &amp;amp;\geq 4ab\
a + b &amp;amp;\geq 2\sqrt{ab}\
\frac{a + b}{2} &amp;amp;\geq \sqrt{ab}
\end{align}
$$&lt;/p&gt;
&lt;p&gt;등호는 $$a - b = 0$$, 즉 $$a = b$$일 때만 성립합니다&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;$$
\text{우리가 알아야될 일반화된 산술기하평균 부등식}
$$&lt;/p&gt;
&lt;p&gt;$$n$$개의 양수 $$a_1, a_2, \ldots, a_n$$에 대해서도 산술평균과 기하평균의 부등식이 성립!&lt;/p&gt;
&lt;p&gt;$$
\LARGE \frac{a_1 + a_2 + \cdots + a_n}{n} \geq \sqrt[n]{a_1 \cdot a_2 \cdot \ldots \cdot a_n}
$$
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;$$
\text{귀납법을 이용한 증명}
$$&lt;/p&gt;
&lt;p&gt;$$n = 2$$인 경우는 이미 증명했습니다. $$n = 2^k$$인 경우에 대해 귀납법을 사용할 수 있습니다&lt;/p&gt;
&lt;p&gt;$$n = 2^k$$일 때 부등식이 성립한다고 가정합시다. $$2^{k+1}$$개의 수 $$a_1, a_2, \ldots, a_{2^{k+1}}$$에 대해&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\frac{a_1 + \cdots + a_{2^{k+1}}}{2^{k+1}} &amp;amp;= \frac{1}{2}\left(\frac{a_1 + \cdots + a_{2^k}}{2^k} + \frac{a_{2^k+1} + \cdots + a_{2^{k+1}}}{2^k}\right)\
&amp;amp;\geq \frac{1}{2}\left(\sqrt[2^k]{a_1 \cdots a_{2^k}} + \sqrt[2^k]{a_{2^k+1} \cdots a_{2^{k+1}}}\right)\
&amp;amp;\geq \sqrt{\sqrt[2^k]{a_1 \cdots a_{2^k}} \cdot \sqrt[2^k]{a_{2^k+1} \cdots a_{2^{k+1}}}}\
&amp;amp;= \sqrt[2^{k+1}]{a_1 \cdots a_{2^{k+1}}}
\end{align}
$$&lt;/p&gt;
&lt;p&gt;여기서도 등호는 모든 $$a_i$$가 서로 같을 때만 성립합니다.
:::&lt;/p&gt;
&lt;p&gt;그래서 이렇게 산술기하평균 부등식을 통해서 문제를 풀 수 있구요..&lt;/p&gt;
&lt;p&gt;식을 구했다! 그르면 이제 어떻게 적용할 것이냐?&lt;/p&gt;
&lt;p&gt;각 원소가 있죠?
(S/n) &amp;gt;= sqrt(P, n)
-&amp;gt; (S/n)^n &amp;gt;= P
이렇게해서 n을 쭉 순회하면서 구하면 됩니다.&lt;/p&gt;
&lt;p&gt;그리고 1 1, 3 3과 같이 S와 P가 같은 경우는 1로 고정 출력하고 이후에는 그냥 2부터 S까지 쭉 순회하면 된답니다!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include&amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
using ll = long long;

int inf = 1e9;

double s, p;

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin &amp;gt;&amp;gt; s &amp;gt;&amp;gt; p;
    
    
    if(s == p){
        cout &amp;lt;&amp;lt; 1;
        return 0;
    }
    for(double n = 2; n &amp;lt;= s; n++){
        double element = s/n;
        
        double current = 1;
        double idx = 0;
        while(idx++&amp;lt;n) current*=element;
        
        if(current &amp;lt; p) continue;
        
        
        
        cout &amp;lt;&amp;lt; n;
        return 0;
    }
    cout &amp;lt;&amp;lt; -1;
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 무한 수열 2</title><link>https://compy07.github.io/Blog/posts/boj/1354/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1354/</guid><description>1354 문제입니다.</description><pubDate>Sun, 13 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1351&quot;&gt;무한 수열 2&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 &amp;lt;= N &amp;lt;= 10¹³, 2&amp;lt;= P, Q &amp;lt;= 10⁹,0&amp;lt;= X, Y &amp;lt;= 10⁹)&lt;/td&gt;
&lt;td&gt;Dynamic Programming(DP)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&amp;gt;
&amp;lt;mtable columnalign=&quot;left&quot;&amp;gt;
&amp;lt;mtr&amp;gt;
&amp;lt;mtd&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;=&amp;lt;/mo&amp;gt;
&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;
&amp;lt;mtext&amp;gt; &amp;lt;/mtext&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;≤&amp;lt;/mo&amp;gt;
&amp;lt;mn&amp;gt;0&amp;lt;/mn&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;/mtd&amp;gt;
&amp;lt;/mtr&amp;gt;
&amp;lt;mtr&amp;gt;
&amp;lt;mtd&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;=&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;
&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;mo&amp;gt;⌊&amp;lt;/mo&amp;gt;
&amp;lt;mfrac&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mi&amp;gt;P&amp;lt;/mi&amp;gt;
&amp;lt;/mfrac&amp;gt;
&amp;lt;mo&amp;gt;⌋&amp;lt;/mo&amp;gt;
&amp;lt;mo&amp;gt;-&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;X&amp;lt;/mi&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;
&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;mo&amp;gt;⌊&amp;lt;/mo&amp;gt;
&amp;lt;mfrac&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mi&amp;gt;Q&amp;lt;/mi&amp;gt;
&amp;lt;/mfrac&amp;gt;
&amp;lt;mo&amp;gt;⌋&amp;lt;/mo&amp;gt;
&amp;lt;mo&amp;gt;-&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;Y&amp;lt;/mi&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;/msub&amp;gt;
&amp;lt;mtext&amp;gt; &amp;lt;/mtext&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;≥&amp;lt;/mo&amp;gt;
&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;/mtd&amp;gt;
&amp;lt;/mtr&amp;gt;
&amp;lt;/mtable&amp;gt;
&amp;lt;/math&amp;gt;&lt;/p&gt;
&lt;p&gt;N, P, Q, X, Y가 주어질 때, AN을 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이건 1번에서 뭐 사실 똑같이 풀면 됩니다. 10초라서 디게 쉽게 접근할 수 있었고, 그냥 0이하일 때 1을 반환 하도록 하면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;map&amp;gt;

using namespace std;

using ll = long long;
map&amp;lt;ll, ll&amp;gt; dp;
ll N, Q, P, X, Y;
ll solution(ll n){
    if(dp.find(n) != dp.end()) return dp[n];
    if(n &amp;lt;= 0) return 1LL;
    ll result = solution((ll)(n/Q)-X) + solution((ll)(n/P)-Y);
    dp.insert({n, result});
    return result;
}

int main(){
    dp.insert({0, 1});
    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; Q &amp;gt;&amp;gt; P &amp;gt;&amp;gt; X &amp;gt;&amp;gt; Y;
    
    cout &amp;lt;&amp;lt; solution(N);
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 강의실 2</title><link>https://compy07.github.io/Blog/posts/boj/1379/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1379/</guid><pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1379&quot;&gt;강의실 2&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 100,000)&lt;/td&gt;
&lt;td&gt;Greedy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 priority_queue 써서 먼저 채워버리는 방식이면 되는데 티어가 priority_queue 때문에 뻥튀기 됨.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MOD 1000000
#define MAX 200000001
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
#include &amp;lt;math.h&amp;gt;
using ll = long long;
using namespace std;

int n;


struct info {
    int number;
    int start, finish;
};

vector&amp;lt;int&amp;gt; rooms;
int members[100001];


int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    auto cmp =[](const info&amp;amp; a, const info&amp;amp; b){
        if(a.start &amp;gt; b.start) return true;
        else if(a.start == b.start) return a.finish &amp;gt; b.finish;
        return false;
    };
    
    cin &amp;gt;&amp;gt; n;
    priority_queue&amp;lt;info, vector&amp;lt;info&amp;gt;, decltype(cmp)&amp;gt; q;
    // 오 이거 신기하네
    
    
    for(int i = 0; i &amp;lt; n; i++){
        int a, b, c;
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; c;
        info current = {a, b, c};
        
        q.push(current);
    }
    
    
    
    while(!q.empty()){
        info current = q.top(); q.pop();
        
        bool canIn= false;
        for(int i = 0; i &amp;lt; rooms.size(); i++){
            if(rooms[i]&amp;lt;= current.start){
                
                members[current.number] = i+1;
                
                canIn = true;
                rooms[i] = current.finish;
                break;
            }
        }
        
        if(!canIn){
            rooms.push_back(current.finish);
            members[current.number] = rooms.size();
        }
        
    }
    
    cout &amp;lt;&amp;lt; rooms.size() &amp;lt;&amp;lt;&quot;\n&quot;;
    
    for(int i = 1; i &amp;lt;= n; i++){
        cout &amp;lt;&amp;lt; members[i] &amp;lt;&amp;lt; &quot;\n&quot;;
    }
        
    
    
    return 0;
    
    
    
    

}

//

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 벌집</title><link>https://compy07.github.io/Blog/posts/boj/1385/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1385/</guid><description>재활 5일차</description><pubDate>Mon, 29 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1385&quot;&gt;벌집&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;1 ≤ a, b ≤ 1,000,000&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 간단히 구현하면 되는데... 벌집을 그래프로 만드는게 조금 어렵달까??&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
using namespace std;
using ll = long long;

int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }

vector&amp;lt;int&amp;gt; board[1&apos;500&apos;000];
pair&amp;lt;int, int&amp;gt; visited[1&apos;500&apos;000];


void connect(int a, int b){
    board[a].push_back(b);
    board[b].push_back(a);
}

void makeBoard(){
    // 1,000,000
    // 3n(n+1)이네
    int current = 1;
    int cycle = 0;
    for(int n = 1; current &amp;lt;= 1&apos;000&apos;001; n++){
        int pre = 3 * (n - 1) * n + 1;
        int end = 3 * n * (n+1) + 1;
        
        
//        connect(pre, end);
        
//        cout &amp;lt;&amp;lt; current &amp;lt;&amp;lt; &quot;\n&quot;;
//        
//        cout &amp;lt;&amp;lt; &quot;term: &quot; &amp;lt;&amp;lt; pre&amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; end &amp;lt;&amp;lt;&quot; &quot; &amp;lt;&amp;lt; 3 * (n - 2) * (n - 1) + 2&amp;lt;&amp;lt; &quot;\n\n&quot;;
        connect(current, current - 1);
        connect(current+1, end);
        if(3 * (n - 2) * (n-1) + 1 &amp;gt; 0){
            connect(current, 3 * (n - 2) * (n-1) + 1);
            connect(current+1, 3 * (n - 2) * (n-1) + 1+1);
        }
        
        
        int term = 1;
        for(current ++; current &amp;lt; end; current++){
            connect(current, current-1);
//            cout &amp;lt;&amp;lt; current &amp;lt;&amp;lt; &quot;\n&quot;;
//            
//            cout &amp;lt;&amp;lt; &quot;term: &quot; &amp;lt;&amp;lt; term&amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; pre &amp;lt;&amp;lt; &quot;\n\n&quot;;
            if(term &amp;gt; cycle){
                connect(current, pre);
                term = 1;
//                cout &amp;lt;&amp;lt; current &amp;lt;&amp;lt; &quot;\n&quot;;
                
            }else{
                connect(current, pre++);
                connect(current, pre);
                term++;
            }
            
            
            if(pre &amp;gt; 3 * (n - 1) * n + 1) pre = 3 * (n - 2) * (n-1) + 2;
        }
        
        
        
        cycle ++;
        
    }
    
    
    board[1].clear();
    for(int i = 2; i &amp;lt; 8; i++) board[1].push_back(i);
    
//    connect(8,  2); 여기 라인 대각선이 문제임
//    connect(19, 7);
}



vector&amp;lt;int&amp;gt; solution(int start, int finish){
    
    
    queue&amp;lt;int&amp;gt; q;
    
    for(int i = 1; i &amp;lt;= 1&apos;000&apos;001; i++){
        visited[i] = {1000000000, -1}; // cost, path
    }
    
//    cout &amp;lt;&amp;lt; &quot;check!&quot;;
    
    q.push(start);
    visited[start] = {0, -1};
    
    
    while(!q.empty()){
        int current = q.front(); q.pop();
        
        for(int next : board[current]){
            if(next &amp;gt;= 1&apos;000&apos;100) continue;
            if(visited[next].first &amp;lt;= visited[current].first + 1) continue;
            
            visited[next].first = visited[current].first + 1;
            visited[next].second = current;
            
            q.push(next);
        }
    }
    
    
    vector&amp;lt;int&amp;gt; result;
    int current = finish;
    while(visited[current].second != -1){
        result.push_back(current);
        current = visited[current].second;
    }
    result.push_back(start);
    
    reverse(result.begin(), result.end());
    return result;
}


int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    

    
    makeBoard();
    
    int a, b;
    cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;
    
//    for(int i : board[1]){
//        cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; &quot;;
//    }
    vector&amp;lt;int&amp;gt; result;
    result = solution(a, b);
    
    for(int i : result) cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; &quot;;
    
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 암호</title><link>https://compy07.github.io/Blog/posts/boj/1394/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1394/</guid><description>재활 21일차</description><pubDate>Mon, 24 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1394&quot;&gt;암호&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ pw ≤ 1,000,000&amp;lt;br/&amp;gt; 1 ≤ words ≤ 100)&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 나와있는 조건대로 구현하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
using namespace std;
using ll = long long;
string pw, words;

char seq[150];

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    
    cin &amp;gt;&amp;gt; words;
    cin &amp;gt;&amp;gt; pw;
    
    for(int i = 0; i &amp;lt; words.size(); i++){
        if(seq[words[i]] &amp;gt; 0) continue;
        
        seq[words[i]] = i+1;
    }
    
    ll cycle = words.size();
    ll result = seq[pw[pw.size() - 1]];
    
    for(int i = pw.size() - 2; i &amp;gt; -1; i--){
        result +=  seq[pw[i]] * cycle;
        result %= 900528;
        cycle *= words.size();
        cycle %= 900528;
    }
    
    cout &amp;lt;&amp;lt; result;
    
    return 0;
}



&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 미친 로봇</title><link>https://compy07.github.io/Blog/posts/boj/1405/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1405/</guid><description>풀이 용병 - 2</description><pubDate>Thu, 27 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1405&quot;&gt;미친 로봇&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 14)&lt;/td&gt;
&lt;td&gt;Implementation, Backtracking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;문제는 그냥 재미있었다.. ㅋㅋㅌㅎㅌㅋ&lt;/p&gt;
&lt;p&gt;일단 우리가 중요하게 생각할 것은 무엇보다도 오차를 줄이는 것.. 컴퓨터는 기본적으로 소수를 표현할 때 무한 소수나 엄청긴 숫자에 대해서 당연히 오차가 발생하는데
이런 것들을 어떻게 최대한 줄일까? 이런 아이디어만 있으면 쉽게 풀 수 있다.&lt;/p&gt;
&lt;p&gt;나는 곱하기로 처리하다가 마지막에만 나눠서 처리하는 방식을 사용했다.&lt;/p&gt;
&lt;p&gt;다행히 문제의 최대값 100^14 값이 1e28인데 double로 찍어보니 나와서 double로 맘편히 처리했다.&lt;/p&gt;
&lt;p&gt;이번에 cout의 출력 형식 맞추는 것도 새롭게 알아간다&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;


double probability[4];
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
int n;
bool visited[50][50];

double solution(int depth, pair&amp;lt;int, int&amp;gt; pos, double current){
    if(depth &amp;gt;= n) return current;
    
    double local_result = 0;
    
    for(int i = 0; i &amp;lt; 4; i++){
        int ny = dy[i] + pos.first;
        int nx = dx[i] + pos.second;
        
        if(visited[ny][nx] || probability[i] == 0) continue;
        visited[ny][nx] = true;
        
        local_result += solution(depth+1, {ny, nx}, current*probability[i]);
//        cout &amp;lt;&amp;lt; &quot;local : &quot; &amp;lt;&amp;lt; local_result &amp;lt;&amp;lt; &quot;\n&quot;;
        visited[ny][nx] = false;
    }
    
    return local_result;
}



int main() {
    ios_base::sync_with_stdio(0); cin.tie(0);
    
    // result = 1e28;
    cin &amp;gt;&amp;gt; n;
    for(int i = 0; i &amp;lt; 4; i++) cin &amp;gt;&amp;gt; probability[i];
    
    pair&amp;lt;int, int&amp;gt; start = {25, 25};
    
    
    
    double d = 1;
    int i = n;
    while(i --&amp;gt; 0) d*=100;
    visited[25][25] = true;
    double result = solution(0, start, 1);
    cout.precision(10);
    cout &amp;lt;&amp;lt; (double)(result / d);
    
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 출근 기록</title><link>https://compy07.github.io/Blog/posts/boj/14238/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/14238/</guid><description>다시 돌아왔습니다 시험이 끝나써여</description><pubDate>Fri, 09 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14238&quot;&gt;출근 기록&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(1 ≤ S ≤ 50)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;일단 골드 2에서 그냥 그리디로 접근하다가 머리 깡 당하고 다시 생각하다보니 dp인 것 같아서 풀긴 했는데요&lt;/p&gt;
&lt;p&gt;아직도 이해가 안되는건 골2가 왜 5차원 dp를 사용하는거죠? 뭐 더 간결하게 풀 수 있을지는 잘 모르겠지만
어허허허허허허허헣.. 쉽지 않네요....&lt;/p&gt;
&lt;p&gt;무슨 문제만 보면 솔루션이 바로 안 떠오르면 자연스럽게 dp로만 생각하게 되는데 이런 버릇도 좀 고치고 싶네요&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;random&amp;gt;

#define mod 1000000LL

using namespace std;
using ll = long long;
string n;

int counts[3];

bool dp[51][51][51][4][4]; // a, b, c, 전 번호, 전전 번호
char result[51];

bool solution(int a, int b, int c, int past, int pastpast){
    
    if(a &amp;lt; 1 &amp;amp;&amp;amp; b &amp;lt; 1 &amp;amp;&amp;amp; c &amp;lt; 1) return true;
    
    if(dp[a][b][c][past][pastpast]) return false;
    
    dp[a][b][c][past][pastpast] = true;
    
    
    result[a+b+c] = &apos;C&apos;;
    if(c &amp;gt; 0 &amp;amp;&amp;amp; !(pastpast == 3 || past == 3)){ // C가 없는 경우
        if(solution(a,b, c-1, 3, past)) return true;
    }
    
    result[a+b+c] = &apos;B&apos;;
    if(b &amp;gt; 0 &amp;amp;&amp;amp; past != 2){

        if(solution(a,b-1, c, 2, past)) return true;
    }
    
    
    result[a+b+c] = &apos;A&apos;;
    if(a &amp;gt; 0){
        if(solution(a-1, b, c, 1, past)) return true;
    }
 
    
    return false;
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; n;
    for(char i : n) counts[i-&apos;A&apos;]++;
    
    int a = counts[0], b = counts[1], c = counts[2];
    
    
    if(!solution(a, b, c, 0, 0)) cout &amp;lt;&amp;lt; -1;
    else{
        for(int i = 1; i &amp;lt;= n.size(); i++) cout &amp;lt;&amp;lt; result[i];
    }
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 나누어 질까</title><link>https://compy07.github.io/Blog/posts/boj/1441/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1441/</guid><description>생각만 하면 풀 수 있어요...</description><pubDate>Tue, 25 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1441&quot;&gt;나누어 질까&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= N &amp;lt;=18, 1 &amp;lt;= L, R &amp;lt;= 10^9)&lt;/td&gt;
&lt;td&gt;Math(수학)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;어떤 숫자 배열 A가 주어지면, L보다 크거나 같고, R보다 작거나 같은 자연수 중에, A에 속해있는 원소 중 적어도 하나로 나누어지는 수의 개수를 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;문제는 정말 간단하지만, 풀이를 생각하는데 꽤 많은 시간이 걸렸다.
일단 최소 공배수를 구하고, 이를 통해서 중복되는 것을 제외한 각 원소들의 배수들의 개수를 더하면 되는 문제입니다.&lt;/p&gt;
&lt;p&gt;이걸 어떻게 풀 수 있을까요??&lt;/p&gt;
&lt;p&gt;먼저 LCM(최소 공배수)를 구하는 방법은&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ll lcm(int a, int b){
    return (a*b) / gcd(a, b);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;gcd의 구현은 &quot;유클리드 호제법&quot;을 이용해서 최적의 방법으로 구현이 가능하다.
이후에 경우의 수를 다 더하면 되는데, 이걸 어떻게 &lt;strong&gt;시간초과를 피하고&lt;/strong&gt;, 정확하게 값을 출력할 수 있을까?
내가 해결한 방법의 힌트를 소개하겠다.&lt;/p&gt;
&lt;p&gt;함수 f는 파라미터로 들어오는 n의 l부터 r까지의 배수의 개수를 가져오는 함수라고 정의한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;f(n) = r/lcm(n) - l/lcm(n) + !(l%lcm(n))&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이렇게 표현할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;r &amp;gt; l이 언제나 성립&lt;/strong&gt;하기 때문에 문제가 없으며, !(l%f(n))은 나머지가 나오지 않는다면, 나누어 떨어지기 때문에 &quot;l 이상&quot;의 조건에 의해서 l까지 포함시켜 더한다.&lt;/p&gt;
&lt;p&gt;자 일단 이러한 함수를 가지고 하나의 문제를 풀어보겠다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;2 3 5라는 원소가 들어왔다고 치자.&lt;/p&gt;
&lt;p&gt;l이 1이고, r이 100이라고 가정하자.&lt;/p&gt;
&lt;p&gt;f(2) + f(3) + f(5)가 기본적으로 있을 것이고, 이제 겹치는 것들을 빼야한다.&lt;/p&gt;
&lt;p&gt;2, 3의 최소 공배수 &lt;strong&gt;f(6)을 빼고&lt;/strong&gt;,
2, 5의 최소 공배수 &lt;strong&gt;f(10)을 빼고&lt;/strong&gt;,
3, 5의 최소 공배수 &lt;strong&gt;f(15)를 뺀다&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;이렇게 뺄 때, 또 중복돼서 빼지는 수가 생기게 된다. 바로 2, 3, 5의 공배수가 3번 빠지기 때문에 개수 카운트가 전혀 안되는 것이다.
그래서 우리는 f(30)을 더해줄 것이다.&lt;/p&gt;
&lt;p&gt;이제 뭔가 규칙이 보이죠??&lt;/p&gt;
&lt;p&gt;바로 N개의 원소들로 공배수를 찾고서 빼거나 더할 때, 그 조건이 N개의 짝수, 홀수 여부라는 것을 알겠죠?&lt;/p&gt;
&lt;p&gt;이제는 바로 문제를 풀어보자.&lt;/p&gt;
&lt;h2&gt;코드 풀이&lt;/h2&gt;
&lt;h3&gt;유클리드 호제법&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;ull gcd(ull a, ull b){
    if(b == 0) return a;
    return gcd(b, a%b);
}

ull lcm(ull a, ull b){
    return (a*b) / gcd(a, b);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;solution&lt;/h3&gt;
&lt;p&gt;그냥 bruteforce로 돌면서 그 조합에 대한 모든 경우를 빼고, 더하기를 하였다.
cpp의 대한 코드를 이해할 수만 있다면, 코드 이해는 쉬울 것이라고 생각한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ll solution(int depth, vector&amp;lt;int&amp;gt; &amp;amp;current, int idx){
    ll result = 0;
    
    ull mylcm = 1;
    
    for(int num : current){
        mylcm = lcm(num, mylcm);
        if(mylcm &amp;gt; r) return result;
    }
    result+=(r/mylcm - l/mylcm + !(l%mylcm)) * (depth%2 ? -1 : 1);
    for(int i = idx+1; i &amp;lt; n; i++){
        current.push_back(nums[i]);
        result+= solution(depth+1, current, i);
        current.pop_back();
    }
    
    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Main&lt;/h3&gt;
&lt;p&gt;이또한 코드에 이해가 안되진 않을 것이라 판단했다.
위에서 설명한 그대로의 식을 사용하여 구현한 것이라, 딱히 문제가 되진 않는다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int main(){
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; l &amp;gt;&amp;gt; r;
    result_num = 0;
    vector&amp;lt;ull&amp;gt; real;
    for(int i = 0; i &amp;lt; n; i++){
        cin &amp;gt;&amp;gt; nums[i];
        result_num += r/nums[i] - l/nums[i] + !(l%nums[i]);
    }
    
    
    sort(nums, nums+n);
    
    vector&amp;lt;int&amp;gt; val;
    ll plus_value = 0;
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = i+1; j &amp;lt; n; j++){
            val.push_back(nums[i]);
            val.push_back(nums[j]);
            plus_value += solution(1, val, j);
            val.pop_back();
            val.pop_back();
        }
    }
    
    cout &amp;lt;&amp;lt;  result_num + plus_value;
    return 0;
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;구현에서 어려웠던 것은 처음 overflow를 고려하지 못한 점이 너무나 컷다 그것 때문에 많은 시간을 허비했는데
요즘따라 overflow에 많이 당하고 있는데도 계속 당하는걸 보니 아직도 부족한가 보다.&lt;/p&gt;
&lt;p&gt;여러분도 조심하셔요!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MAX 2&apos;100&apos;000&apos;000
#define limit 10&apos;000&apos;000&apos;000
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
#include &amp;lt;math.h&amp;gt;
#include &amp;lt;random&amp;gt;
using namespace std;

using ull = unsigned long long;
using ll = long long;


ull gcd(ull a, ull b){
    if(b == 0) return a;
    return gcd(b, a%b);
}

ull lcm(ull a, ull b){
    return (a*b) / gcd(a, b);
}

int nums[20];
ull result_num;
ll l, r;
int n;


ll solution(int depth, vector&amp;lt;int&amp;gt; &amp;amp;current, int idx){
    ll result = 0;
    
    
    ull mylcm = 1;
    for(int num : current){
        mylcm = lcm(num, mylcm);
        if(mylcm &amp;gt; r) return result;
    }
    result+=(r/mylcm - l/mylcm + !(l%mylcm)) * (depth%2 ? -1 : 1);
    for(int i = idx+1; i &amp;lt; n; i++){
        current.push_back(nums[i]);
        result+= solution(depth+1, current, i);
        current.pop_back();
    }
    
    
    return result;
}
bool visited[20];
int main(){
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; l &amp;gt;&amp;gt; r;
    result_num = 0;
    vector&amp;lt;ull&amp;gt; real;
    for(int i = 0; i &amp;lt; n; i++){
        cin &amp;gt;&amp;gt; nums[i];
        result_num += r/nums[i] - l/nums[i] + !(l%nums[i]);
    }
    
    
    sort(nums, nums+n);
    
    vector&amp;lt;int&amp;gt; val;
    ll plus_value = 0;
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = i+1; j &amp;lt; n; j++){
            val.push_back(nums[i]);
            val.push_back(nums[j]);
            plus_value += solution(1, val, j);
            val.pop_back();
            val.pop_back();
        }
    }
    
    cout &amp;lt;&amp;lt;  result_num + plus_value;
    return 0;
    
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 벽 부수고 이동하기 2</title><link>https://compy07.github.io/Blog/posts/boj/14442/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/14442/</guid><description>BFS 연습하기 좋은 문제인 거 같아요</description><pubDate>Fri, 11 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14442&quot;&gt;벽 부수고 이동하기 2&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= N, M &amp;lt;= 1000, 1 &amp;lt;= K &amp;lt;= 10)&lt;/td&gt;
&lt;td&gt;BFS(너비우선탐색)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로로 이동하려 한다. 최단경로는 맵에서 가장 적은 개수의 칸을 지나는 경로를 말하는데, 이때 시작하는 칸과 끝나는 칸도 포함해서 센다.&lt;/p&gt;
&lt;p&gt;만약에 이동하는 도중에 벽을 부수고 이동하는 것이 좀 더 경로가 짧아진다면, 벽을 K개 까지 부수고 이동하여도 된다.&lt;/p&gt;
&lt;p&gt;한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.&lt;/p&gt;
&lt;p&gt;맵이 주어졌을 때, 최단 경로를 구해 내는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그냥 BFS 문제임. 딱히 설명은 필요 없을 듯 합니다.&lt;/p&gt;
&lt;p&gt;아 이번에 structured binding을 사용하면서 좀 코드 스타일에 변화가 찾아왔습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;

int n, m, k;

int board[1001][1001];
int visited[1001][1001][11];
typedef struct pos{
    int y, x, k, c;
} pos;

bool visit_processing(pos p){
    if(p.k &amp;gt; 10) return true;
    for(int i = p.k; i &amp;gt; -1; i--)
        if(visited[p.y][p.x][i] &amp;lt;= p.c)
            return true;
        
    return false;
}

int main(){
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; k;
    char _;
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m; j++){
            cin &amp;gt;&amp;gt; _;
            board[i][j] = _ - &apos;0&apos;;
            for(int k_ = 0; k_ &amp;lt;= k; k_++)
                visited[i][j][k_] = 2100000000;
        }
    }
    
    queue&amp;lt;pos&amp;gt; q;
    q.push({0, 0, 0, 0});
    
    
    int dy[] = {1, -1, 0, 0};
    int dx[] = {0, 0, 1, -1};
    
    while(!q.empty()){
        pos p = q.front(); q.pop();
        
        if(visit_processing(p)) continue;
        auto [y, x, k, c] = p;
        visited[y][x][k] = c;
        for(int i = 0; i &amp;lt; 4; i++){
            int ny = y + dy[i];
            int nx = x + dx[i];
            
            if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m)
                q.push({y+dy[i], x + dx[i], k+(board[ny][nx]), c+1});
            
        }
    }
        
    
    int result = 2100000000;
    for(int i = 0; i &amp;lt;= k; i++) result = min(result, visited[n-1][m-1][i]);
    if(result == 2100000000) cout &amp;lt;&amp;lt; -1;
    else cout &amp;lt;&amp;lt; result+1;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 테트로미노</title><link>https://compy07.github.io/Blog/posts/boj/14500/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/14500/</guid><description>bruteforce로 14500 풀어보기</description><pubDate>Sun, 23 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1955&quot;&gt;테트로미노&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(4 ≤ N, M ≤ 500)&lt;/td&gt;
&lt;td&gt;Bruteforce(완전 탐색)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;정사각형은 서로 겹치면 안 된다.&lt;/li&gt;
&lt;li&gt;도형은 모두 연결되어 있어야 한다.&lt;/li&gt;
&lt;li&gt;정사각형의 변끼리 연결되어 있어야 한다. 즉, 꼭짓점과 꼭짓점만 맞닿아 있으면 안 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;정사각형 4개를 이어 붙인 폴리오미노는 테트로미노라고 하며, 다음과 같은 5가지가 있다.
&lt;img src=&quot;./img.png&quot; alt=&quot;tetrio&quot; /&gt;&lt;/p&gt;
&lt;p&gt;아름이는 크기가 N×M인 종이 위에 테트로미노 하나를 놓으려고 한다. 종이는 1×1 크기의 칸으로 나누어져 있으며, 각각의 칸에는 정수가 하나 쓰여 있다.
테트로미노 하나를 적절히 놓아서 테트로미노가 놓인 칸에 쓰여 있는 &lt;strong&gt;수들의 합을 최대&lt;/strong&gt;로 하는 프로그램을 작성하시오.
테트로미노는 반드시 한 정사각형이 정확히 하나의 칸을 포함하도록 놓아야 하며, &lt;strong&gt;회전이나 대칭을 시켜도 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;내가 한번 틀린 후 정답을 맞히게 되었는데, 내가 걸린 것은 바로 이 &quot;대칭&quot;을 제외하고, 회전만을 신경써서 틀리게되었다.
모두 &lt;strong&gt;회전, 대칭이 모두 가능&lt;/strong&gt; 하다는 것을 인지하고, 문제를 접근하자!&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이 문제는 딱히 dp나 다른 방법이 떠오르지 않았다. 문제의 시간 제한도 널널하고 조건 또한 그리 크지않아서
바로 완전 탐색을 돌려 해결하게 되었다.&lt;/p&gt;
&lt;p&gt;코드의 설명은 딱히 필요 없을 정도로 굉장히 간단하고, 직관적이라 생략하도록 하겠다.&lt;/p&gt;
&lt;p&gt;(현재 나는 go 언어 공부 중에 있어서, go 언어로 ps를 하는 중이다 -&amp;gt; go언어의 이해도를 높이기 위해서)&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;먼저 방법만 설명을 하자면,
T, O, L, I, Z 의 대칭, 회전의 모든 모양을 if 처리하여 bruteforce를 돌린 코드이다.
굉장히 간단하고 직관적인다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;Go 언어에서 지원하는 기본적인 fmt.Scanf()는 입력을 받는 속도가 느리기 때문에 ps에 활용할때는 시간 초과가 나기 쉽다고 합니다.
그래서 bufio, os에 있는 reader, writer을 선언하고, 입력을 빠르게 받을 수 있도록 Fscan을 통해서 입력을 받았습니다.&lt;/p&gt;
&lt;p&gt;다른 블로그를 찾아보니 딱히 reader, writer를 close 해줄 필요는 없는 것 같습니다(in ps)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import (
	&quot;bufio&quot;
	&quot;fmt&quot;
	&quot;os&quot;
)

var board [501][501]int
var n, m int

type pos struct {
	x int
	y int
}

func mymax(a, b int) int {
	if a &amp;gt; b {
		return a
	}
	return b
}

func solution(pos pos, cost int) int {

	if pos.x &amp;gt; m-1 {
		pos.x = 0
		pos.y += 1
		return solution(pos, cost)
	}
	if pos.y &amp;gt;= n {
		return cost
	}

	// L 4가지
	if pos.y &amp;lt; n-2 {
		if pos.x &amp;gt; 0 {
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x]+board[pos.y][pos.x-1]+board[pos.y+2][pos.x])
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x]+board[pos.y+2][pos.x-1]+board[pos.y+2][pos.x])
		}
		if pos.x &amp;lt; m-1 {
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x]+board[pos.y+2][pos.x+1]+board[pos.y+2][pos.x])
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x]+board[pos.y][pos.x+1]+board[pos.y+2][pos.x])

		}
	}
	if pos.x &amp;gt; 0 &amp;amp;&amp;amp; pos.x &amp;lt; m-1 {
		if pos.y &amp;gt; 0 {
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y-1][pos.x+1]+board[pos.y][pos.x+1]+board[pos.y][pos.x-1])
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y-1][pos.x-1]+board[pos.y][pos.x+1]+board[pos.y][pos.x-1])

		}
		if pos.y &amp;lt; n-1 {
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x-1]+board[pos.y][pos.x+1]+board[pos.y][pos.x-1])
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x+1]+board[pos.y][pos.x+1]+board[pos.y][pos.x-1])

		}
	}

	// I 2가지
	if pos.y &amp;gt; 2 { // 세로
		cost = mymax(cost, board[pos.y-3][pos.x]+board[pos.y-2][pos.x]+board[pos.y-1][pos.x]+board[pos.y][pos.x])
	}
	if pos.x &amp;lt; m-3 {
		cost = mymax(cost, board[pos.y][pos.x]+board[pos.y][pos.x+1]+board[pos.y][pos.x+2]+board[pos.y][pos.x+3])
	}
	// O 1가지
	if pos.x &amp;lt; m-1 &amp;amp;&amp;amp; pos.y &amp;lt; n-1 {
		cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x]+board[pos.y][pos.x+1]+board[pos.y+1][pos.x+1])
	}
	// Z 2가지
	if pos.y &amp;lt; n-2 {
		if pos.x &amp;lt; m-1 {
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x]+board[pos.y+1][pos.x+1]+board[pos.y+2][pos.x+1])
		}
		if pos.x &amp;gt; 0 {
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x]+board[pos.y+1][pos.x-1]+board[pos.y+2][pos.x-1])
		}
	}
	if pos.x &amp;lt; m-2 {
		if pos.y &amp;lt; n-1 {
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y][pos.x+1]+board[pos.y+1][pos.x+1]+board[pos.y+1][pos.x+2])
		}
		if pos.y &amp;gt; 0 {
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y][pos.x+1]+board[pos.y-1][pos.x+1]+board[pos.y-1][pos.x+2])
		}

	}
	// T 4가지
	if pos.x &amp;gt; 0 &amp;amp;&amp;amp; pos.x &amp;lt; m-1 {
		if pos.y &amp;lt; n-1 {
			// ㅜ
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x]+board[pos.y][pos.x+1]+board[pos.y][pos.x-1])
		}
		if pos.y &amp;gt; 0 {
			// ㅗ
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y][pos.x+1]+board[pos.y][pos.x-1]+board[pos.y-1][pos.x])
		}

	}
	if pos.y &amp;lt; n-2 {
		if pos.x &amp;gt; 0 {
			// ㅓ
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x]+board[pos.y+1][pos.x-1]+board[pos.y+2][pos.x])
		}
		if pos.x &amp;lt; m-1 {
			// ㅏ
			cost = mymax(cost, board[pos.y][pos.x]+board[pos.y+1][pos.x]+board[pos.y+1][pos.x+1]+board[pos.y+2][pos.x])

		}
	}

	pos.x += 1
	return solution(pos, cost)

}

func main() {
	// helo
	// fmt.Println(&quot;hello&quot;)
	var reader *bufio.Reader = bufio.NewReader(os.Stdin)
	var writer *bufio.Writer = bufio.NewWriter(os.Stdout)
	// defer: 이를 호출한 함수의 리턴 직전에 실행한다.
	// Flush: 모든 데이터가 writer에 보내졌음을 의미 (꼭 써줘야한다!)
	defer writer.Flush()

	fmt.Fscanln(reader, &amp;amp;n, &amp;amp;m)

	for i := 0; i &amp;lt; n; i++ {
		for j := 0; j &amp;lt; m; j++ {
			_, err := fmt.Fscan(reader, &amp;amp;board[i][j])
			if err != nil {
				return
			}
		}
	}

	var current pos
	current.y = 0
	current.x = 0
	fmt.Println(solution(current, 0))

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 연구소</title><link>https://compy07.github.io/Blog/posts/boj/14502/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/14502/</guid><description>14502 bfs, bruteforce 풀이</description><pubDate>Wed, 10 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;** https://www.acmicpc.net/problem/14502 **&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(3&amp;lt;=N,M&amp;lt;=8)&lt;/td&gt;
&lt;td&gt;BFS(너비 우선 탐), BruteForce(전체탐색)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;일단 문제가 자체가 너무 BFS로 푸세요!!를 내뿜고 있어서 바로 BFS로 접근했다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다.
연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다.
일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다. 새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.
예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2 0 0 0 1 1 0&lt;br /&gt;
0 0 1 0 1 2 0&lt;br /&gt;
0 1 1 0 1 0 0&lt;br /&gt;
0 1 0 0 0 0 0&lt;br /&gt;
0 0 0 0 0 1 1&lt;br /&gt;
0 1 0 0 0 0 0&lt;br /&gt;
0 1 0 0 0 0 0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이때, 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 곳이다. 아무런 벽을 세우지 않는다면, 바이러스는 모든 빈 칸으로 퍼져나갈 수 있다.
2행 1열, 1행 2열, 4행 6열에 벽을 세운다면 지도의 모양은 아래와 같아지게 된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2 1 0 0 1 1 0&lt;br /&gt;
1 0 1 0 1 2 0&lt;br /&gt;
0 1 1 0 1 0 0&lt;br /&gt;
0 1 0 0 0 1 0&lt;br /&gt;
0 0 0 0 0 1 1&lt;br /&gt;
0 1 0 0 0 0 0&lt;br /&gt;
0 1 0 0 0 0 0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;바이러스가 퍼진 뒤의 모습은 아래와 같아진다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2 1 0 0 1 1 2&lt;br /&gt;
1 0 1 0 1 2 2&lt;br /&gt;
0 1 1 0 1 2 2&lt;br /&gt;
0 1 0 0 0 1 2&lt;br /&gt;
0 0 0 0 0 1 1&lt;br /&gt;
0 1 0 0 0 0 0&lt;br /&gt;
0 1 0 0 0 0 0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;벽을 3개 세운 뒤, 바이러스가 퍼질 수 없는 곳을 안전 영역이라고 한다. 위의 지도에서 안전 영역의 크기는 27이다.
연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;우리가 문제에서 집중해야 되는 것은 바로 &quot;일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다. 새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.&quot; 이 문장이다. 또한 grid(격자) 문제 답게 상하좌우로 이동할 수 있다고 문제에서 말하고 있다. &quot;0은 빈 칸, 1은 벽, 2는 바이러스가 있는 곳이다.&quot; 이렇게 우리가 얻은 정보는&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;벽의 개수는 3개이며 &quot;꼭&quot; 모두 세워야한다.&lt;/li&gt;
&lt;li&gt;바이러스는 동서남북으로 한 턴마다 확산된다.&lt;/li&gt;
&lt;li&gt;바이러스는 벽을 이동하지 못 한다.&lt;/li&gt;
&lt;li&gt;안전 영역이 가장 큰 경우를 구해야됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;이제 문제를 파악했으니, 어떤 기능이 있어야 되는지를 정리해보자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;바이러스가 퍼지는걸 돌려줄 BFS 함수&lt;/li&gt;
&lt;li&gt;벽을 3개 세워줄 벽세우는 함수&lt;/li&gt;
&lt;li&gt;main&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 정도로 줄여줄 수 있을 것이다.
&lt;br /&gt;
 
&lt;br /&gt;
 &lt;/p&gt;
&lt;p&gt;코드를 하나하나 짜보자!!&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;
 &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;전역변수&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;pre&gt;&lt;code&gt;using ll = long long;
using namespace std;
int dy[] = {0, 0, -1, 1}, dx[] = {1, -1, 0, 0};
int n, m;
int board[10][10];
vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; origin;
vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; position;
int count_ = 0;
int res = 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;dy, dx&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;동서남북으로 움직일 바이러스의 경로를 for문으로 간단 처리하기 위해서&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;n, m&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;입력으로 n*m 크기의 연구소가 주어지기 때문에 입력받을 크기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;origin&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;바이러스의 위치를 담은 vector&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;position&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;빈칸의 위치를 담은 vector&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;board&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;n*m 크기의 연구실(바이러스, 빈 칸, 벽의 위치를 담고 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;count_&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;빈 공간의 크기(개수)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;res&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;result 값&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m;j++){
            cin&amp;gt;&amp;gt;board[i][j];
            if(board[i][j] == 2){
                origin.push_back(make_pair(i, j));
                board[i][j] = 0;
                count_++;
            }
            else if(board[i][j] == 0){
                count_++;
                position.push_back(make_pair(i, j));
            }
            
        }
    }
    
    int result = rec(3, 0) - 3;
    cout &amp;lt;&amp;lt; result;
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); 빠른 입출력을 하는 코드입니다.&lt;/p&gt;
&lt;p&gt;먼저 n, m을 받습니다.&lt;/p&gt;
&lt;p&gt;그 후 n*m 만큼 반복문을 돌면서 각 위치마다의 정보를 저장합니다.&lt;/p&gt;
&lt;p&gt;바이러스이면 origin에 넣고, 빈 공간이면 position에 좌표를 저장합니다.&lt;/p&gt;
&lt;p&gt;다음으로 3개의 벽을 세울 코드를 짜보자!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int rec(int current, int start){
    if(current &amp;lt;= 0){
        queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q;
        for(pair&amp;lt;int, int&amp;gt; item : origin) q.push(item);
        int tmp[10][10];
        memcpy(tmp, board, sizeof(board));
        return solution(q, tmp, count_);
    }
    int result = 0;
    for(int i = start; i &amp;lt; position.size(); i++){
        pair&amp;lt;int, int&amp;gt; item = position[i];
        board[item.first][item.second] = 1;
        result = max(rec(current-1, i+1), result);
        board[item.first][item.second] = 0;
    }
    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;저는 재귀적으로 배치하고 solution이라는 BFS 함수를 호출해서 개수를 세도록 코드를 작성하였습니다.&lt;/p&gt;
&lt;p&gt;약간의 백트래킹 느낌으로 current가 초기에 3이고, 그 후 1씩 감소하면서 3개의 벽을 생성 후 BFS를 돌리는 식으로 설계했습니다.&lt;/p&gt;
&lt;p&gt;이제 마지막으로 BFS를 돌리는 solution에 대해서 보도록 하자&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int solution(queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q, int bor[10][10], int cnt){

    int x, y, ny, nx;
    while(!q.empty()){
        pair&amp;lt;int, int&amp;gt; pos = q.front();
        
        q.pop();

        x = pos.second;
        y = pos.first;
        if(bor[y][x] != 0) continue;
        bor[y][x] = 2;
        cnt--;

        for(int i = 0; i &amp;lt; 4; i ++){
            ny = y + dy[i];
            nx = x + dx[i];
            if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; bor[ny][nx] == 0) q.push(make_pair(ny, nx));
        }
    }
    return cnt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;solution은 q에 넣어놓은 바이러스들을 차례대로 확산시키면서 큐가 empty가 될 때까지 돌도록 하였습니다.&lt;/p&gt;
&lt;p&gt;한번 방문한 위치에는 2로 업데이트 후 다시 동서남북으로 확상되도록 만들었습니다.&lt;/p&gt;
&lt;p&gt;if문은 혹시나 board의 범위 밖으로 나가는 것을 방지하기 위해서 작성된 조건문입니다.&lt;/p&gt;
&lt;p&gt;이렇게 나온 count는 벽을 포함한 빈 공간의 개수이므로 -3을 통해서 최대 빈 공간의 개수를 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;정답코드&lt;/strong&gt;&lt;/em&gt;
&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;정답보기&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- summary 아래 한칸 공백 두어야함 --&amp;gt;&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#define MAX 200000001
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
using ll = long long;
using namespace std;

int dy[] = {0, 0, -1, 1}, dx[] = {1, -1, 0, 0};

int n, m;
int board[10][10];

vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; origin;
vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; position;
int count_ = 0;
int res = 0;
int test = 0;

int solution(queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q, int bor[10][10], int cnt){

    int x, y, ny, nx;
    while(!q.empty()){
        pair&amp;lt;int, int&amp;gt; pos = q.front();
        
        q.pop();

        x = pos.second;
        y = pos.first;
        if(bor[y][x] != 0) continue;
        bor[y][x] = 2;
        cnt--;

        for(int i = 0; i &amp;lt; 4; i ++){
            ny = y + dy[i];
            nx = x + dx[i];
            if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; bor[ny][nx] == 0) q.push(make_pair(ny, nx));
        }
    }
    return cnt;
}

int rec(int current, int start){
    if(current &amp;lt;= 0){
        queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q;
        for(pair&amp;lt;int, int&amp;gt; item : origin) q.push(item);
        int tmp[10][10];
        memcpy(tmp, board, sizeof(board));
        return solution(q, tmp, count_);
    }
    int result = 0;
    for(int i = start; i &amp;lt; position.size(); i++){
        pair&amp;lt;int, int&amp;gt; item = position[i];
        board[item.first][item.second] = 1;
        result = max(rec(current-1, i+1), result);
        board[item.first][item.second] = 0;
    }
    return result;
}
int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    
    
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m;j++){
            cin&amp;gt;&amp;gt;board[i][j];
            if(board[i][j] == 2){
                origin.push_back(make_pair(i, j));
                board[i][j] = 0;
                count_++;
            }
            else if(board[i][j] == 0){
                count_++;
                position.push_back(make_pair(i, j));
            }
            
        }
    }
    
    int result = rec(3, 0) - 3;
    cout &amp;lt;&amp;lt; result;
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 거의 소수</title><link>https://compy07.github.io/Blog/posts/boj/1456/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1456/</guid><description>재활 5일차</description><pubDate>Wed, 05 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1456&quot;&gt;거의 소수&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1 ≤ A ≤ B ≤ 1e14)&lt;/td&gt;
&lt;td&gt;Math(수학), 에라토스테네스의 체, 소수판별&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;어떤 수가 소수의 N제곱(N ≥ 2) 꼴일 때, 그 수를 거의 소수라고 한다.&lt;/p&gt;
&lt;p&gt;두 정수 A와 B가 주어지면, A보다 크거나 같고, B보다 작거나 같은 거의 소수가 몇 개인지 출력한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;아니 문제는 딱히 할 말이 없어요.. 그대로 나와 있기 때문에 하지만 여기서 기본 구현은 쉬워서 50%까지는 뚫는데 거기서 틀려서 뭐지 싶습니다.&lt;/p&gt;
&lt;p&gt;이때 잘 보시면 overflow가 발생한다는 것을 알 수 있는데요 저는 이걸 바로 못 발견해서 진짜 많이 당황했습니다. 하지만 이후에 바꿔서 잘 체크해서 넘겨서 AC를 받았네요&lt;/p&gt;
&lt;p&gt;진짜 매번 overflow를 조심해야지 했는데 또 이렇게 하는 것을 보니 아직도 많이 부족하네요! 열심히 PS 해야겠습니다. 여러분들도 즐거운 PS 하세용!!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

ll a, b;

bool visited[(ll) 1e7+1];
vector&amp;lt;ll&amp;gt; primes;


int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;

    
    
    
    for(ll i = 2; i &amp;lt; 1e7; i++){
        if(visited[i]) continue;
        for(ll current = i; current &amp;lt;= 1e7; current+=i) visited[current] = true;
        primes.push_back(i);
    }
    
    
    int result = 0;
    for(ll prime : primes){
        ll current = prime*prime;
        while(current &amp;lt;= b){
            result+=(current &amp;gt;= a);
            if(current &amp;gt; b/prime) break;
            current*=prime;
        }
    }

    cout &amp;lt;&amp;lt; result;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 샤워실 바닥 깔기 (Large)</title><link>https://compy07.github.io/Blog/posts/boj/14601/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/14601/</guid><description>재활 15일차</description><pubDate>Mon, 13 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14601&quot;&gt;샤워실 바닥 깔기 (Large)&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 7)&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;핵심 아이디어는 그냥 분할 정복이고, 사분면으로 나눠서 푸는 아이디어로 어디를 비워놓고 다른곳을 다 채울지
이것만 생각하면 금방 풀 수 잇는 문제인데, 약간의 구현력이 조금 따라오는거 같구요.&lt;/p&gt;
&lt;p&gt;그리고 ㄱ 모양의 타일은 결국 정사각형의 나눠진 부분을 전부 채울 수 없고, 한칸을 비워놓을 수 밖에 없으니
그걸 어디서 합치고, 또 만약에 배수구 빼놓고 했을 때 완벽한 사각형이 등장하고, 이거 제외해서 어떻게
만들거냐! 이거를 생각만하면 solve가 가능한거 같네요&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = unsigned long long;
using ld = long double;
//ll MOD = 1&apos;000&apos;000&apos;007;
ll MOD = 1&apos;000&apos;000&apos;000;
//ll MOD = 1.8446744074E19;

int N, K;

int board[130][130];

int my_count;
int t1, t2;

// 그냥 사분면으로 나눠서 짤라버리면 되겠네 어차피 생긴 모양이 그래버리니까
// 사이즈가 그냥 한 변이라고 생각하고, 정사각형으로만 만든다고 생각하자
bool solution(int size, int y, int x, pair&amp;lt;int, int&amp;gt; pos){
    auto [hy, hx] = pos;
    
    if(size == 0) return false;
    
    int half = 1 &amp;lt;&amp;lt; (size - 1);
    
    // 좌상, 우상, 좌하, 우하
    pair&amp;lt;int, int&amp;gt; states[] = {{0, 0}, {0, 1}, {1, 0}, {1, 1}};
    
    int hole_section = 0;
    // 일단 어느 사분면에 그 hole이 있는지 찾아보자
    // pos가 비워야될 위치
    for(int i = 0; i &amp;lt; 4; i++){
        int qy = y + half * states[i].first;
        int qx = x + half * states[i].second;
        
        if(qy &amp;lt;= hy &amp;amp;&amp;amp; hy &amp;lt; qy + half &amp;amp;&amp;amp; qx &amp;lt;= hx &amp;amp;&amp;amp; hx &amp;lt; qx + half){
            hole_section = i;
            break;
        }
    }
    
    pair&amp;lt;int, int&amp;gt; centers[] = {
        {y + half - 1, x + half - 1},  // 좌상
        {y + half - 1, x + half},      // 우상
        {y + half, x + half - 1},      // 좌하
        {y + half, x + half}           // 우하
    };
    
    for(int idx = 0; idx &amp;lt; 4; idx++){
        if(hole_section == idx) continue;
        board[centers[idx].first][centers[idx].second] = my_count;
    }
    my_count++;
    
    for(int idx = 0; idx &amp;lt; 4; idx++){
        int qy = y + half * states[idx].first;
        int qx = x + half * states[idx].second;
        
        if(hole_section == idx){
            solution(size-1, qy, qx, pos);
        }else{
            solution(size-1, qy, qx, centers[idx]);
        }
    }
    
    return false;
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; K;
    N = 1 &amp;lt;&amp;lt; K;

    
    cin &amp;gt;&amp;gt; t1 &amp;gt;&amp;gt; t2;
    
    board[N - t2][t1 - 1] = -1;
    my_count = 1;
    solution(K, 0, 0, {N - t2,t1 - 1});
    
    
    for(int i = 0; i &amp;lt; N; i++){
        for(int j = 0; j &amp;lt; N; j++){
            cout &amp;lt;&amp;lt; board[i][j] &amp;lt;&amp;lt; &quot; &quot;;
        }
        cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    
    
    
    return 0;
}

/*
 
 0 0 0 0
 0 0 0 0
 0 0 0 0
 0 0 0 0

 
 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 바이너리 문자열 토글</title><link>https://compy07.github.io/Blog/posts/boj/14893/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/14893/</guid><description>greedy? sweeping?</description><pubDate>Sat, 05 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14893&quot;&gt;바이너리 문자열 토글&lt;/a&gt;&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N, U ≤ 100,000)&lt;/td&gt;
&lt;td&gt;greedy, sweeping&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;길이가 N이고 모든 문자가 0인 바이너리 문자열 S0이 있다. 이 문자열에 변경 연산을 총 U번 할 것이고, 변경 연산이 끝난면 다른 문자열로 바뀌게 된다. i번째 연산은 Si-1을 Si로 바꾸는 연산이다. 따라서, U번의 변경 연산이 모두 끝나게 되면 문자열은 SU가 된다.&lt;/p&gt;
&lt;p&gt;변경 연산은 (Li, Ri)와 같은 형태이다. 연산은 구간 [Li, Ri] (양 끝도 포함)에 속하는 모든 1을 0으로 바꾸고, 0을 1로 바꾸는 것이다.&lt;/p&gt;
&lt;p&gt;모든 연산이 끝나게 되면, S0, S1, ..., SU를 구할 수 있게 된다. 이 U+1개의 바이너리 문자열 중에서 사전 순으로 가장 뒤에 오는 것을 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;0-&amp;gt;1 / 1-&amp;gt;0
이다 그러면 뭐가 생각날까요? 바로 XOR이 떠오르죠? 일단 이걸 구현하고, 그걸 문제에 적용시키면서 그냥 사전순으로 업데이트하면 뙇 문제가 풀립니다.&lt;/p&gt;
&lt;p&gt;(그냥 누적으로 xor 쌓은 다음에 각 단계 문자열 비교하고 최대 문자열 비교해서 업데이트하는 방법으로 구현하여 AC 맞음)&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

const int MAX_N = 100001;

int N, U;
int xr[MAX_N];
string result(MAX_N, &apos;0&apos;);

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; U;

    
    vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; op(U);
    for (int i = 0; i &amp;lt; U; i++) {
        cin &amp;gt;&amp;gt; op[i].first &amp;gt;&amp;gt; op[i].second;
        op[i].first--;
    }

    for (pair&amp;lt;int, int&amp;gt; op : op) {
        xr[op.first] ^= 1;
        xr[op.second] ^= 1;
    }

    int current = 0;
    for (int i = 0; i &amp;lt; N; i++) {
        current ^= xr[i];
        result[i] = current + &apos;0&apos;;
    }

    string max_string = result;
    for (int i = U - 1; i &amp;gt;= 0; i--) {
        for (int j = op[i].first; j &amp;lt; op[i].second; j++)
            result[j] = (result[j] - &apos;0&apos;) ? &apos;0&apos; : &apos;1&apos;;
        max_string = max(max_string, result);
    }

    for(int i = 0; i &amp;lt; N; i++) cout &amp;lt;&amp;lt; max_string[i];

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 불 켜기</title><link>https://compy07.github.io/Blog/posts/boj/1505/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1505/</guid><description>재활 7일차</description><pubDate>Wed, 01 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1505&quot;&gt;불 켜기&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N, M ≤ 8)&lt;/td&gt;
&lt;td&gt;BruteForce, Greedy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;솔직히 그냥 풀면 되는데... 처음에 가지치기 없이 그냥 풀었다가 머리 깡 당하고, 가만히 있다가 예...&lt;/p&gt;
&lt;p&gt;더 생각해보니.. 뭔가 어떤 전구 문제 플레에 있었는데 분명 이거 브포밖에 답이 없는데 막 이러다
그리디로 접근해보자 하고 각잡고 생각하니까 겨우 풀었네요...&lt;/p&gt;
&lt;p&gt;완전 어려운 난이도는 아니었는데 왜 이렇게 못 풀었는지 모르겠습니다.. 아직 실력이 많이 부족한가 봅니다.
오류 수정 2시간 동안 너무 힘들었습니다. ㅠ&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
using namespace std;
using ll = long long;
ll MOD = 1&apos;000&apos;000&apos;007;

int n, m;



set&amp;lt;ll&amp;gt; visited;
//map&amp;lt;ll, int&amp;gt; visited;


ll push(ll current, pair&amp;lt;int, int&amp;gt; yx){
    
    auto [y, x] = yx;
    
    int my = min(y+2, n);
    y = max(0, y - 1);
    
    int mx = min(x + 2, m);
    x = max(0, x - 1);
    
//    cout &amp;lt;&amp;lt; y &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; my &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; mx &amp;lt;&amp;lt; &quot;\n&quot;;
    for(int cy = y; cy &amp;lt; my; cy++){
        for(int cx = x; cx &amp;lt; mx; cx++){
            current ^= (1LL &amp;lt;&amp;lt; (cy * 8 + cx));
        }
    }
    
    return current;
}



int main() {
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    // 이거 greedy가 맞는듯.
    // 그니까 결국 끝에서 바꾸는걸 포인트로 잡는건 맞고
    // 순차적으로 들어가는건데 맵 윗줄은 확정적으로 만들어야하니까 그것만 좀 알아서 잘 찾아내서 돌리고, 이후에 greedy하게 왼쪽 대각선있는 놈이 꺼져있으면 키도록 만들면 되는거 아님?
    // 그러면 마지막에도 되겠지 뭐
    
    
    
    ll state = 0;
    for(int i = 0; i &amp;lt; n; i++){
        char tmp;
        for(int j = 0; j &amp;lt; m; j++){
            
            cin &amp;gt;&amp;gt; tmp;
            if(tmp == &apos;*&apos;) state |= 1LL &amp;lt;&amp;lt; (j + i * 8);
            
        }
    }
    ll target = 0;
    
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m; j++){
            target |= (1LL &amp;lt;&amp;lt; (i * 8 + j));
        }
    }
    
    
    // 순차적으로 돌렷는데 못 만드는 모양이면 그냥 못만드는 거니까 미련가지지 않으면 됨.
    
    ll result =LLONG_MAX;
    
    
    ll check = (1LL &amp;lt;&amp;lt; (n+1));
    
    
    
    for(ll fy = 0; fy &amp;lt; (1LL &amp;lt;&amp;lt; n); fy++){
        for(ll fx = 0; fx &amp;lt; (1LL &amp;lt;&amp;lt; m); fx++){
            
            ll current = state;
            // fy가 첫번째 줄 이ㅐㅅ키임
            //fx는 처음 시작 col ㅇㅣㅁ
            
            ll cnt = 0;
            
            bool debug = false;
            
//            for(int i = 0; i &amp;lt; 5; i++){
//                if(fx &amp;amp; (1 &amp;lt;&amp;lt; i)) cout &amp;lt;&amp;lt; 1;
//                else cout &amp;lt;&amp;lt; 0;
//            }
//            cout &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; fx;
//            cout &amp;lt;&amp;lt; &quot;\n&quot;;
//            
//            if(fy == 15) debug = true;
            if(debug) cout &amp;lt;&amp;lt; fy &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; fx &amp;lt;&amp;lt; &quot;\n&quot;;
            
            
            
            for(int i = 0; i &amp;lt; n; i++){
                if(fy &amp;amp; (1LL &amp;lt;&amp;lt; ((i)))) {
                    current = push(current, {i, 0});
                    cnt ++;
                }
            }
//            cout &amp;lt;&amp;lt; &quot;\n&quot; &amp;lt;&amp;lt; cnt &amp;lt;&amp;lt; &quot;\n&quot;;
            if(debug){
                
                for(int i = 0; i &amp;lt; n; i++){
                    for(int j = 0; j &amp;lt; m; j++){
                        if(!(current &amp;amp; 1LL &amp;lt;&amp;lt; (i * 8 + j))) cout &amp;lt;&amp;lt; 0;
                        else cout &amp;lt;&amp;lt; 1;
                    }
                    cout &amp;lt;&amp;lt; &quot;\n&quot;;
                }
                cout &amp;lt;&amp;lt; &quot;\n&quot;;
            }
            for(int i = 0; i &amp;lt; m; i++){
                if(fx &amp;amp; (1LL &amp;lt;&amp;lt; (i))){
                    current = push(current, {0, i});
                    cnt++;
                }
            }
//
            
            for(int i = 1; i &amp;lt; n; i++){
                for(int j = 1; j &amp;lt; m; j++){
//                    cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; j &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; (target &amp;amp; (1LL &amp;lt;&amp;lt; ((i-1) * 8 + (j-1)))) &amp;lt;&amp;lt; &quot;\n&quot;;
//                    
//                    if(i == 3 &amp;amp;&amp;amp; j == 3){
//                        for(int i = 0; i &amp;lt; n; i++){
//                            for(int j = 0; j &amp;lt; m; j++){
//                                if(!(current &amp;amp; 1LL &amp;lt;&amp;lt; (i * 8 + j))) cout &amp;lt;&amp;lt; 0;
//                                else cout &amp;lt;&amp;lt; 1;
//                            }
//                            cout &amp;lt;&amp;lt; &quot;\n&quot;;
//                        }
//                        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//                        
//                        
//                        for(int i = 0; i &amp;lt; n; i++){
//                            for(int j = 0; j &amp;lt; m; j++){
//                                if(!(((1LL &amp;lt;&amp;lt; ((i-1) * 8 + (j-1)))) &amp;amp; 1LL &amp;lt;&amp;lt; (i * 8 + j))) cout &amp;lt;&amp;lt; 0;
//                                else cout &amp;lt;&amp;lt; 1;
//                            }
//                            cout &amp;lt;&amp;lt; &quot;\n&quot;;
//                        }
//                        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//                        
//                    }
//                    
                    if(!(current &amp;amp; (1LL &amp;lt;&amp;lt; ((i-1) * 8 + (j-1))))) {
                        current = push(current, {i, j});
                        cnt++;
                    }
                }
            }
            if(debug){
                
                for(int i = 0; i &amp;lt; n; i++){
                    for(int j = 0; j &amp;lt; m; j++){
                        if(!(current &amp;amp; 1LL &amp;lt;&amp;lt; (i * 8 + j))) cout &amp;lt;&amp;lt; 0;
                        else cout &amp;lt;&amp;lt; 1;
                    }
                    cout &amp;lt;&amp;lt; &quot;\n&quot;;
                }
                cout &amp;lt;&amp;lt; &quot;\n&quot;;
            }
            if(target == current)
                result = min(result, cnt);
            
        }
    }
    
//    
    if(result == LLONG_MAX) cout &amp;lt;&amp;lt; -1;
    else cout &amp;lt;&amp;lt; result;

    
//    cout &amp;lt;&amp;lt; bfs(state);
//    if(visited.find(LLONG_MAX) == visited.end()) cout &amp;lt;&amp;lt; -1;
//    else cout &amp;lt;&amp;lt; visited[LLONG_MAX];
    return 0;
}

/*
 
 for(int i = 0; i &amp;lt; n; i++){
     for(int j = 0; j &amp;lt; m; j++){
         if(!(state &amp;amp; 1 &amp;lt;&amp;lt; (i * 8 + j))) cout &amp;lt;&amp;lt; 0;
         else cout &amp;lt;&amp;lt; 1;
     }
     cout &amp;lt;&amp;lt; &quot;\n&quot;;
 }
 state = push(state, {2, 2});
 
 cout &amp;lt;&amp;lt; &quot;\n&quot;;
 
 for(int i = 0; i &amp;lt; n; i++){
     for(int j = 0; j &amp;lt; m; j++){
         if(!(state &amp;amp; 1 &amp;lt;&amp;lt; (i * 8 + j))) cout &amp;lt;&amp;lt; 0;
         else cout &amp;lt;&amp;lt; 1;
     }
     cout &amp;lt;&amp;lt; &quot;\n&quot;;
 }
 state = push(state, {2, 2});
 
 cout &amp;lt;&amp;lt; &quot;\n&quot;;
 
 for(int i = 0; i &amp;lt; n; i++){
     for(int j = 0; j &amp;lt; m; j++){
         if(!(state &amp;amp; 1 &amp;lt;&amp;lt; (i * 8 + j))) cout &amp;lt;&amp;lt; 0;
         else cout &amp;lt;&amp;lt; 1;
     }
     cout &amp;lt;&amp;lt; &quot;\n&quot;;
 }
 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 팰린드롬 분할</title><link>https://compy07.github.io/Blog/posts/boj/1509/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1509/</guid><description>어흑..</description><pubDate>Fri, 28 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1509&quot;&gt;팰린드롬 분할&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 2500)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;코드 처음할 때 3차원으로 정의해놓고.. 쓰다보니까 그냥 아니라서 혼자 생각해서 풀었는데요&lt;/p&gt;
&lt;p&gt;dp[i][j]는 i - j + 1부터 i까지 점프를 뛸 때(즉 팰린드롬일 때) 최소 값을 저장하고 이후에 돌면서
최솟값 업데이트해주고 그걸로 시간초과 피하면서 처리하면 AC!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#define inf 2100000000

using namespace std;
using ll = long long;

int dp[2501][2501]; // [i][j][k] i번째 수일 때 i - j까지의 연속된 순서가 팰린드롬일 때 현재 나눠진 팰린드롬의 개수
int visited[2501];

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    string input;
    
    cin &amp;gt;&amp;gt; input;
    int result = (int) input.size();
    
    int n = result;
    // 먼저 자기 자신 일단 넣어두고, 다음으로
    
    for(int i = 1; i &amp;lt;= n; i++){
        for(int j = 1; j &amp;lt;= n; j++) dp[i][j] = inf;
    }
    
    for(int i = 0; i &amp;lt;= n; i++) dp[0][i] = 0;
    dp[1][1] = 1;
    
    visited[0] = 0;
    visited[1] = 1;
    
    for(int i = 2; i &amp;lt;= n; i++){
        dp[i][1] = min(dp[i-1][1] + 1, dp[i-1][2]);
        visited[i] = inf;
        
        
    }
//        dp[i][
        
////    }
//    
//    
    for(int i = 2; i &amp;lt;= n; i++){
        
        for(int k = 1; k &amp;lt;= i; k++) {
            dp[i][1] = min(dp[i][1], dp[i-1][k] + 1);
            visited[i] = min(visited[i], dp[i][1]);
        }
        
        
        for(int j = 1; j &amp;lt; i; j++){
            
            
            
            if(input[j - 1] != input[i - 1]) continue;

//            else {
//                if(input[i-j-1] == &apos;R&apos;){
//                    cout &amp;lt;&amp;lt;i &amp;lt;&amp;lt; &quot; &quot;&amp;lt;&amp;lt; j &amp;lt;&amp;lt; &quot;\n&quot;;
//                }
//            }
            if(dp[i - 1][i - j - 1] &amp;gt;= inf) continue;
            
//            cout &amp;lt;&amp;lt; &quot;check: &quot; &amp;lt;&amp;lt; input[i-j-1] &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; input[i] &amp;lt;&amp;lt; &quot;\n&quot;;
//            cout &amp;lt;&amp;lt; &quot;dp: &quot; &amp;lt;&amp;lt; dp[i-1][j-1] &amp;lt;&amp;lt; &quot;\n&quot;;
//
            dp[i][i - j + 1] = min(dp[i][i - j + 1], visited[j-1]+1);
            visited[i] = min(dp[i][i-j+1], visited[i]);
            
        }
    }
    
//    
//    for(int i = 1; i &amp;lt;= n; i++){
//        for(int j = 1; j &amp;lt;= n; j++){
//            if(dp[j][i] == inf) cout &amp;lt;&amp;lt; &quot;O &quot;;
//            else cout &amp;lt;&amp;lt; dp[j][i] &amp;lt;&amp;lt; &quot; &quot;;
//        }
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//    }
    
    cout &amp;lt;&amp;lt; visited[n];
    
    
}
// ABACAAB
// QWERTYTREWQWERT


// DCADD
// 16
// BBCDDECAECBDABADDCEBACCCBDCAABDBADD
// BBCDDECAECBDABADDCEBACCCBDCAABDBADD

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 구슬 탈출 3</title><link>https://compy07.github.io/Blog/posts/boj/15644/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/15644/</guid><description>재활 12일차 - 2</description><pubDate>Mon, 17 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15644&quot;&gt;구슬 탈출 3&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(3 ≤ N, M ≤ 10)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;우려먹기 시리즈라 그냥 설명은 첫번째 구슬 탈출 2에서 봐주시면 감사하겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

int inf = 1e9+7;

int n ,m;

char board[11][11];
bool visited[11][11][2]; // 0 is red, 1 is blue



pair&amp;lt;pair&amp;lt;int, int&amp;gt;, bool&amp;gt; move(int dx, int dy, int y, int x, pair&amp;lt;int, int&amp;gt; current){
    queue&amp;lt;pair&amp;lt;int ,int&amp;gt;&amp;gt; q;
    q.push({y, x});
    
    pair&amp;lt;int, int&amp;gt; last;
    
    bool check = false;
    
    while(!q.empty()){
        
        auto [cy, cx] = q.front(); q.pop();
        last= {cy, cx};
        
        if(board[cy][cx] == &apos;O&apos;) return {last, false};
        
        if(cy == current.first &amp;amp;&amp;amp; cx == current.second) check = true;
        
        int nx = cx + dx;
        int ny = cy + dy;
        
        if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; board[ny][nx] != &apos;#&apos;) q.push({ny, nx});
        
    }
    
    last.first += dy * -1 *  check;
    last.second += dx * -1 *  check;
    
    
    return {last, check};
    
}

pair&amp;lt;int, string&amp;gt; solution(pair&amp;lt;int, int&amp;gt; red_ball, pair&amp;lt;int, int&amp;gt; blue_ball){
    
    queue&amp;lt;pair&amp;lt;pair&amp;lt;int, int&amp;gt;, string&amp;gt;&amp;gt; balls;
    
    
    int dy[] = {1, -1, 0, 0}, dx[] = {0, 0, 1, -1};
    
    char direction[] = {&apos;D&apos;, &apos;U&apos;, &apos;R&apos;, &apos;L&apos;};
    // 근데 뭐가 먼저 움직이는가.. 이게 중요하거든 blue 부터
    
    balls.push({blue_ball, &quot;&quot;});
    balls.push({red_ball, &quot;&quot;});
    visited[red_ball.first][red_ball.second][0] = true;
    visited[blue_ball.first][blue_ball.second][1] = true;
    
    
    
    int step = 0;
    while(++step &amp;lt;= 10){
        int length = balls.size();
        
        
        
        
        while(length &amp;gt; 0){
            auto [blue, _] = balls.front(); balls.pop();
            auto [red, path] = balls.front(); balls.pop();

            for(int i = 0; i &amp;lt; 4; i++){
                auto [blue_move, blue_check] = move(dx[i], dy[i], blue.first, blue.second, red);
                
                if(board[blue_move.first][blue_move.second] == &apos;O&apos;) continue;
                
                auto [red_move, red_check] = move(dx[i], dy[i], red.first, red.second, blue);
                
                if(board[red_move.first][red_move.second] == &apos;O&apos;) return {step, path+direction[i]};
                
                balls.push({blue_move, &quot;&quot;});
                balls.push({red_move, path + direction[i]});
            }
            length-=2;
        }
        
//
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//        for(int i = 0; i &amp;lt; n; i++){
//            for(int j = 0; j &amp;lt; m; j++){
//                cout &amp;lt;&amp;lt; visited[i][j][0] &amp;lt;&amp;lt; &quot; &quot;;
//            }
//            cout &amp;lt;&amp;lt; &quot;\n&quot;;
//        }
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    
    return {-1, &quot;&quot;};
    
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    
    pair&amp;lt;int, int&amp;gt; red_ball, blue_ball, hole;
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m; j++){
            cin &amp;gt;&amp;gt; board[i][j];
            
            if(board[i][j] == &apos;B&apos;) blue_ball = {i, j};
            else if(board[i][j] == &apos;R&apos;) red_ball = {i, j};
            else if(board[i][j] == &apos;O&apos;) hole = {i, j};
        }
    }
    // 그렇네.. 허헣 아 그러면
    
    auto [result, path] = solution(red_ball, blue_ball);
    
    cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
    if(result &amp;gt;-1) cout &amp;lt;&amp;lt; path;
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 수학</title><link>https://compy07.github.io/Blog/posts/boj/1565/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1565/</guid><description>유클리드 호제법을 이용한 1565 풀이</description><pubDate>Mon, 24 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1565&quot;&gt;수학&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= D, M &amp;lt;=50, 0 &amp;lt; Element &amp;lt;= 10^9)&lt;/td&gt;
&lt;td&gt;Math(수학)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;배열 D와, 배열 M이 주어졌을 때, D에 있는 모든 수의 배수이며, M에 있는 모든 수의 약수인 수의 개수를 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;문제가 원하는 답은 정말 너무나 간단하다. 두 배열이 주어졌을 때, 하나의 배열을 D, 또 다른 배열을 M으로 둔다.
이 때 D의 최소공배수와 M의 최대공약수의 약수의 겹치는 수가 몇개 있는지를 찾아내는 프로그램을 작성하면 된다는 것이다.&lt;/p&gt;
&lt;p&gt;먼저 우리는 빠른 시간 안에 최대 공약수와 최소 공배수를 찾는 방법에 대해서 알아보자&lt;/p&gt;
&lt;h2&gt;유클리드 호제법(Euclidean Algorithm)?&lt;/h2&gt;
&lt;hr /&gt;
&lt;h3&gt;2개의 자연수의 최대 공약수(Greatest Common Divisor, GCD)를 구하는 알고리즘&lt;/h3&gt;
&lt;p&gt;:::tip[샹식]
호제법이란 말은 두 수가 서로(互) 상대방 수를 나누어(除)서 결국 원하는 수를 얻는 알고리즘을 나타낸다.
:::&lt;/p&gt;
&lt;p&gt;유클리드 호제법은 보통 GCD라는 함수로 구현되어 있습니다.&lt;/p&gt;
&lt;p&gt;:::note[핵심]
유클리드 호제법은 Mod 연산(나머지 연산)을 통해서 최대 공약수를 얻어내는 방식이다.&lt;/p&gt;
&lt;p&gt;&apos;GCD(A, B) -&amp;gt; A % B -&amp;gt; B % (A%B) -&amp;gt; ....... -&amp;gt; GCD(A, B)&apos;
:::&lt;/p&gt;
&lt;p&gt;2개의 자연수 a, b에 대해서 a를 b로 나눈 나머지를 r이라 하면(단, a&amp;gt;b), a와 b의 최대공약수는 b와 r의 최대공약수와 같다.
이 성질에 따라, b를 r로 나눈 나머지 r&apos;를 구하고, 다시 r을 r&apos;로 나눈 나머지를 구하는 과정을 반복하여 나머지가 0이 되었을 때 나누는 수가 a와 b의 최대공약수이다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;또한 최대 공약수를 이용해서 최소 공배수를 얻을 수 있는데
이를 이용해서 이 문제를 금방 풀 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import (
	&quot;bufio&quot;
	&quot;fmt&quot;
	&quot;os&quot;
)

var n, m int

func gcd(a, b int) int {
	if b == 0 {
		return a
	}
	return gcd(b, a%b)
}

func lcm(a, b int) int {
	return (a * b) / gcd(a, b)
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	_, err := fmt.Fscanln(reader, &amp;amp;n, &amp;amp;m)
	if err != nil {
		return
	}

	var tmp int
	fmt.Fscan(reader, &amp;amp;tmp)
	myLcm := tmp
	if tmp == 0 {
		fmt.Fprintln(writer, 0)
		return
	}
	for i := 0; i &amp;lt; n-1; i++ {
		fmt.Fscan(reader, &amp;amp;tmp)
		if tmp == 0 {
			fmt.Fprintln(writer, 0)
			return
		}
		myLcm = lcm(myLcm, tmp)
	}

	fmt.Fscan(reader, &amp;amp;tmp)
	myGcd := tmp

	if tmp == 0 {
		fmt.Fprintln(writer, 0)
		return
	}

	for i := 0; i &amp;lt; m-1; i++ {
		fmt.Fscan(reader, &amp;amp;tmp)
		if tmp == 0 {
			fmt.Fprintln(writer, 0)
			return
		}
		myGcd = gcd(myGcd, tmp)
	}

	if myGcd%myLcm != 0 {
		fmt.Fprintln(writer, 0)
		return
	}

	count := 0
	div := myGcd / myLcm
	for i := 1; i*i &amp;lt;= div; i++ {
		if div%i == 0 {
			count++
			if i != div/i {
				count++
			}
		}
	}

	fmt.Fprintln(writer, count)

}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 구슬 탈출 4</title><link>https://compy07.github.io/Blog/posts/boj/15653/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/15653/</guid><description>재활 12일차 - 3</description><pubDate>Mon, 17 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15653&quot;&gt;구슬 탈출 4&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(3 ≤ N, M ≤ 10)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;우려먹기 그 마지막 시리즈&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

int inf = 1e9+7;

int n ,m;

char board[11][11];
bool visited[11][11][11][11]; // 0 is red, 1 is blue



pair&amp;lt;pair&amp;lt;int, int&amp;gt;, bool&amp;gt; move(int dx, int dy, int y, int x, pair&amp;lt;int, int&amp;gt; current){
    queue&amp;lt;pair&amp;lt;int ,int&amp;gt;&amp;gt; q;
    q.push({y, x});
    
    pair&amp;lt;int, int&amp;gt; last;
    
    bool check = false;
    
    while(!q.empty()){
        
        auto [cy, cx] = q.front(); q.pop();
        last= {cy, cx};
        
        if(board[cy][cx] == &apos;O&apos;) return {last, false};
        
        if(cy == current.first &amp;amp;&amp;amp; cx == current.second) check = true;
        
        int nx = cx + dx;
        int ny = cy + dy;
        
        if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; board[ny][nx] != &apos;#&apos;) q.push({ny, nx});
        
    }
    
    last.first += dy * -1 *  check;
    last.second += dx * -1 *  check;
    
    
    return {last, check};
    
}

int solution(pair&amp;lt;int, int&amp;gt; red_ball, pair&amp;lt;int, int&amp;gt; blue_ball){
    
    queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; balls;
    
    
    int dy[] = {1, -1, 0, 0}, dx[] = {0, 0, 1, -1};
    
    
    
    // 근데 뭐가 먼저 움직이는가.. 이게 중요하거든 blue 부터
    
    balls.push(blue_ball);
    balls.push(red_ball);
    visited[red_ball.first][red_ball.second][blue_ball.first][blue_ball.second] = true;
    
    
    
    
    int step = 1;
    
    while(!balls.empty()){
        int length = balls.size()/2;
        while(length --&amp;gt; 0){
            pair&amp;lt;int, int&amp;gt; blue = balls.front(); balls.pop();
            pair&amp;lt;int, int&amp;gt; red = balls.front(); balls.pop();
            for(int i = 0; i &amp;lt; 4; i++){
                auto [blue_move, blue_check] = move(dx[i], dy[i], blue.first, blue.second, red);
                
                if(board[blue_move.first][blue_move.second] == &apos;O&apos;) continue;
                
                auto [red_move, red_check] = move(dx[i], dy[i], red.first, red.second, blue);
                
                if(board[red_move.first][red_move.second] == &apos;O&apos;) return step;
                
                if(visited[red_move.first][red_move.second][blue_move.first][blue_move.second])
                    continue;
                
                visited[red_move.first][red_move.second][blue_move.first][blue_move.second] = true;
                balls.push(blue_move);
                balls.push(red_move);
            }
        }

        step++;
    }
    
    return -1;
    
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    
    pair&amp;lt;int, int&amp;gt; red_ball, blue_ball, hole;
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m; j++){
            cin &amp;gt;&amp;gt; board[i][j];
            
            if(board[i][j] == &apos;B&apos;) blue_ball = {i, j};
            else if(board[i][j] == &apos;R&apos;) red_ball = {i, j};
            else if(board[i][j] == &apos;O&apos;) hole = {i, j};
        }
    }
    // 그렇네.. 허헣 아 그러면
    
    cout &amp;lt;&amp;lt; solution(red_ball, blue_ball);
    
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 연세워터파크</title><link>https://compy07.github.io/Blog/posts/boj/15678/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/15678/</guid><description>재활 3일차</description><pubDate>Sat, 05 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15678&quot;&gt;연세워터파크&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(2 ≤ N ≤ 100000&amp;lt;br/&amp;gt;1 ≤ D ≤ N-1)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 전형적인 DP인데 약간 새로운 관리법이 추가된 dp? 느낌이네요. 생각보다 재밌는 문제였습니다.&lt;/p&gt;
&lt;p&gt;한번씩 풀어보면 좋을 듯 합니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;set&amp;gt;
using namespace std;
using ll = long long;
int n, d;
ll dp[100&apos;001];
ll nums[100&apos;001];
map&amp;lt;ll, ll&amp;gt; current;
set&amp;lt;ll&amp;gt; keys;

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; d;
    ll result = -1e10;
    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; nums[i];
    
    for(int i = 0; i &amp;lt; d; i++){
        ll previous = keys.empty() ? 0 : *--keys.end();
        dp[i] = max(previous + nums[i], nums[i]);
        
        keys.insert(dp[i]);
        current[dp[i]]++;
        
        result = max(dp[i], result);

    }
    for(int i = d; i &amp;lt; n; i++){
        ll previous = *--keys.end(); // 일단 전에 있던 것들 중에서 올 수 있는 가장 쩌는 애를 데려오겟다ㅓ
        dp[i] = max(previous + nums[i], nums[i]);
        
        keys.insert(dp[i]);
        current[dp[i]]++;
        
        
        current[dp[i - d]] --;
        if(!current[dp[i - d]]) keys.erase(dp[i-d]);
        
        result = max(dp[i], result);
    }
    
    
    cout &amp;lt;&amp;lt; result;

    
    

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 회문은 회문아니야!!</title><link>https://compy07.github.io/Blog/posts/boj/15927/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/15927/</guid><description>재미있는 날먹 문제</description><pubDate>Wed, 02 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15927&quot;&gt;회문은 회문아니야!!&lt;/a&gt;&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 500,000)&lt;/td&gt;
&lt;td&gt;에드혹&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;팰린드롬이란 앞으로 읽으나 뒤로 읽으나 같은 문자열을 말한다. 팰린드롬의 예시로 POP, ABBA 등이 있고, 팰린드롬이 아닌 것의 예시로 ABCA, PALINDROME 등이 있다.&lt;/p&gt;
&lt;p&gt;뭔가 이상한 점이 보이지 않는가? 그 어떤 언어에서도 팰린드롬을 뜻하는 단어는 팰린드롬이 아니다! 많은 사람들이 추구하는 “대칭의 아름다움”은 그저 허상에 불과하다.&lt;/p&gt;
&lt;p&gt;알파벳 대문자로 이루어진 문자열이 주어졌을 때, 팰린드롬이 아닌 가장 긴 부분문자열의 길이를 구해 보자. 이때 부분문자열을 이루는 글자는 연속해야 한다. AB는 ABCD의 부분문자열이지만, AC는 아니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;자 문제에 대해서 생각해보자&lt;/p&gt;
&lt;p&gt;도대체 어떤 식으로 접근할까? 아이디어는 간단하다.
우리는 팰린드롬이 아닌 값을 찾아내는 것인데!&lt;/p&gt;
&lt;p&gt;제일 긴 값을 찾는 것은 바로 끝과 끝을 비교한다.
그래서 쭉 돌아오면서 팰린드롬인지 확인한다.
만약 팰린드롬이면 마지막에서 -1해서 size -1의 상태에서 다시 팰린드롬인지 확인한다.&lt;/p&gt;
&lt;p&gt;그렇다면 바로 이 값을 통해서 모두가 같은 문자열인지 아닌지를 판단한다.
모두가 같은 문자열이면 -1을할 것이고, 아니면 문자열의 size 또는 size -1을 반환하겠다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;cstring&amp;gt;

using namespace std;

string n;

int solution(){
    
    // 걍 맞는지만 체크하면 끝이네
    
    int start =0, end = (int) n.size() - 1;
    while(start &amp;lt; end)
        if(n[start++] != n[end--]) return (int) n.size();
    
    end = (int) n.size() - 2;
    start = 0;
    
    while(start &amp;lt; end)
        if(n[start++] != n[end--]) return (int) n.size()-1;
    
    
    return -1;
    
}
int main() {
    cin &amp;gt;&amp;gt; n;
    
    cout &amp;lt;&amp;lt; solution();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] Parcel</title><link>https://compy07.github.io/Blog/posts/boj/15981/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/15981/</guid><description>재활 2일차</description><pubDate>Fri, 04 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15981&quot;&gt;Parcel&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(10 ≤ w ≤ 799,994&amp;lt;br/&amp;gt;4 ≤ n ≤ 5,000)&lt;/td&gt;
&lt;td&gt;Dynamic Programming, PreSum&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;일단 이거는 Meet in the middle 요거 사용한다고 하는데 그렇게 직관적인 사용은 아니고.. 이 네개의 값을 어떻게 시간 안에 우겨넣지?
라는 생각으로 풀다보면 solution이 나오게 되긴 합니다.&lt;/p&gt;
&lt;p&gt;처음에는 두개씩 묶어서 이분 탐색으로 찾으려고 했는데 문제가 제가 생각한 느낌이 아니라서 좌절되고, 이후에 조금 생각해보니 아 이거 각 조합에다가 그냥 이중 두번 사용하면 좋겠다 이렇게 됐는데...&lt;/p&gt;
&lt;p&gt;사실 이것도 시간초과가 납니다. 이게 그냥 이중이 아니라 안에 한번더 포문이 돌아가는데 별로 크지 않다고 생각했던게 아니였던 것이였죠.. 허ㅓㅎㅎ&lt;/p&gt;
&lt;p&gt;그래서 생각했던게.. &quot;최대인 놈 하나만&quot; 찾으면 된다는 것에서 어차피 순차적으로 1부터 n까지 돌때 더했던 값을 맨 마지막으로 업데이트 시켜놓고 포문을 다시 1부터 n까지 돌게 시키면
제일 빨리 나올 수 있게 되면서 중복도 피할 수 있게 만들 수 있습니다. 그래서 그렇게 짰더니? 다행이 AC가 나오더군요..&lt;/p&gt;
&lt;p&gt;다른 분들 풀이를 보니 저처럼 푼 분들이 많은 것으로 보였습니다. 하튼 생각보다 재미있는 문제였습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
int w, n;
int nums[5001];
pair&amp;lt;int,int&amp;gt; dp[400&apos;001];



int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; w &amp;gt;&amp;gt; n;
    
    for(int i = 1; i &amp;lt;= n; i++) cin &amp;gt;&amp;gt; nums[i];
    
    int cnt = 0;
    for(int i = 1; i &amp;lt;= n; i++){
        for(int j = i+1; j &amp;lt;= n; j++) dp[nums[i]+nums[j]] = {i, j};
    }
    
    for(int i = 1; i &amp;lt;= n; i++){
        for(int j = i+1; j &amp;lt;= n; j++){
            int current = w - nums[i] - nums[j];
            if(!(1 &amp;lt; current &amp;amp;&amp;amp; current &amp;lt; 400&apos;001)) continue;
            if(dp[current].first == i || dp[current].second == j || dp[current].first + dp[current].second == 0
               || dp[current].first == j || dp[current].second == i) continue;
            
//            cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; j &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; dp[current].first &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; dp[current].second &amp;lt;&amp;lt; &quot;\n&quot;;
            cout &amp;lt;&amp;lt; &quot;YES&quot;;
            return 0;
            
        }
    }
    
    cout &amp;lt;&amp;lt; &quot;NO&quot;;
    
    
    return 0;
}
/*
 10 6
 2 3 3 4 5 1
 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 말이 되고픈 원숭이</title><link>https://compy07.github.io/Blog/posts/boj/1600/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1600/</guid><description>NZEC 오류와 첫 만남을 가졌다...</description><pubDate>Tue, 02 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1600&quot;&gt;말이 되고픈 원숭이&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= N, M &amp;lt;= 200&amp;lt;br/&amp;gt;0&amp;lt;=K&amp;lt;=30)&lt;/td&gt;
&lt;td&gt;BFS(너비우선탐색)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;동물원에서 막 탈출한 원숭이 한 마리가 세상구경을 하고 있다.
그 녀석은 말(Horse)이 되기를 간절히 원했다.
그래서 그는 말의 움직임을 유심히 살펴보고 그대로 따라 하기로 하였다. 말은 말이다. 말은 격자판에서 체스의 나이트와 같은 이동방식을 가진다. 다음 그림에 말의 이동방법이 나타나있다. x표시한 곳으로 말이 갈 수 있다는 뜻이다. 참고로 말은 장애물을 뛰어넘을 수 있다.
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./horse.png&quot; alt=&quot;horse&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;근데 원숭이는 한 가지 착각하고 있는 것이 있다. 말은 저렇게 움직일 수 있지만 원숭이는 능력이 부족해서 총 K번만 위와 같이 움직일 수 있고, 그 외에는 그냥 인접한 칸으로만 움직일 수 있다. 대각선 방향은 인접한 칸에 포함되지 않는다.&lt;/p&gt;
&lt;p&gt;이제 원숭이는 머나먼 여행길을 떠난다. 격자판의 맨 왼쪽 위에서 시작해서 맨 오른쪽 아래까지 가야한다. 인접한 네 방향으로 한 번 움직이는 것, 말의 움직임으로 한 번 움직이는 것, 모두 한 번의 동작으로 친다. 격자판이 주어졌을 때, 원숭이가 최소한의 동작으로 시작지점에서 도착지점까지 갈 수 있는 방법을 알아내는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 문제가 파놓은 함정이 있는데, 여기서 k만큼 먼저 움직이고 동서남북으로 이동하는 것이 아니었다. 그냥 아무때나 해도된다는 것을 알고 문제 풀이를 들어가야한다.
(요건 때문에 좀 힘들었음. 너무 모호했음)&lt;/p&gt;
&lt;p&gt;일단 문제는 그걸로 이동하면 되면서, 시간, 메모리 초과가 나지 않도록 적절히 visit처리 해주기만하면 금방 풀 수 있는 문제이다.&lt;/p&gt;
&lt;p&gt;내일이 시험인데 백준풀고, 전공 공부하고 있는데 지금 큰일난거 같다.... 흐아 어트카냐&lt;/p&gt;
&lt;p&gt;일단 말은 dx, dy와 horsedx, horsedy를 선언하고 이를 토대로 격자판을 탐색하였다.&lt;/p&gt;
&lt;p&gt;또한 n=1, m=1일때는 bfs가 시작되지도 않기 때문에 따로 예외처리를 해주었다.&lt;/p&gt;
&lt;p&gt;일단 go언어를 공부 중이라(서버 개발 공부중)서 go로 푸는데 큐를 썼는데 바로 import가 안되서 std에 없는 것 같아 직접 구현하여 사용했다.&lt;/p&gt;
&lt;p&gt;생각보다 새롭고 재미있는 ps 경험이다. (대회때는 절대 안 쓴다. cpp 사랑해)&lt;/p&gt;
&lt;p&gt;Queue 구현&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Pos struct {
	x, y, cost, current_k int
}

type Node struct {
	next *Node
	prev *Node
	item *Pos
}

type Queue struct {
	head, tail *Node
	size       int
}
type IQueue interface {
	Enqueue(*Pos)
	Init()
	Dequeue() *Pos
	Front() *Pos
	Size() int
	Empty() bool
}

func (q *Queue) Enqueue(pos *Pos) {
	var newNode *Node = &amp;amp;Node{next: q.tail, prev: q.tail.prev, item: pos}
	if q.head.next == nil {
		q.tail.prev = newNode
		q.head.next = newNode
		newNode.prev = q.head
	} else {
		q.tail.prev.next = newNode
		q.tail.prev = newNode
	}

	q.size++

}
func (q *Queue) Init() {
	q.head = &amp;amp;Node{next: nil, prev: nil, item: nil}
	q.tail = &amp;amp;Node{next: nil, prev: nil, item: nil}
	q.size = 0
}

func (q *Queue) Dequeue() *Pos {
	if q.head.next == nil {
		return nil
	}

	var remove *Pos = q.head.next.item

	if q.head.next.next == q.tail {
		q.head.next = nil
		q.tail.prev = nil
	} else {
		q.head.next = q.head.next.next
	}
	q.size--
	return remove
}
func (q *Queue) Front() *Pos {
	return q.head.item
}

func (q *Queue) Size() int {
	return q.size
}
func (q *Queue) Empty() bool {
	return q.size == 0
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enqueue, Dequeue를 구현하고, 안에 들어갈 데이터 pos를 만들어주었다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br/&amp;gt;
&amp;lt;br/&amp;gt;
&amp;lt;br/&amp;gt;&lt;/p&gt;
&lt;p&gt;BFS 구현&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for !q.Empty() {
		pos := q.Dequeue()

		if costBoard[pos.y][pos.x][pos.current_k] &amp;lt; pos.cost {
			continue
		}
		costBoard[pos.y][pos.x][pos.current_k] = pos.cost

		if pos.current_k &amp;lt; k {
			for i := 0; i &amp;lt; 8; i++ {
				ny = pos.y + hdy[i]
				nx = pos.x + hdx[i]

				if 0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; costBoard[ny][nx][pos.current_k+1] &amp;gt; pos.cost+1 {
					q.Enqueue(&amp;amp;Pos{x: nx, y: ny, cost: pos.cost + 1, current_k: pos.current_k + 1})
					costBoard[ny][nx][pos.current_k+1] = pos.cost + 1

				}
			}
		}
		for i := 0; i &amp;lt; 4; i++ {
			ny = pos.y + dy[i]
			nx = pos.x + dx[i]

			if 0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; costBoard[ny][nx][pos.current_k] &amp;gt; pos.cost+1 {
				q.Enqueue(&amp;amp;Pos{x: nx, y: ny, cost: pos.cost + 1, current_k: pos.current_k})
				costBoard[ny][nx][pos.current_k] = pos.cost + 1
			}
		}
	}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;솔직히 코드는 너무 직관적으로 쉬워서 금방 읽고 뭔 소린지 이해할 수 있을거라고 생각한다.&lt;/p&gt;
&lt;p&gt;기본적인 BFS와 다른점은 바로 costBoard를 3차원으로 선언한 것이데 이것은 k가 자연수일때, 아무때나 말과 같은 점프를 뛸 수 있는데
이를 고려하여, 현재 k가 몇 남아있냐에 따라서 각자 cost를 따로 먹여놓은 것이다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import (
	&quot;bufio&quot;
	&quot;fmt&quot;
	&quot;math&quot;
	&quot;os&quot;
)

type Pos struct {
	x, y, cost, current_k int
}

type Node struct {
	next *Node
	prev *Node
	item *Pos
}

type Queue struct {
	head, tail *Node
	size       int
}
type IQueue interface {
	Enqueue(*Pos)
	Init()
	Dequeue() *Pos
	Front() *Pos
	Size() int
	Empty() bool
}

func (q *Queue) Enqueue(pos *Pos) {
	var newNode *Node = &amp;amp;Node{next: q.tail, prev: q.tail.prev, item: pos}
	if q.head.next == nil {
		q.tail.prev = newNode
		q.head.next = newNode
		newNode.prev = q.head
	} else {
		q.tail.prev.next = newNode
		q.tail.prev = newNode
	}

	q.size++

}
func (q *Queue) Init() {
	q.head = &amp;amp;Node{next: nil, prev: nil, item: nil}
	q.tail = &amp;amp;Node{next: nil, prev: nil, item: nil}
	q.size = 0
}

func (q *Queue) Dequeue() *Pos {
	if q.head.next == nil {
		return nil
	}

	var remove *Pos = q.head.next.item

	if q.head.next.next == q.tail {
		q.head.next = nil
		q.tail.prev = nil
	} else {
		q.head.next = q.head.next.next
	}
	q.size--
	return remove
}
func (q *Queue) Front() *Pos {
	return q.head.item
}

func (q *Queue) Size() int {
	return q.size
}
func (q *Queue) Empty() bool {
	return q.size == 0
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)

	defer writer.Flush()

	var q IQueue = new(Queue)
	q.Init()
	hdx, hdy, dx, dy := []int{2, 2, -2, -2, 1, 1, -1, -1}, []int{1, -1, 1, -1, 2, -2, 2, -2}, []int{1, -1, 0, 0}, []int{0, 0, 1, -1}

	var k int
	fmt.Fscanln(reader, &amp;amp;k)
	q.Enqueue(&amp;amp;Pos{x: 0, y: 0, cost: 0, current_k: 0})
	var n, m int
	fmt.Fscanln(reader, &amp;amp;m, &amp;amp;n)

	var board [202][202]int
	var costBoard [202][202][32]int

	var limit int = math.MaxInt

	for i := 0; i &amp;lt; n; i++ {
		for j := 0; j &amp;lt; m; j++ {
			fmt.Fscan(reader, &amp;amp;board[i][j])
			if board[i][j] == 1 {
				for k := 0; k &amp;lt; 31; k++ {
					costBoard[i][j][k] = -1
				}

			} else {
				for k := 0; k &amp;lt; 31; k++ {
					costBoard[i][j][k] = limit
				}

			}
		}
	}

	var ny, nx int
	for !q.Empty() {
		pos := q.Dequeue()

		if costBoard[pos.y][pos.x][pos.current_k] &amp;lt; pos.cost {
			continue
		}
		costBoard[pos.y][pos.x][pos.current_k] = pos.cost

		if pos.current_k &amp;lt; k {
			for i := 0; i &amp;lt; 8; i++ {
				ny = pos.y + hdy[i]
				nx = pos.x + hdx[i]

				if 0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; costBoard[ny][nx][pos.current_k+1] &amp;gt; pos.cost+1 {
					q.Enqueue(&amp;amp;Pos{x: nx, y: ny, cost: pos.cost + 1, current_k: pos.current_k + 1})
					costBoard[ny][nx][pos.current_k+1] = pos.cost + 1

				}
			}
		}
		for i := 0; i &amp;lt; 4; i++ {
			ny = pos.y + dy[i]
			nx = pos.x + dx[i]

			if 0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; costBoard[ny][nx][pos.current_k] &amp;gt; pos.cost+1 {
				q.Enqueue(&amp;amp;Pos{x: nx, y: ny, cost: pos.cost + 1, current_k: pos.current_k})
				costBoard[ny][nx][pos.current_k] = pos.cost + 1
			}
		}
	}

	var result int = limit

	for i := 0; i &amp;lt;= k; i++ {

		if costBoard[n-1][m-1][i] != -1 &amp;amp;&amp;amp; costBoard[n-1][m-1][i] != limit {
			result = min(result, costBoard[n-1][m-1][i])
		}
	}

	if n == 1 &amp;amp;&amp;amp; m == 1 {
		result = 0
	}

	if result == limit {
		fmt.Fprintln(writer, -1)
	} else {
		fmt.Fprintln(writer, result)
	}
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 날카로운 눈</title><link>https://compy07.github.io/Blog/posts/boj/1637/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1637/</guid><description>재활 다시 시작할 때가..</description><pubDate>Thu, 15 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1637&quot;&gt;날카로운 눈&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 20000&amp;lt;br/&amp;gt;1 ≤ A, B, C ≤ 2,147,483,647)&lt;/td&gt;
&lt;td&gt;Binary Search&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;솔직히 처음에 엄청 해맸습니다. 문제가 하나만 홀수라는 포인트를 못 읽어서 아주 많이 생각을 해봤는데요.
음.. 문제가 너무 안 풀려서 처음으로 돌아가려니까 그제서야 보이더라구요.&lt;/p&gt;
&lt;p&gt;친구는 xor로 열심히 memoization 돌려서 풀더라구요? 엄청 신기하기도 했습니다.
저는 옆에서 binary search 열심히 돌려서 AC 맞았구요.&lt;/p&gt;
&lt;p&gt;힘들었던 필기를 소개합니당
&lt;img src=&quot;./img.jpeg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;그래도 정말 재미있게 풀었던 문제였습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;random&amp;gt;

#define mod 1000000007LL
//          1000000007
#define M 2147483647

using namespace std;
using ll = long long;
int n;

struct node{
    ll a, c, b;
};

node nodes[20001];


ll count(ll target){
    
    ll result = 0;
    for(int i = 0; i &amp;lt; n; i++){
        if(target &amp;gt;= nodes[i].a) result += (min(nodes[i].c, target) - nodes[i].a) / nodes[i].b + 1;
    }
    
    return result;
}


ll solution(){
    
    ll start = 1, finish = M;
    
    while(start &amp;lt;= finish){
        ll pivot = (start + finish) / 2;
        
        ll pivot_count = count(pivot);
//        cout &amp;lt;&amp;lt; &quot;pivot: &quot; &amp;lt;&amp;lt; pivot &amp;lt;&amp;lt; &quot; count: &quot; &amp;lt;&amp;lt; pivot_count &amp;lt;&amp;lt; &quot;\n&quot;;
        if((pivot_count - count(pivot - 1)) % 2 == 1) return pivot;
        
        if(pivot_count % 2 == 0) start = pivot + 1;
        else finish = pivot - 1;
    }
    return -1;
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
  
    cin &amp;gt;&amp;gt; n;
    for(int i = 0; i &amp;lt; n; i++){
        ll a, c, b;
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; c &amp;gt;&amp;gt; b;
        nodes[i] = {a, c, b};
    }
    
    
    ll result = solution();
    if(result == -1) cout &amp;lt;&amp;lt; &quot;NOTHING&quot;;
    else cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; count(result) - count(result-1);
    
    
    return 0;
}

/*
 6 5 1
 1 6 2 9 8
 7 2 6 9 8
 1 8 3 4 2
 7 4 6 2 3
 9 2 3 6 1
 4 2 9 3 1
 3
 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 괄호 추가하기</title><link>https://compy07.github.io/Blog/posts/boj/16637/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/16637/</guid><description>다시 재활 시작</description><pubDate>Wed, 02 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16637&quot;&gt;괄호 추가하기&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.5s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 19)&lt;/td&gt;
&lt;td&gt;BruteForce&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;각 괄호를 어떻게 칠까? 이거를 그냥 N의 범위가 엄청 작기 때문에 무조건 시간 안에 돌아갈 수 있다고 판단!
그래서 그거 토대로 구현하면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#define inf 1000000000
using namespace std;

using ll = long long;

int nums[20];
char operators[20];
int n;

void calc(int current, ll* value, ll calc_value){
    switch(operators[current-1]){
        case &apos;*&apos;:
            *value *= calc_value;
            break;
        case &apos;+&apos;:
            *value += calc_value;
            break;
        case &apos;-&apos;:
            *value -= calc_value;
            break;
    }
}

ll solution(int current, ll current_result){
    if(current &amp;gt;= n) return current_result;
    ll result = -inf;
    
    ll nonWrap = current_result;
    calc(current, &amp;amp;nonWrap, nums[current]);
    
    
    if(current &amp;lt; n-1){
        ll wrap = nums[current];
        calc(current+1, &amp;amp;wrap, nums[current+1]);
        
        
        ll wrap_result = current_result;
        calc(current, &amp;amp;wrap_result, wrap);
        
        
        result = max(result, solution(current+2, wrap_result));
    }
    result = max(result, solution(current+1, nonWrap));
    
    return result;
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; n;
    
    for(int i = 0; i &amp;lt; n; i++){
        if(i%2 == 0) cin &amp;gt;&amp;gt; nums[i/2];
        else cin &amp;gt;&amp;gt; operators[i/2];
    }
    
    
    cout &amp;lt;&amp;lt; solution(1, nums[0]);   
    
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 겹치는 선분</title><link>https://compy07.github.io/Blog/posts/boj/1689/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1689/</guid><description>재활 10일차</description><pubDate>Wed, 12 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1689&quot;&gt;겹치는 선분&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 1,000,000&amp;lt;br/&amp;gt; |e| ≤ 10억)&lt;/td&gt;
&lt;td&gt;Greedy, Sort&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이 문제는 솔직히... 아쉬웠따. 뭔가 그 게시판을 들어갔다가 잘못 봐버리면서 접근을 이미 약간 힌트를 얻어서
거의 그 이후로 바로 풀었는데.. 나는 반례있는지 확인하러 들어간 것 뿐인데.. 쩝&lt;/p&gt;
&lt;p&gt;하튼 그냥 정렬해서 잘 풀면 된다. 아이디어는 그렇게 어렵지도 완전 쉽지도 않은 아이디어였다.&lt;/p&gt;
&lt;p&gt;시간에 대해서 어떻게 줄이지라고 고민을하면 약간 자연스럽게 접근할 수 있는 정도였다.
&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;처음에 priority_queue로 풀고 있엇는데 이것도 되는 것 같다ㅏ. 근데 요게 더 효율적이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;

using ll = long long;
int n;


bool compare(pair&amp;lt;int, int&amp;gt; a, pair&amp;lt;int ,int&amp;gt; b){
    if(a.first == b.first) return a.second &amp;lt; b.second;
    return a.first &amp;lt; b.first;
}


int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    cin &amp;gt;&amp;gt; n;
    
    int start, end;
    
    vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; coor;
    for(int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; start &amp;gt;&amp;gt; end;
        
        coor.push_back({start, 1});
        coor.push_back({end, -1});
    }
    
    sort(coor.begin(), coor.end(), compare);
    
    int current = 0;
    int result = 0;
    for(int i = 0; i &amp;lt; n*2; i++){
//        cout &amp;lt;&amp;lt; coor[i].first &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt;coor[i].second &amp;lt;&amp;lt; &quot;\n&quot;;
        current += coor[i].second;
        result = max(result, current);
    }
    
    cout &amp;lt;&amp;lt; result;
    
    
    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 벽 부수고 이동하기 3</title><link>https://compy07.github.io/Blog/posts/boj/16933/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/16933/</guid><description>재활 6일차</description><pubDate>Mon, 14 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16933&quot;&gt;벽 부수고 이동하기 3&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N, M ≤ 1000&amp;lt;br/&amp;gt;1 ≤ K ≤ 10)&lt;/td&gt;
&lt;td&gt;Implementation, BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;전형적인 BFS 문제로 연습하기 좋은 문제인 것 같습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;
using ll = long long;
int n, m, k;

char board[1001][1001];
ll visited[1001][1001][11][2];


ll solution(){
    
    queue&amp;lt;pair&amp;lt;pair&amp;lt;int, bool&amp;gt;, pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; q;
    
    q.push({{0, false}, {0, 0}});
    visited[0][0][0][0] = 0; // 0 ㄴㅏㅈ, 1 ㅂㅏㅁ
    visited[0][0][0][1] = 1; // 0 ㄴㅏㅈ, 1 ㅂㅏㅁ
    
    int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, -1, 1};
    
    while(!q.empty()){
        auto [__, _] = q.front(); q.pop();
        auto [y, x] = _;
        auto [ck, state] = __;
        
        
        for(int i = 0; i &amp;lt; 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];
            
            if(0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; 0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n){
                
                if(board[ny][nx] == &apos;1&apos;){
                    if(ck &amp;gt;= k) continue;
                    if(visited[ny][nx][ck+1][!state] &amp;lt;= visited[y][x][ck][state] + 1) continue;
                    
                    if(state){
                        visited[y][x][ck][!state] = min(visited[y][x][ck][state] + 1, visited[y][x][ck][!state]);
                        q.push({{ck, !state}, {y, x}});
                        continue;
                    }
                    
                    visited[ny][nx][ck+1][!state] = visited[y][x][ck][state] + 1;
                    q.push({{ck+1, !state}, {ny, nx}});
                }else{
                    if(visited[ny][nx][ck][!state] &amp;lt;= visited[y][x][ck][state] + 1) continue;
                    
                    visited[ny][nx][ck][!state] = visited[y][x][ck][state] + 1;
                    q.push({{ck, !state}, {ny, nx}});
                }
                
            }
        }
        
    }
    
    ll result = 1e15;
    
    for(int i = 0; i &amp;lt;= k; i++) {
        result = min(result, min(visited[n-1][m-1][i][0], visited[n-1][m-1][i][1]));
    }
    
//
//    for(int i = 0; i &amp;lt; n; i++){
//        for(int j = 0; j &amp;lt; m; j++){
//            
//            cout &amp;lt;&amp;lt; visited[i][j][7][0] &amp;lt;&amp;lt; &quot; &quot;;
//        }
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//    }
//    
//    cout &amp;lt;&amp;lt; &quot;\n&quot;;
    
    return result+ 1LL;
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; k;
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m; j++){
            cin &amp;gt;&amp;gt; board[i][j];
            
            for(int t = 0; t &amp;lt;= k; t++){
                visited[i][j][t][0] = 1e15;
                visited[i][j][t][1] = 1e15;
            }
        }
    }
    
    ll result = solution();
    if(result &amp;gt;= 1e15) cout &amp;lt;&amp;lt; -1;
    else cout &amp;lt;&amp;lt; result;
    
    
    
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 연구소 3</title><link>https://compy07.github.io/Blog/posts/boj/17142/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/17142/</guid><description>재활 4일차</description><pubDate>Tue, 04 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17142&quot;&gt;연구소 3&lt;/a&gt;&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.25s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(4 ≤ N ≤ 50, 1 ≤ M ≤ 10)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 바이러스는 활성 상태와 비활성 상태가 있다. 가장 처음에 모든 바이러스는 비활성 상태이고, 활성 상태인 바이러스는 상하좌우로 인접한 모든 빈 칸으로 동시에 복제되며, 1초가 걸린다. 승원이는 연구소의 바이러스 M개를 활성 상태로 변경하려고 한다.&lt;/p&gt;
&lt;p&gt;연구소는 크기가 N×N인 정사각형으로 나타낼 수 있으며, 정사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽, 바이러스로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다. 활성 바이러스가 비활성 바이러스가 있는 칸으로 가면 비활성 바이러스가 활성으로 변한다.&lt;/p&gt;
&lt;p&gt;예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자. 0은 빈 칸, 1은 벽, 2는 바이러스의 위치이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2 0 0 0 1 1 0&lt;/li&gt;
&lt;li&gt;0 0 1 0 1 2 0&lt;/li&gt;
&lt;li&gt;0 1 1 0 1 0 0&lt;/li&gt;
&lt;li&gt;0 1 0 0 0 0 0&lt;/li&gt;
&lt;li&gt;0 0 0 2 0 1 1&lt;/li&gt;
&lt;li&gt;0 1 0 0 0 0 0&lt;/li&gt;
&lt;li&gt;2 1 0 0 0 0 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;M = 3이고, 바이러스를 아래와 같이 활성 상태로 변경한 경우 6초면 모든 칸에 바이러스를 퍼뜨릴 수 있다. 벽은 -, 비활성 바이러스는 *, 활성 바이러스는 0, 빈 칸은 바이러스가 퍼지는 시간으로 표시했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;* 6 5 4 - - 2&lt;/li&gt;
&lt;li&gt;5 6 - 3 - 0 1&lt;/li&gt;
&lt;li&gt;4 - - 2 - 1 2&lt;/li&gt;
&lt;li&gt;3 - 2 1 2 2 3&lt;/li&gt;
&lt;li&gt;2 2 1 0 1 - -&lt;/li&gt;
&lt;li&gt;1 - 2 1 2 3 4&lt;/li&gt;
&lt;li&gt;0 - 3 2 3 4 *&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;시간이 최소가 되는 방법은 아래와 같고, 4초만에 모든 칸에 바이러스를 퍼뜨릴 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 1 2 3 - - 2&lt;/li&gt;
&lt;li&gt;1 2 - 3 - 0 1&lt;/li&gt;
&lt;li&gt;2 - - 2 - 1 2&lt;/li&gt;
&lt;li&gt;3 - 2 1 2 2 3&lt;/li&gt;
&lt;li&gt;3 2 1 0 1 - -&lt;/li&gt;
&lt;li&gt;4 - 2 1 2 3 4&lt;/li&gt;
&lt;li&gt;* - 3 2 3 4 *&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;연구소의 상태가 주어졌을 때, 모든 빈 칸에 바이러스를 퍼뜨리는 최소 시간을 구해보자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;정말 충격이였다.. 처음에 짠 bfs 코드를 문제를 또 잘못 읽고서 풀어서 다른 문제를 풀어버려쏙 다시 풀어서 제출하니 시간초과 어머나....&lt;/p&gt;
&lt;p&gt;그래서 다시 보니 아 이걸 중복 검사가 있어서 다시 제출 그러다 다시 시간 초과...&lt;/p&gt;
&lt;p&gt;아 그 이후에 dfs를 다시 중복 없이 검사 및 빨리 끝나도록 설계해서 다시 제출 그러나 또 시간초과&lt;/p&gt;
&lt;p&gt;알고보니까 bfs 코드를 조금 다시 보니 다익스트라 마냥 짜놔서 이거 방문 이후에 검사하는 방식이여서 최악의 경우 한 노드당 방문 수가 4가 되면서 O(N^4)을 찍어ㅑ버렸다.&lt;/p&gt;
&lt;p&gt;미치겠다.. 이걸 왜 이렇게 짠 걸까.. 총 문제 풀이 시간은 길지 않았으나.. 이렇게 틀리고, 멘탈이 흔들리니 아직도 많이 방심하는 성격이 있는 것 같다. 항상 긴장하고 코드 한줄한줄마다 시간 복잡도 생각하면서 작성해야겠다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;
부끄럽네요...&lt;/p&gt;
&lt;p&gt;다음부터는 좋은 코드로 돌아오겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

const int INF = 1e9;
const int MAX_N = 51;
int n, m;
int board[MAX_N][MAX_N], use[MAX_N][MAX_N];
pair&amp;lt;int, int&amp;gt; vir[10];
int virCnt = 0;
int emptySpace = 0;

const int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};

void init(){
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; n; j++){
            if(board[i][j] == 1) use[i][j] = -1;
            else use[i][j] = INF;
        }
    }
}

int solution(vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;amp; current){
    init();
    queue&amp;lt;pair&amp;lt;int, pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; q;
    int filled = 0;
    
    for(auto&amp;amp; p : current) {
        q.push({0, p});
        use[p.first][p.second] = 0;
    }
    
    int maxTime = 0;
    while(!q.empty() &amp;amp;&amp;amp; filled &amp;lt; emptySpace){
        auto [time, pos] = q.front();
        auto [y, x] = pos;
        q.pop();
        
        if(board[y][x] != 2) {
            maxTime = time;
            filled++;
        }
        
        for(int i = 0; i &amp;lt; 4; i++){
            int ny = y + dy[i];
            int nx = x + dx[i];
            if(ny &amp;lt; 0 || ny &amp;gt;= n || nx &amp;lt; 0 || nx &amp;gt;= n || use[ny][nx] != INF) continue;
            use[ny][nx] = time + 1;
            q.push({time + 1, {ny, nx}});
        }
    }
    
    return filled == emptySpace ? maxTime : INF;
}

int search(int idx, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;amp; current){
    if(current.size() == m) return solution(current);
    if(idx &amp;gt;= virCnt || virCnt - idx &amp;lt; m - current.size()) return INF;
    
    int result = INF;
    
    current.push_back(vir[idx]);
    result = min(result, search(idx + 1, current));
    current.pop_back();
    
    result = min(result, search(idx + 1, current));
    
    return result;
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; n; j++){
            cin &amp;gt;&amp;gt; board[i][j];
            if(board[i][j] == 2) vir[virCnt++] = {i, j};
            else if(board[i][j] == 0) emptySpace++;
        }
    }
    
    vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; vec;
    int result = search(0, vec);
    
    cout &amp;lt;&amp;lt; (result == INF ? -1 : result);
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] A Plus Equals B</title><link>https://compy07.github.io/Blog/posts/boj/17167/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/17167/</guid><description>17167 풀이를 해봅시다.</description><pubDate>Sun, 30 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17167&quot;&gt;A Plus Equals B&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;=A, B&amp;lt;=10¹⁸, 0&amp;lt;= n &amp;lt;= 5000)&lt;/td&gt;
&lt;td&gt;Math(수학)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;일단 문제가 영어러 되어있기 때문에 제가 이해한 문장을 적겠습니다.(원문을 보시고 싶다면, 위의 링크를 클릭해 문제를 풀고 오십숑)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A, B라는 두개의 자연수가 있다. 우리는 이 A, B 두 수를 같게 만들어야한다.
하지만 이 수를 같게 만드려면 아래의 4가지 방법 밖에 사용해야 한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A += A&lt;/li&gt;
&lt;li&gt;A += B&lt;/li&gt;
&lt;li&gt;B += A&lt;/li&gt;
&lt;li&gt;B += B&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 4가지의 과정을 잘 섞어서 두 수를 같게 만들어보자. 그런데 이 연산이 5000번은 넘으면 안된다.&lt;/p&gt;
&lt;p&gt;답을 찾았다면, 연산 횟수를 먼저 출력하고 다음 줄부터 어떤 연산을 하였는지 차례대로 출력하여라!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;내가 이해한 문제는 이렇다.&lt;/p&gt;
&lt;p&gt;첨에는 음.. bfs로 다 찾는건가? 이러고 짜다가 alt+tab 이러고 읽고 풀고, 읽고 풀고 하다가 시간 초과가 의심되기 시작하면서,
머리가 띵해졌다....(일단 overflow문제도 뭔가 찜찜했었음..)&lt;/p&gt;
&lt;p&gt;그러다가 A+=A, B+=B가 2배로 높아진다는 것을 알았다.&lt;/p&gt;
&lt;p&gt;자 이제부터 어메이징한 것들이 나올 것이다.&lt;/p&gt;
&lt;p&gt;A+=A 이것을 잘 생각해보면, B/=2와 같은 동작을 한다는 것을 알 수 있다.&lt;/p&gt;
&lt;h2&gt;왜?라고 생각할 수 있다. 자 생각해보자&lt;/h2&gt;
&lt;p&gt;-- 일단 나누는 수는 당연히 짝수라고 생각한다. --&lt;/p&gt;
&lt;p&gt;어... 이 아이디어가 굉장히 어메이징한데....&lt;/p&gt;
&lt;p&gt;어떤 수 A가 존재한다고 하자.
A+=A를 진행하면 A는 2A가 된다.
다음 A가 같은 연산을 한다면 4A가 된다.&lt;/p&gt;
&lt;p&gt;자 여기까진 이해가 쉽다.&lt;/p&gt;
&lt;p&gt;이제 왜 A/=2가 B+=B와 같은 연산이라는건지 설명하겠다.&lt;/p&gt;
&lt;p&gt;A=8, B=4 라고 생각해보자
이럴 때는 B*2 = A가 성립하면서, 굉장히 직관적으로 설명이 가능하다.&lt;/p&gt;
&lt;p&gt;만약 A=100, B = 2 라고 생각한다면,
여러분들의 머릿속에는 &quot;??&quot; 또는 &quot;아하&quot;가 나올 것이다.&lt;/p&gt;
&lt;p&gt;먼저 결국 우리가 연산을 진행하면서 overflow를 막기 위해서 또한 좀 문제를 직관적으로 보기 위해서 계속해서 수를 좀 작게 만들고 있는데,
A/2는 50인데 왜 2B하는 것과 같은 효과가 나오는 거야?라고 할텐데.&lt;/p&gt;
&lt;p&gt;if문에서 보는 a, b는 홀수이다. (이건 명백하다)&lt;/p&gt;
&lt;p&gt;그때 a+b를 한다면 무조건 짝수가 나온다.&lt;/p&gt;
&lt;p&gt;A = 3, B = 9
이때, B+=A를 하게되면 B= 12가 된다.
이때 무조건 A가 2배를 해도 B보다 작다는 것은 알 수 있다. 그래서 A+=A해도 B와 작다. -&amp;gt; 그래서 반복
계속해서 이 과정을 반복하면 되는데, 이때 짝수가된 B를 계속해서 나눠주는 이유는 이와 같이 결국 A, B의 관계에서 &quot;A+=A해도 B와 작다 -&amp;gt; 반복&quot; 이것을 반복하면 점점 더 가까워지기 때문에 문제가 없을 것이라는 것을 알고 있기 때문이다.&lt;/p&gt;
&lt;p&gt;5000번 이상의 연산이 없을 거란걸 생각하지 않고 제출하였는데, 넘어가는 것을 보니 그런 케이스는 딱히 없는 것같다.
이것 또한 기회가 된다면 찾아서 업데이트를 해놓도록 하겠다.(아마 안 할거 같긴해용)&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MAX 2&apos;100&apos;000&apos;000
#define limit 10&apos;000&apos;000&apos;000
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
#include &amp;lt;math.h&amp;gt;
#include &amp;lt;random&amp;gt;
using namespace std;

using ull = unsigned long long;
using ll = long long;

ull a, b;

int main() {
   
   
    cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;
    vector&amp;lt;string&amp;gt; result;
    
    
    while(a != b){
        
        while(a%2 == 0){
            a/=2; // == b+=b
            result.push_back(&quot;B+=B&quot;);
        }
        while(b%2 == 0){
            b/=2; // == a+=a
            result.push_back(&quot;A+=A&quot;);
        }
        
        // cout &amp;lt;&amp;lt; 1;
        if(a &amp;gt; b){
            a+=b;
            result.push_back(&quot;A+=B&quot;);

        }
        else if(a &amp;lt; b){
            b+=a;
            result.push_back(&quot;B+=A&quot;);
        }
        
        
    }
    // cout &amp;lt;&amp;lt; a &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; b &amp;lt;&amp;lt; &quot;\n&quot;;
    
    cout &amp;lt;&amp;lt; result.size() &amp;lt;&amp;lt; &quot;\n&quot;;
    for(string str : result){
        cout &amp;lt;&amp;lt; str&amp;lt;&amp;lt;&quot;\n&quot;;
    }
    
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 점 나누기</title><link>https://compy07.github.io/Blog/posts/boj/1723/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1723/</guid><description>재활 3일차...</description><pubDate>Mon, 03 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1723&quot;&gt;점 나누기&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(3&amp;lt;= N &amp;lt;= 10,000&amp;lt;br/&amp;gt;3 &amp;lt;= K &amp;lt;= 1,000)&lt;/td&gt;
&lt;td&gt;BruteForce, Two Pointer(?)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;N개의 점들이 원의 내부에 찍혀 있다. 이 원을 K개의 부채꼴로 등분하려 한다. 즉, 각 부채꼴의 중심각이 360/K도가 되어야 한다. 부채꼴을 어떻게 나누느냐에 따라서 각 부채꼴에 찍혀 있는 점의 개수가 달라질 수 있다.&lt;/p&gt;
&lt;p&gt;N개의 점들이 찍혀 있는 각도가 주어졌을 때, 가장 많은 점이 찍혀 있는 부채꼴에 찍혀 있는 점의 개수와, 가장 적은 점이 찍혀 있는 부채꼴에 찍혀 있는 점의 개수의 차이의 최솟값을 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;p&gt;단, 부채꼴의 테두리에 점이 포함되어서는 안 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;디게 오랜만에 문제가 굉장히 재미있었다. 머리가 말랑말랑 해지는 기분이랄까.. 확실히 유사코 문제가 재밌는게 많은 듯 하다.&lt;/p&gt;
&lt;p&gt;처음에 어? 이거 그냥 브포 돌리면 되겠는데 했는데 다행이 되어서 다행이다 나중에 다른 코드들 보니까 브포 보다는 그냥 이분탐색만 있어서 약간 당황&lt;/p&gt;
&lt;p&gt;그리고 그 벡터쓰는 버릇을 없애야겠다. 이게 나중가니까 너무 벡터 함수에 의존하는 경향이 생기는 것 같아서 최대한 함수 없이 구현하려고 노력하고 있다. 다음부터는 배열을 주로 사용하기로!&lt;/p&gt;
&lt;p&gt;처음에 틀렸던 것은 360/K로 해서 등분하는게 아니라 K개의 선을 그어서 나오는거 찾는 줄 알았따. 정말 놀랍게도 그렇게 두번을 제출했고 레전드를 찍었다.&lt;/p&gt;
&lt;p&gt;그 이후에 다시 읽고서 아 등분이구나! 하고 다시 풀어서 시초(time out)맞고 다시 풀어서 AC 맞았따.. 문제 풀면서 디게 재밌게 풀었고 좀 기억에 남을 듯 하다.&lt;/p&gt;
&lt;p&gt;코드는 약간 설명하자면,
나중에 점 계산 빠르게 돌리기 위해서 정렬 돌린다.
어차피 점을 나누는게 제일 중요하기 때문에 다른 곳은 볼 필요가 없다고 생각했다.
&lt;strong&gt;이 문제의 경우 최적의 부채꼴 분할은 반드시 어떤 점을 정확히 부채꼴의 경계에 두는 경우를 포함하기 때문에&lt;/strong&gt;
그 점을 시작으로 잡고 돌렸다.&lt;/p&gt;
&lt;p&gt;그러고 정렬된거 토대로 개수를 세주고 이후에 result에 반영&lt;/p&gt;
&lt;p&gt;그 360 더하는 거는 진짜 와... 이걸 어찌저찌 해봐서 됐는데&lt;/p&gt;
&lt;p&gt;만약에 300도에서 시작하는 점이 있으면, 여기는 k가 3일 때 120도 만큼 잘라야되니.. 넘어가죠 이걸 고려해서 연산을 쉽게하려고 하는 겁니다ㅏ&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;요런 문제의 경우 파이썬이 아니니까 data type의 범위 잘 신경쓰시고 오차도 신경을 좀 써주셔야 될 것 같아용!!
이 문제는 그런게 덜하지만 다른 문제는 좀 이상하게 나오는 경우가 있다고 들었거든요!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    int N, K;
    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; K;
    
    vector&amp;lt;double&amp;gt; angles(N);
    for (int i = 0; i &amp;lt; N; i++) cin &amp;gt;&amp;gt; angles[i];
    sort(angles.begin(), angles.end());
    
    vector&amp;lt;double&amp;gt; extended_angles = angles;
    for (double angle : angles) {
        extended_angles.push_back(angle + 360);
    }
    
    ll realResult = 1e12;
    double sector_size = 360.0 / K;
    
    for (int i = 0; i &amp;lt; N; i++) {
        vector&amp;lt;ll&amp;gt; result(K, 0);
        double start = angles[i];
        
        int left = 0;
        for (int j = 0; j &amp;lt; K; j++) {
            double sectorStart = start + j * sector_size;
            double sectorEnd = sectorStart + sector_size;
            
            while (left &amp;lt; extended_angles.size() &amp;amp;&amp;amp; extended_angles[left] &amp;lt; sectorEnd) {
                if (extended_angles[left] &amp;gt;= sectorStart) {
                    result[j]++;
                }
                left++;
            }
        }
        
        if (!result.empty()) {
            ll maxResult = 0;
            ll minResult = 1e12;
            for (ll value : result) {
                maxResult = max(maxResult, value);
                minResult = min(minResult, value);
            }
            realResult = min(realResult, maxResult - minResult);
        }
    }
    
    cout &amp;lt;&amp;lt; realResult;
    return 0;
}


// 345 94 218
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이거 자기 전에 풀어서 그런가.. 스네이크랑 카멜이랑 섞여 있네요 와 ㄹㅈㄷ 이게 요즘 코드에서도 발견되네
&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 로봇</title><link>https://compy07.github.io/Blog/posts/boj/1726/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1726/</guid><description>재활 23일차...</description><pubDate>Wed, 26 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1726&quot;&gt;로봇&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N, M ≤ 100)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;일단 이 문제는 방향에 대해서 visited 처리 해주면 된다. 일단 내가 BFS를 작성할 때 cost를 저장하는 배열과 비교하는 연산을 &amp;lt;code&amp;gt;&amp;lt;=&amp;lt;/code&amp;gt;으로 처리하는 것을 많이 사용한 나머지... 그대로 썼다가...
이거 찾는데 30분을 날렸다. 진짜 미치겠다. 하튼 그냥 그렇게 bfs 돌리면 풀리는 문제이다..&lt;/p&gt;
&lt;p&gt;즐거운 ps 하셔요..ㅠㅜ&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;
using ll = long long;

int n, m;

int board[101][101];
int costBoard[101][101][4];

int dx[] = {0, 1, -1, 0, 0}, dy[] = {0, 0, 0, 1, -1};

struct pos{
    int dir;
    int y, x;
    int pre_dir;
    int cost;
    int nc;
    
};


int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    
    
    for(int i = 1; i &amp;lt;= n; i++){
        for(int j = 1; j &amp;lt;= m; j++){
            costBoard[i][j][0] = 1000000000;
            costBoard[i][j][1] = 1000000000;
            costBoard[i][j][2] = 1000000000;
            costBoard[i][j][3] = 1000000000;
            cin &amp;gt;&amp;gt; board[i][j];
        }
    }

    pos target;
    
    queue&amp;lt;pos&amp;gt; q;
    
    int sd, sy, sx;
    
    cin &amp;gt;&amp;gt; sy &amp;gt;&amp;gt; sx &amp;gt;&amp;gt; sd;
    
    q.push({sd, sy, sx, 10000, 0, 0});
    
    cin &amp;gt;&amp;gt; sy &amp;gt;&amp;gt; sx &amp;gt;&amp;gt; sd;
    
    target = {sd, sy, sx, 10000, 0, 0};

    while(!q.empty()){
        pos current = q.front(); q.pop();
        
        
        if(costBoard[current.y][current.x][current.dir-1] &amp;lt; current.cost) continue;
        
        
        if(current.y == target.y &amp;amp;&amp;amp; target.x == current.x) {
            // 아니 min을 안 넣었네..
            int result;
            if(current.dir == target.dir) result = current.cost;
            else result = current.cost + 1 + (abs(current.dir - target.dir) == 1 &amp;amp;&amp;amp; (target.dir == 1 || target.dir == 4 || current.dir == 1 || current.dir == 4));
            
            costBoard[current.y][current.x][target.dir-1] = min(costBoard[current.y][current.x][target.dir-1], result);
            continue;
        }
        else costBoard[current.y][current.x][current.dir-1] = current.cost;
        
        
        
        
        
        for(int i = 1; i &amp;lt; 5; i++){
            int nx = current.x + dx[i];
            int ny = current.y + dy[i];
            
            if(0 &amp;lt; ny &amp;amp;&amp;amp; ny &amp;lt;= n &amp;amp;&amp;amp; 0 &amp;lt; nx &amp;amp;&amp;amp; nx &amp;lt;= m){
                if(costBoard[ny][nx][i-1] &amp;lt; current.cost || board[ny][nx]) continue;
                    
                int current_cost = current.cost;
                int current_nc = current.nc;
                current_cost+=2;
                
                if(current.dir == i){
                    
                    current_cost -=2;
                    current_cost+=(current.pre_dir &amp;gt; 100 &amp;amp;&amp;amp; current.dir == i) + (current_nc &amp;gt;= 3);
                    if(current_nc &amp;gt;= 3) current_nc = 0;
                    current_nc++;
                    
                }
                else if(abs(current.dir - i) == 1 &amp;amp;&amp;amp; (i == 1 || i == 4 || current.dir == 1 || current.dir == 4)){
                    current_cost++;
                    current_nc = 1;
                }else {
                    current_nc = 1;
                }
                // 서쪽.. 2 북 4
                
                
                q.push({i, ny, nx, current.dir, current_cost, current_nc});
            }
            
        }
        
//        
//        cout &amp;lt;&amp;lt; &quot;current: &quot; &amp;lt;&amp;lt; current.y &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; current.x &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; current.cost &amp;lt;&amp;lt; &quot;\n&quot;;
//
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//        for(int i = 1; i &amp;lt;= n; i++){
//            for(int j = 1; j &amp;lt;= m; j++) {
//                if(costBoard[i][j][1] == 1000000000) cout &amp;lt;&amp;lt; &quot;o &quot;;
//                else cout &amp;lt;&amp;lt; costBoard[i][j][1] &amp;lt;&amp;lt; &quot; &quot;;
//            }
//            cout &amp;lt;&amp;lt; &quot;\n&quot;;
//        }
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//        
//        
        
    }

    cout &amp;lt;&amp;lt; costBoard[target.y][target.x][target.dir-1];
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 오큰수</title><link>https://compy07.github.io/Blog/posts/boj/17298/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/17298/</guid><description>리뷰.. 오늘은 많이 피곤한 날이라 문제는 못 풀고, 괜찮은 문제 풀어봅니다.</description><pubDate>Thu, 20 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17298&quot;&gt;오큰수&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 1,000,000&amp;lt;br/&amp;gt;1 ≤ Ai ≤ 1,000,000)&lt;/td&gt;
&lt;td&gt;Stack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;전형적인 모노톤 스택 연습 문제입니다.&lt;/p&gt;
&lt;p&gt;:::tip[모노톤 스택?]&lt;/p&gt;
&lt;h2&gt;모노톤 스택(Monotonic Stack)이란?&lt;/h2&gt;
&lt;p&gt;모노톤(Monotonic)이라는 말은 &quot;단조로운&quot;, &quot;한 방향으로 증가/감소하는&quot; 이라는 뜻을 가지고 있습니다. 그래서 이걸 스택에
적용해보면 어떻게 이해할 수 있을까요?&lt;/p&gt;
&lt;p&gt;바로 스택 내의 모든 요소들이 일정한 순서(오름차순 또는 내림차순)을 유지하는 형태의 스택을 말한답니다&lt;/p&gt;
&lt;h2&gt;간단한 작동 원리&lt;/h2&gt;
&lt;p&gt;제가 핵심 원리로 생각하는 것은 새로운 요소를 추가할 때마다 이 스택의 단조성을 유지하는 것입니다.&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;모노톤 스택은 당연히 시간 복잡도가 O(N)으로 단순히 본 문제를 풀면 시간 초과가 나는 코드를 효과적을 줄일 때도 쓰이는 등 정말
여러 문제에서 쓰입니다. 뭐 빌딩이 보인다. 내 친구가 보인다 등등 뭐 이런 문제에서 많이 쓰이더라구요!&lt;/p&gt;
&lt;p&gt;그래서 이런 모노톤 스택 연습해보시길 강추 드립니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;stack&amp;gt;

using namespace std;

void print(int* res, int n){
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
    for(int i = 0; i &amp;lt; n; i++){
        cout &amp;lt;&amp;lt; res[i] &amp;lt;&amp;lt; &quot; &quot;;
    }
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    string s;
    int n;
    cin &amp;gt;&amp;gt; n;
    
    stack&amp;lt;int&amp;gt; stack;
    int result[1000000];
    int origin[1000000];

    for(int i = n - 1; i &amp;gt; -1; i--){
        result[i] = -1;
        origin[i] = -1;
        cin &amp;gt;&amp;gt; origin[i];
        while(!stack.empty() &amp;amp;&amp;amp; origin[i] &amp;gt; origin[stack.top()]){
            result[stack.top()] = origin[i];
            stack.pop();
        }
        //print(result, n);
        //cout &amp;lt;&amp;lt; origin[i] &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; (stack.empty() ? 0 : stack.top()) &amp;lt;&amp;lt; &quot;\n\n&quot;;
        stack.push(i);
    }
    
    for(int i = n-1; i &amp;gt; -1; i--){
        cout &amp;lt;&amp;lt; result[i] &amp;lt;&amp;lt; &quot; &quot;;
    }
    
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 쇼핑몰</title><link>https://compy07.github.io/Blog/posts/boj/17612/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/17612/</guid><description>재활 16일차</description><pubDate>Tue, 18 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17612&quot;&gt;쇼핑몰&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 100,000&amp;lt;br/&amp;gt;1 ≤ k ≤ 100,000&amp;lt;br/&amp;gt;1 ≤ id_i ≤ 1,000,000&amp;lt;br/&amp;gt;1 ≤ w_i ≤ 20)&lt;/td&gt;
&lt;td&gt;Greedy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;문제는 전형적인 priority_queue.. 즉 우선순위 큐를 활용한 풀이가 가능하다라는 것을 엄청 강하게 풍기고 있다..&lt;/p&gt;
&lt;p&gt;나는 그걸 알고 바로 들어갔는데 코드가 와... 이상한 곳에 빠져서 엄청 고민했다.&lt;/p&gt;
&lt;p&gt;실시간 업데이트를 시키면서 진행하는데.. 이때 같은 시간에 있는 소비자들을 전부 처리하고 다음에 들어오는 사람들을 받은게 아니라 한명 나가면 바로 업데이트 하고 들어오게 하면서
순서가 꼬이게 되었다. 또 마지막에 K의 길이가 1인 case에 대해서도 한번 틀리고서야 반례 찾고 고쳤는데... 아 정말 이런 디테일에 있어서
아직 나는 많이 부족한 것 같다.&lt;/p&gt;
&lt;p&gt;지금 골드를 최대한 1시간 이내로 푸는 목표인데... 수학, dp 같은 경우는 2시간 정도 안에는 무조건 푸는걸로 하고 있는데... 이문제는 무려 1시간 10분 정도 잡고 있었다.&lt;/p&gt;
&lt;p&gt;으아ㅏㅏㅏ 아직도 갈 길이 멀다.. 계속해서 성장해 나가자..&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;
using ll = long long;

struct consumer {
    ll cost, id, k;
    
    
};
struct CompareConsumer {
    bool operator()(const consumer&amp;amp; a, const consumer&amp;amp; b) {
        if(a.cost == b.cost) return a.k &amp;lt; b.k;
        return a.cost &amp;gt; b.cost;
    }
};

pair&amp;lt;ll, ll&amp;gt; consumers[100&apos;000];

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    
    priority_queue&amp;lt;consumer, vector&amp;lt;consumer&amp;gt;, CompareConsumer&amp;gt; q;
    priority_queue&amp;lt;ll, vector&amp;lt;ll&amp;gt;, greater&amp;lt;ll&amp;gt;&amp;gt; ks;
    ll n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    ll a_, b_;
    for(int i = 0; i &amp;lt; n; i++){
        if(i &amp;lt; m) ks.push(i);
        cin &amp;gt;&amp;gt; a_ &amp;gt;&amp;gt; b_;
        consumers[i] = {a_, b_}; // id, cost
    }

    
    ll current = 0;
    ll idx = 0;
    
    ll result = 0;
    ll result_idx = 1LL;
    
    do{
        if(q.size() &amp;lt; m &amp;amp;&amp;amp; idx &amp;lt; n) {
            ll k = ks.top(); ks.pop();
            auto [a, b] = consumers[idx++];
            q.push({current + b, a, k});
//            cout &amp;lt;&amp;lt; &quot;in: &quot; &amp;lt;&amp;lt; current + b &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; a &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; k &amp;lt;&amp;lt; &quot;\n&quot;;
            continue;
        }
        ll back = -1;
        do{
            auto [a, b, k] = q.top(); q.pop();
            ks.push(k);
//            cout &amp;lt;&amp;lt; &quot;out: &quot; &amp;lt;&amp;lt; a &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; b &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; k &amp;lt;&amp;lt; &quot;\n&quot;;
            if(a != current) current = a;
//            cout &amp;lt;&amp;lt; b*result_idx &amp;lt;&amp;lt; &quot;\n&quot;;
            result+= b*result_idx++;
            back = a;
        }while(!q.empty() &amp;amp;&amp;amp; back == q.top().cost);
//        cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
    }while(!q.empty() || idx &amp;lt; n);
    
    
    cout &amp;lt;&amp;lt; result;
    return 0;
}

/*
 5 1
 1 1
 2 1
 3 1
 4 1
 5 1
 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 컵라면</title><link>https://compy07.github.io/Blog/posts/boj/1781/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1781/</guid><description>재활 1일차</description><pubDate>Sun, 21 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1781&quot;&gt;컵라면&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;1 ≤ N ≤ 200,000&lt;/td&gt;
&lt;td&gt;Sort, Greedy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;문제는 대놓고 그리디 냄새를 냈지만, 생각보다 간단한 문제임에도 불구하고
오래걸렸다...&lt;/p&gt;
&lt;p&gt;간단히 현재 시간까지의 최대값을 유지하면서 진행을 해버리면 됩니다~~&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;

using ll = long long;

int N;

pair&amp;lt;int, int&amp;gt; problems[200&apos;001];
int visited[200&apos;001];
bool compare(const std::pair&amp;lt;int, int&amp;gt;&amp;amp; a, const std::pair&amp;lt;int, int&amp;gt;&amp;amp; b) {
    return a.second &amp;lt; b.second;
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin &amp;gt;&amp;gt; N;
    
    priority_queue&amp;lt;pair&amp;lt;int, int&amp;gt;, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;, greater&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; q;
    
    for(int i = 0; i &amp;lt; N; i++){
        cin &amp;gt;&amp;gt; problems[i].second &amp;gt;&amp;gt; problems[i].first;
//        q.push({problems[i].second, problems[i].first});
//        visited[i+1] = -1;
    }
    sort(problems, problems+N, compare);
    
    
    q.push(problems[0]);
    for(int i = 1; i &amp;lt; N; i++){
        auto [cost, current] = problems[i];
        
        if(current &amp;gt; q.size()){
            q.push(problems[i]);
//            cout &amp;lt;&amp;lt;&quot;size: &quot; &amp;lt;&amp;lt; q.size() &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; current &amp;lt;&amp;lt; &quot;\n&quot;;
        }
        else{
//            cout &amp;lt;&amp;lt; &quot;cost: &quot; &amp;lt;&amp;lt; cost &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; q.top().first &amp;lt;&amp;lt; &quot;\n&quot;;
            if(cost &amp;lt;= q.top().first) continue;
//            cout &amp;lt;&amp;lt; &quot;cost: &quot; &amp;lt;&amp;lt; cost &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; q.top().first &amp;lt;&amp;lt; &quot;\n&quot;;

            q.pop();
            q.push({problems[i]});
        }
        
//        cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    
    
    int result = 0;
    while(!q.empty()){
//        cout &amp;lt;&amp;lt; q.top().first &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; q.top().second &amp;lt;&amp;lt; &quot;\n&quot;;
        result += q.top().first; q.pop();
        
    }

    
    cout &amp;lt;&amp;lt; result;
    return 0;
}




&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] K번째 최단경로 찾기</title><link>https://compy07.github.io/Blog/posts/boj/1854/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1854/</guid><description>재활 7일차</description><pubDate>Sun, 09 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1854&quot;&gt;K번째 최단경로 찾기&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1 ≤ n ≤ 1000, 0 ≤ m ≤ 250000, 1 ≤ k ≤ 100, mk ≤ 3000000, 1 ≤ a,b,c ≤ n, 1 ≤ c ≤ 1000)&lt;/td&gt;
&lt;td&gt;Dijkstra&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;다익스트라 응용 문제인데 굉장히 흔한 유형이여서 거의 20분 컷을 했는데.. 이런 유형이 딱 그런 느낌이에요 알면 바로 풀리고, 모르면 좀 걸리는?&lt;/p&gt;
&lt;p&gt;또 이제 구현 한번 해보면 이런 문제들은 금방 접근도 되고, 거의 바로 풀리는거라서 그렇게 어렵진 않았습니다.&lt;/p&gt;
&lt;p&gt;그래도 꽤 난이도가 높게 나오는 문제여서 조금 설명을 하고 마치겠습니다.&lt;/p&gt;
&lt;p&gt;현재 노드에서 k번 초과로 방문할 필요가 없음! 왜냐하면 여기랑 이어져있는 노드들은 k번 방문할 수 있는 경우.. 이미 이 노드를 지나가거나 다른 경우임.
그래서 이걸로 최적화를 해주고요. 그리고 k번째 방문했다면! result 업데이트 해주고... 이런 순서는 priority_queue로 처리해서 간단히 다익스트라 돌리면 되구요.&lt;/p&gt;
&lt;p&gt;마지막으로 중요한 것은 방문 안 하면 -1을 출력하는데
이때 예외 처리를 조금 조심해줘야 합니다. 왜냐하면 k=1이면, 1번 도시 즉 시작 도시는 0이 찍힙니다. 이때 그냥 print를 N_i = 0이면 -1을 출력하게 했다..
그러면 이제 WA 뜨는겁니다. 뭐 이거는 문제를 읽고서 풀다보면 자연스럽게 해결할 수 있는 간단한 문제였구요.&lt;/p&gt;
&lt;p&gt;재밌었습니다..&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include&amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
using ll = long long;

int inf = 1e9;

int n, m, k;
int graph[1001][1001];
int visited[1001];
ll result[1001];


int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; k;
    
    for(int i = 0; i &amp;lt; m; i++){
        int a, b, c;
        
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; c;
        
        graph[a][b] = c;
    }
    
    for(int i = 1; i &amp;lt;= n; i++) visited[i] = 0;
    
    priority_queue&amp;lt;pair&amp;lt;ll, int&amp;gt;, vector&amp;lt;pair&amp;lt;ll, int&amp;gt;&amp;gt;, greater&amp;lt;pair&amp;lt;ll, int&amp;gt;&amp;gt;&amp;gt; q;
    
    q.push({0, 1});
    
    while(!q.empty()){
        auto [cost, current] = q.top(); q.pop();
        
        if(visited[current] &amp;gt;= k) continue;
        visited[current]++;
        
        if(visited[current] &amp;gt;= k) result[current] = cost;
        
        for(int i = 1; i &amp;lt;= n; i++){
            if(graph[current][i] == 0 || visited[i] &amp;gt;= k) continue;
            
            q.push({graph[current][i] + cost, i});
        }
    }
    
    if(k == 1) cout &amp;lt;&amp;lt; result[1];
    else {
        if(result[1] == 0) cout &amp;lt;&amp;lt; -1;
        else cout &amp;lt;&amp;lt; result[1];
    }
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
    
    
    
    for(int i = 2; i &amp;lt;= n; i++){
        if(result[i] == 0) cout &amp;lt;&amp;lt; -1;
        else cout &amp;lt;&amp;lt; result[i];
        cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 스카이라인 쉬운거</title><link>https://compy07.github.io/Blog/posts/boj/1863/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1863/</guid><description>재활 11일차...</description><pubDate>Sun, 16 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1863&quot;&gt;스카이라인 쉬운거&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 50,000)&lt;/td&gt;
&lt;td&gt;Stack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;음.. 대놓고 그냥 스택 문제여서 잘 풀었던 것 같다.&lt;/p&gt;
&lt;p&gt;이거는 스택으로 올라가는 놈들 처리만 해주면 된다!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = unsigned long long;

int inf = 1e9+7;


int n;
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    
    vector&amp;lt;int&amp;gt; stack;
    
        
    
    cin &amp;gt;&amp;gt; n;
    int s, h;
    int result = 0;
    
    for(int i = 0; i &amp;lt; n; i++){
        cin &amp;gt;&amp;gt; s &amp;gt;&amp;gt; h;
        
        while(!stack.empty() &amp;amp;&amp;amp; stack.back() == 0)stack.pop_back();
        

        
        if(stack.empty()){
            stack.push_back(h);
        }
        else{
            if(stack.back() &amp;gt; h){
                while(!stack.empty() &amp;amp;&amp;amp; stack.back() &amp;gt; h){
                    stack.pop_back();
                    result++;
                }
                if(stack.empty() || stack.back() != h) stack.push_back(h);
            
                
                
                
                
                
            }else{
                stack.push_back(h);
            }
            
        }
    }
    

    
    while(!stack.empty() &amp;amp;&amp;amp; stack.back() == 0)stack.pop_back();

    
    cout &amp;lt;&amp;lt; result + stack.size();

    
    
    return 0;
}


/*
 
 1 2 1 2 1 2 2 1 0 1 0 1

 10
 1 0
 2 2
 3 1
 4 2
 5 1
 6 2
 8 1
 9 0
 10 1
 11 0
 12 1
 
 1
 1 0

 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 가장 큰 정사각형</title><link>https://compy07.github.io/Blog/posts/boj/1915/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1915/</guid><description>재활 22일차...</description><pubDate>Tue, 25 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1915&quot;&gt;가장 큰 정사각형&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ n, m ≤ 1,000)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이것도 전형적인 dp 문제로 점화식이 굉장히 직관적이라 풀기 좋았습니다.. 요즘 어려운 문제 보다가 이런 문제 보니까 그나마 숨이 트이네요.. ㅠㅜ&lt;/p&gt;
&lt;p&gt;현재 위치에서 정사각형이려면 위에서 정사각형이였던 애들을 전부다 검사해서 왼쪽, 위, 좌 대각선에 있는 값이 전부다 정사각형이었다면 그 중에서 작은 친구의 정사각형 값을 1 확장해주면 된다.&lt;/p&gt;
&lt;p&gt;이런 아이디어로 AC를 맞았습니다.
근데 &amp;lt;code&amp;gt;y &amp;lt;= &amp;amp;&amp;amp; x &amp;lt;= 0&amp;lt;/code&amp;gt; 조건을 이따구로 써서 2번 틀렸는데 조심하셔요.. 무의식적으로 작성된 코드는 사람을 고뇌에 빠트립니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
using namespace std;
using ll = long long;

int n, m;
int dp[1001][1001];


int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int current;
    char tmp;
    int result = 0;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    for(int y = 0; y &amp;lt; n; y++){
        
        for(int x = 0; x &amp;lt; m; x++){
            cin &amp;gt;&amp;gt; tmp;
            
            current = tmp - &apos;0&apos;;
            dp[y][x] = current;

            if(current){
                if(x &amp;lt;= 0 || y &amp;lt;= 0) continue;
                
                dp[y][x] = min({dp[y][x-1], dp[y-1][x], dp[y-1][x-1]}) + 1;
            }
        }
    }
    
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m; j++){
            result = max(result, dp[i][j]);
        }
    }
    cout &amp;lt;&amp;lt; result * result;
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 중량제한</title><link>https://compy07.github.io/Blog/posts/boj/1939/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1939/</guid><description>실수를 잡는 것도 실력이긴해.. ㅠ</description><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1939&quot;&gt;중량제한&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(2 ≤ N ≤ 10,000, 1 ≤ A, B ≤ N, 1 ≤ C ≤ 1,000,000,000)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;처음 풀이법이 그냥 맞았는데 메모리 초과가 뜸.. 알고보니까 1000인 줄 알았는데 10000이어서 메모리 초과 나는거를
나는 뭐 또 queue에 많이 들어가는 줄 알았네.. ㅠㅜ&lt;/p&gt;
&lt;p&gt;또 바꾼 것도 어이가 없긴 하지만 좀 이상하게 풀어버리면서 시간이 좀 걸린 케이스
1시간 컷일 줄 알았는데.. 머 그래도 멀티 테스킹하면서 풀어본 문제라 그렇게 늦게 푼것 같지도 않네요.
(사실 bfs 기본 문제라서 오래 걸린게 맞음 ㅠ)&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = long long;
using ld = long double;
ll MOD = 1&apos;000&apos;000&apos;000 + 7;
int inf = 1 &amp;lt;&amp;lt; 30;

using namespace std;
int n, m;

int start, finish;


vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; nodes[10&apos;001]; // 크기가 1000인줄
int visited[10&apos;001];

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    
    for(int i = 1; i &amp;lt;= n; i++){
        visited[i] = 0;
    }
    for(int i = 0; i &amp;lt; m; i++){
        int a, b, c;
        
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; c;
//        cout &amp;lt;&amp;lt; a &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; b &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; c &amp;lt;&amp;lt; &quot;\n&quot;;
        // 일단 중복 무시하고 짜보고 걸리면 set으로
        nodes[a].push_back({b, c});
        nodes[b].push_back({a, c});
    
        
    }
    
    
    
    
    cin &amp;gt;&amp;gt; start &amp;gt;&amp;gt; finish;
    
    priority_queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q; // max heap이다
    
    q.push({1&apos;000&apos;000&apos;000, start});
    
    visited[start] =1&apos;000&apos;000&apos;000;
    while(!q.empty()){
        auto [cost, node] = q.top(); q.pop();
        
        if(node == finish) { // 무조건 작아질 수밖에 없는 구조에서 먼저 도착하면 maxheap이므로 그냥 이게 정답
            cout &amp;lt;&amp;lt; cost;
            return 0;
        }
        for(pair&amp;lt;int, int&amp;gt; next : nodes[node]){
            
            if(next.second &amp;lt;= 0) continue;
            
            int next_cost = next.second;
//            cout &amp;lt;&amp;lt; next_cost &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; cost &amp;lt;&amp;lt; &quot;\n&quot;;
            
            if(min(next_cost, cost) &amp;gt; visited[next.first]){
                
                // 아니 처음 접근법이 갸 ㅇ맞았던건데 왜 bool로 바꿨을까 쉬봉방
                visited[next.first] = min(next_cost, cost);
                q.push({min(next_cost, cost), next.first});
                
            }
        }
        
    }
    
    cout &amp;lt;&amp;lt; 1;
    

    
    
    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 선물 전달</title><link>https://compy07.github.io/Blog/posts/boj/1947/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1947/</guid><description>재활 10일차...</description><pubDate>Sat, 15 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1947&quot;&gt;선물 전달&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 1,000,000)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;음.. 제가 한번 풀고서 갑자기 하나가 구상하던게 떠올라서 또 제출해봤더니 그것도 정답이더라구요?&lt;/p&gt;
&lt;p&gt;음.. 제가 작성한 코드 2개가 같은 의미를 지닌건 아닌거 같은데.. 그냥 나열해서 관찰한 후에 규칙성 찾아서 풀었습니다..&lt;/p&gt;
&lt;p&gt;그냥 열심히 머리 굴리면 되긴하는데.. 뭔가 수학적으로 딱 구해서 해보는 날이 오면 좋겠네요..&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;풀이 영상&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/vJapgxUsEsE?si=wQBaY96Yc-5MO5PX&quot; title=&quot;ps&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;1번&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

#define mod 1000000000

using namespace std;
using ll = long long;



ll dp[1&apos;000&apos;001];

int main() {
    dp[0] = 0;
    dp[1] = 0;
    dp[2] = 1;
    dp[3] = 2;
    dp[4] = 9;
    dp[5] = 44;
    
    int n;
    cin &amp;gt;&amp;gt; n;
    
    for(ll i = 6; i &amp;lt;= n; i++){
        dp[i] = ((i-1)*dp[i-1] + (i-1)*dp[i-2]) % mod; // ㄹㅇ 이거라고?
        
    }
    cout &amp;lt;&amp;lt; dp[n];
    return 0;
}

/*
 a               0
 a b             1
 a b c           2 -&amp;gt; 2*1 + 0
 a b c d         9 -&amp;gt; 3*2(6) + 3 * 1
 a b c d e      44 -&amp;gt; 4*9(36) + 4 * 2(8) = 44 ㄹㅇ 이건가
 a b c d e f   ???
 
 
  
 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2번
(요놈 왜..)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;
ll dp[1000001];

int main() {
    dp[0] = 0;
    dp[1] = 0;
    dp[2] = 1;
    dp[3] = 2;
    dp[4] = 9;
    

    int n;
    cin &amp;gt;&amp;gt; n;
    
    bool check = false;
    for(ll i = 5; i &amp;lt;= n; i++){
        dp[i] = i * dp[i-1];
        if(check) dp[i]++;
        else dp[i]--;
        dp[i]%= 1000000000LL;
        check = !check;
    }
    
    cout &amp;lt;&amp;lt; dp[n] ;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 수식 표현</title><link>https://compy07.github.io/Blog/posts/boj/1955/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/1955/</guid><description>dynamic programming을 이용한 문제 풀이</description><pubDate>Wed, 12 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;https://www.acmicpc.net/problem/1955&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= N &amp;lt;=10,000)&lt;/td&gt;
&lt;td&gt;Math, DP(dynamic programming)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이 문제를 보면 갑자기 겁이 들 수도 있을 것 같다. 하지만 사실 문제 자체가 너무 간단하기 때문에
그렇게 겁먹지 않아도 된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;수식 표현이란 1, +, *, !, (, )로만 이루어진 수식을 말한다. 간명하게 정의하기 위해, 다음과 같이 귀납적으로 정의할 수 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;1은 수식표현이다.&lt;/li&gt;
&lt;li&gt;e가 수식표현이면 (e)와 e!도 수식표현이다.&lt;/li&gt;
&lt;li&gt;e1과 e2가 수식표현이면 e1+e2와 e1*e2도 수식표현이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;예를 들어 18의 수식표현은 (1+1+1)&lt;em&gt;(1+1+1)!, (1+1+1+1)&lt;/em&gt;(1+1+1) +(1+1+1)! 등이 있다. 우리는 n이 주어졌을 때, n의 값을 갖는 수식표현을 구하고 싶다. 단, 1의 개수를 최소로 사용하는 것이어야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;문제를 분석해보자면 숫자 1과 연산자들만 사용해서 입력으로 들어오는 N을 만드는 것을 찾는데,
여기서 중요한건 1을 최대한 적게 사용하는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;생각을 조금만 해본다면 &quot;e1과 e2가 수식표현이면 e1+e2와 e1*e2도 수식표현이다.&quot;라는 것을 본다면
아 작은 것부터 제일 적은 표현법을 찾고, 그를 통해서 큰 수를 확장해 나가면 되겠구나라고 생각할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int dp[100&apos;001] = {MAX, };


int fact(int current){
    if(current == 1) return 1;
    return fact(current-1) * current;
}
int main(){
    int n;
    for(int i = 0; i &amp;lt;= 10000; i++) dp[i] = MAX;
    // 7! 이후 부터는 !쓰지 않음.
    dp[0] = 0;
    dp[1] = 1;
    dp[2] = 2;
    dp[3] = 3;
    dp[4] = 4;
    dp[5] = 5;
    dp[6] = 3;
    dp[7] = 4;
    
    cin &amp;gt;&amp;gt; n;
    
    for(int i = 1; i &amp;lt;= n; i++){
        dp[i] = min({dp[i], dp[i-1]+1});
        for(int j = 2; j &amp;lt;= i; j++){
            if(j*i &amp;lt; 10001) dp[i*j] = min(dp[i*j], dp[i] + dp[j]);
            if(i+j &amp;lt; 10001) dp[i+j] = min(dp[i+j], dp[i] + dp[j]);
        }
        if(i &amp;lt; 8) dp[fact(i)] = dp[i];
    }
    
    cout &amp;lt;&amp;lt; dp[n];
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 코드를 하나하나 분석해 보겠다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0을 표현하는 1은 0개&lt;/li&gt;
&lt;li&gt;1은 1개 / 1&lt;/li&gt;
&lt;li&gt;2는 2개 / 1+1&lt;/li&gt;
&lt;li&gt;3은 3개 / 1+1+1&lt;/li&gt;
&lt;li&gt;4는 4개 / 1+1+1+1&lt;/li&gt;
&lt;li&gt;5는 5개 / 1+1+1+1+1&lt;/li&gt;
&lt;li&gt;6은 3개 / (1+1+1)!&lt;/li&gt;
&lt;li&gt;7은 4개 / 6+1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이것을 미리 해놓은 이유는
좀 편하게 팩토리얼 값을 넣어놓고 시작하기 위해서 넣어놓았습니다.&lt;/p&gt;
&lt;p&gt;이제는 for문이 도는 원리를 설명하자면,&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;현재 내 자신과 전에 있던 조합에서 1을 더한 것 중에 1이 더 적게 쓰인 것을 선택합니다.&lt;/p&gt;
&lt;p&gt;또한 자기 자신에서 자기보다 작은 값과의 곱과 합을 통해서 다음 큰 수들의 최솟값을 다시 업데이트함으로써&lt;/p&gt;
&lt;p&gt;20을 4*5 의 최소 1의 개수를 얻을 수 있도록 설계하였습니다.&lt;/p&gt;
&lt;p&gt;fact은 7로 제한하였는데 이유는 8!이 n의 최댓값을 넘어가기 때문에 조건을 걸어주었습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;정답보기&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- summary 아래 한칸 공백 두어야함 --&amp;gt;&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#define MAX 2&apos;100&apos;000&apos;000
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
#include &amp;lt;math.h&amp;gt;
#include &amp;lt;random&amp;gt;

#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;


using ll = long long;
using ull = unsigned long long;
using namespace std;
// int dy[] = {-1, -1, 0, 1, 1, 1, 0, -1}, dx[] = {0, -1, -1, -1, 0, 1, 1, 1}; // up, lup, left, ldown, down, rdown, right, rup

int dp[100&apos;001] = {MAX, };


int fact(int current){
    if(current == 1) return 1;
    return fact(current-1) * current;
}
int main(){
    int n;
    for(int i = 0; i &amp;lt;= 10000; i++) dp[i] = MAX;
    // 7! 이후 부터는 !쓰지 않음.
    dp[0] = 0;
    dp[1] = 1;
    dp[2] = 2;
    dp[3] = 3;
    dp[4] = 4;
    dp[5] = 5;
    dp[6] = 3;
    dp[7] = 4;
    
    cin &amp;gt;&amp;gt; n;
    
    for(int i = 1; i &amp;lt;= n; i++){
        dp[i] = min({dp[i], dp[i-1]+1});
        for(int j = 2; j &amp;lt;= i; j++){
            if(j*i &amp;lt; 10001) dp[i*j] = min(dp[i*j], dp[i] + dp[j]);
            if(i+j &amp;lt; 10001) dp[i+j] = min(dp[i+j], dp[i] + dp[j]);
        }
        if(i &amp;lt; 8) dp[fact(i)] = dp[i];
    }
    
    cout &amp;lt;&amp;lt; dp[n];
    
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 다이어트</title><link>https://compy07.github.io/Blog/posts/boj/19942/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/19942/</guid><description>재활 25일차</description><pubDate>Fri, 28 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/19942&quot;&gt;다이어트&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(3 ≤ N ≤ 15&amp;lt;br/&amp;gt; 0 ≤ mp, mf, ms, mv ≤ 500&amp;lt;br/&amp;gt;mp + mf + ms + mv &amp;gt; 0&amp;lt;br/&amp;gt; 0 ≤ p, f, s, v, c ≤ 500)&lt;/td&gt;
&lt;td&gt;Backtracking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;오늘은 조금 문제 푸는 과정을 보여드리고 싶어서 찍어봤습닏 ㅏ.. 평소에 골랜디 즐겨하는데요 그거 좀 보여드립니다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;풀이 영상
&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/xQNaFrBomkU?si=D1ZfkD-sQGT_HfCo&quot; title=&quot;ps&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;지금 영상을 보면 1분 정도쯤에 바로 브포의 가능성을 보고서 바로 작성하는 것을 볼 수 있습니다. 또 구조도 한 문제 해석 단계에서 대부분 끝내는 편이구요&lt;/p&gt;
&lt;p&gt;어려운 문제의 경우 문제를 해석하고 풀이 과정을 머리로 그리는 시간이 오래걸립니다. 사실상 코드 작성 시간은 어떤 문제든 간에 1시간 이내로 끝내는 것이 대부분인데.. 짜잘한 문법 실수나 다른 +, - 이런거 실수하는 거 때문에 많이 시간을 먹을 때도 있습니다.&lt;/p&gt;
&lt;p&gt;이번 문제는 약간 사전순 정렬을 뒤늦게 발견해서 백트래킹의 순서를 바꿔줬습니다. 또 price를 모르고 그냥 조건 되는대로 가다가 이상하다 싶어서 보니까 네.. 문제도 사실 잘못된 정보로 작성하다가 바꾸게 되었죠?&lt;/p&gt;
&lt;p&gt;그래도 백트래킹이 가능하다는 것(최대 경우의 수가 많지 않음을 파악함.), 사전순 정렬(백트래킹 순서) 이거를 한번에 파악했다는 것부터 좋은 접근이었다고 생각합니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;


struct food{
    int p, f, s, v, price;
    bool visited;
    
    int sum(){
        return p+f+s+v;
    }
};



int n;
food foods[16];
food current, target;
food result;

vector&amp;lt;int&amp;gt; result_members;

bool complete(){
    return current.f &amp;gt;= target.f &amp;amp;&amp;amp; current.p &amp;gt;= target.p &amp;amp;&amp;amp; current.s &amp;gt;= target.s &amp;amp;&amp;amp; current.v &amp;gt;= target.v;
}

void getResult(){
    if(current.price &amp;lt; result.price) {
        result.p = current.p;
        result.v = current.v;
        result.f = current.f;
        result.s = current.s;
        result.price = current.price;
        result_members.clear();
        for(int i = 0; i &amp;lt; n; i++) {
            if(foods[i].visited) result_members.push_back(i+1);
        }
    }
}


void solution(int current_idx){
    if(complete()) {
        getResult();
        return;
    }
    if(current_idx &amp;gt;= n) return;
    
    current.p += foods[current_idx].p;
    current.v += foods[current_idx].v;
    current.f += foods[current_idx].f;
    current.s += foods[current_idx].s;
    foods[current_idx].visited = true;
    current.price += foods[current_idx].price;
    solution(current_idx+1);
    
    current.p -= foods[current_idx].p;
    current.v -= foods[current_idx].v;
    current.f -= foods[current_idx].f;
    current.s -= foods[current_idx].s;
    foods[current_idx].visited = false;
    current.price -= foods[current_idx].price;
    solution(current_idx+1);
    
    
    
}

int main() {
    ios_base::sync_with_stdio(0); cin.tie(0);

    current = {0, 0, 0, 0, 0};
    cin &amp;gt;&amp;gt; n;
    cin &amp;gt;&amp;gt; target.p &amp;gt;&amp;gt; target.f &amp;gt;&amp;gt; target.s &amp;gt;&amp;gt; target.v;
    
    result = {1000000000, 1000000000,1000000000,1000000000,1000000000, 0};
    
    for(int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; foods[i].p &amp;gt;&amp;gt; foods[i].f &amp;gt;&amp;gt; foods[i].s &amp;gt;&amp;gt; foods[i].v &amp;gt;&amp;gt; foods[i].price;
        foods[i].visited = false;
    }
    
    solution(0);
    
    if(result_members.empty()) cout &amp;lt;&amp;lt; -1;
    else{
        cout &amp;lt;&amp;lt; result.price &amp;lt;&amp;lt; &quot;\n&quot;;
        for(int i : result_members) cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; &quot;;
    }
    
    
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 보석 줍기</title><link>https://compy07.github.io/Blog/posts/boj/2001/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2001/</guid><description>오랜만에 풀었더니 그냥 변수도 틀리네</description><pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2001&quot;&gt;보석 줍기&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ n ≤ 100, &amp;lt;br/&amp;gt;1 ≤ m ≤ 1000&amp;lt;br/&amp;gt;1 ≤ K ≤ 14&amp;lt;br/&amp;gt;1 &amp;lt;= c &amp;lt;= 100)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;솔직히 문제 자체는 쉬웠는데, bit count 세는 로직에서 k를 n으로 적어놓고 오류를 못 찾아서 많이 오래걸린
케이스이다.&lt;/p&gt;
&lt;p&gt;최적화 개념에서 bit masking을 활용한다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = long long;
using ld = long double;
ll MOD = 1&apos;000&apos;000&apos;000 + 7;

using namespace std;

int n, m, k;

int strength[101][101];
// 걍 viist처리를 몇 번 움직였는가? + 몇 번째 보석을 가졌는가? 에 대한 visit처리만 하면 될드 ㅅ? 근데 최대가 14개인데 이걸르 어케 처리하냐!!! 바로 bit masking이지 아무래도 ㅇㅇ

bool visited[101][20001];


vector&amp;lt;int&amp;gt; gemIsland;

int countOne(int v){
    int result = 0;
    
    int flag = 1;
    while(flag &amp;lt; 1 &amp;lt;&amp;lt; k){ // 아니 쉬발 이거였다고? 와 이걸 왜 이따구로 썻냐
        
     
        result += (flag &amp;amp; v) ? 1 : 0;
        flag &amp;lt;&amp;lt;= 1;
    }
    
//    cout &amp;lt;&amp;lt; &quot;result: &quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
    return result;
    
}


int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
  
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; k;
    bool start_gem = 0;
    for(int i = 0; i &amp;lt; k; i++) {
        int tmp;
        cin &amp;gt;&amp;gt; tmp;
        
        gemIsland.push_back(tmp-1);
        if(tmp == 1)start_gem = true;
    }
    
    
    
    for(int i = 0; i &amp;lt; m; i++){
        int a, b, c;
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; c;
        
        
        strength[a-1][b-1] = c;
        strength[b-1][a-1] = c;
    }
    
    for(int i = 0;  i &amp;lt; 101; i++){
        for(int j = 0; j &amp;lt; 20001; j++){
            visited[i][j] = false;
        }
    }
    
    
    queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q;
    
    q.push({0, 0});
    int result = start_gem;
    
    while(!q.empty()){
        auto [node, current_visited] = q.front(); q.pop();
        
        int currentCount = countOne(current_visited); // 허허 ㅅ발
        
        
        if(node == 0){
            result = max(result, currentCount);
//            int flag = 1;
//            while(flag &amp;lt; 1 &amp;lt;&amp;lt; n){
//             
//                if(flag &amp;amp; current_visited) cout &amp;lt;&amp;lt; 1;
//                else cout &amp;lt;&amp;lt; 0;
//                flag &amp;lt;&amp;lt;= 1;
//            }
//            cout &amp;lt;&amp;lt; &quot; : &quot; &amp;lt;&amp;lt; countOne(current_visited) &amp;lt;&amp;lt;&quot; \n\n&quot;;
            
        }
        
        
        int origin_current_visited = current_visited;
        
        
        for(int i = 0; i &amp;lt; n; i++){
            if(!strength[node][i]) continue; // 0이면 안 들어갈곤데
            if(strength[node][i] &amp;lt; currentCount) continue;
            current_visited = origin_current_visited;
            
            
            if(!visited[i][current_visited]){
                visited[i][current_visited] = true;
                q.push({i, current_visited});
            }
            
            
            for(int j = 0; j &amp;lt; k; j++){
                if(gemIsland[j] == i){
                    current_visited |= 1 &amp;lt;&amp;lt; j;
                    break;
                }
            }
            
            
            if(!visited[i][current_visited]){
                
                visited[i][current_visited] = true;
                q.push({i, current_visited});
            }
        }
    }

    
    cout &amp;lt;&amp;lt; result;
    return 0;
}

/*
 1 0 1
 1
 
 
 2 1 2
 1
 2
 2 1 3
 
 6 5 6
 1
 2
 3
 4
 5
 6
 1 2 1
 1 3 2
 1 4 3
 1 5 4
 1 6 5
 
 
 6 5 6
 1
 2
 3
 4
 5
 6
 1 2 1
 1 3 1
 1 4 1
 1 5 4
 1 6 5
 */


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 소풍</title><link>https://compy07.github.io/Blog/posts/boj/2026/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2026/</guid><description>간단한 backtracking</description><pubDate>Thu, 21 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;https://www.acmicpc.net/problem/2026&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 900&amp;lt;br/&amp;gt;1 ≤ K ≤ 62&amp;lt;br/&amp;gt;1 ≤ F ≤ 5,600)&lt;/td&gt;
&lt;td&gt;BackTracking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;간단히 backtracking 구현하면 끝나는 문제입니다.&lt;/p&gt;
&lt;p&gt;처음에 시작하는 index 번호에 대해서 교집합 처리로 하려고 했는데..
그냥 구현으로는 시간초과를 피하기가 어려워서 그냥 직관적으로 보이는 풀이로 풀었습니다.&lt;/p&gt;
&lt;p&gt;-1이 있는지도 모르고 계속 틀리다가.. 허헣
조심하십쇼&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;

using ll = long long;

int n, k, f;

bool connect[901][901];
int sizes[901];

bool visited[901];
bool connectVisited[901];

bool isPossible(int target){
    for(int i = 1; i &amp;lt;= n; i++){
        if(!visited[i]) continue;
        
        if(!connect[target][i]) return false;
    }
    return true;
}

bool solution(int current, int depth){
    
    if(depth == k) return true;

    for(int i = 1; i &amp;lt;= n; i++){
        if(!connect[current][i] || visited[i] || !isPossible(i)) continue;
        
        visited[i] = true;
        bool result = solution(i, depth+1);
        if(result) return true;
        visited[i] = false;
    }

    return false;
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; k &amp;gt;&amp;gt; n &amp;gt;&amp;gt; f;
    
    for(int i = 0; i &amp;lt; f; i++){
        int a, b;
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;
        connect[a][b] = true;
        connect[b][a] = true;
        
        sizes[a]++;
        sizes[b]++;
    }
    
    
    
    for(int i = 1; i &amp;lt;= n; i++){
        if(sizes[i] + 1 &amp;lt; k) continue;
        
        bool result = solution(i, 0);
//        cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
        if(result){
            int count = 0;
            for(int i = 1; i &amp;lt;= n; i++){
                if(visited[i]){
                    count ++;
                    cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot;\n&quot;;
                }
                
                if(count == k) return 0;
            }
        }
        
    }
    cout &amp;lt;&amp;lt; -1;
   
        
    return 0;
}

/*
 
 1 6 8
 1 2
 1 3
 1 6
 2 3
 2 6
 3 6
 4 5
 5 6
 */


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 구간 합 구하기</title><link>https://compy07.github.io/Blog/posts/boj/2042/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2042/</guid><description>움.. 얘 왜 골드 1임?</description><pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2042&quot;&gt;구간 합 구하기&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(2 ≤ N ≤ 10,000, 1 ≤ A, B ≤ N, 1 ≤ C ≤ 1,000,000,000)&lt;/td&gt;
&lt;td&gt;PrefixSum&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;아니 근데 문제가 세그 트리라는데 제가 세그 트리를 안 써봐가지고 그냥 풀긴 했거든여.. 근데 다들 세그 트리로 풀었다네요&lt;/p&gt;
&lt;p&gt;저는 이게 당연히 정해인 줄 알앗는데 ㅠ&lt;/p&gt;
&lt;p&gt;그래도 거의 20분 컷 해서 기분은 좋습니다.&lt;/p&gt;
&lt;p&gt;포인트는 바뀌는 것을 어떻게 보정해서 더한 값이 같게 만들어질 것인지를 고민하면 풀립니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MAX 200000001
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
#include &amp;lt;math.h&amp;gt;
using ll = long long;
using namespace std;

ll preSum[1&apos;000&apos;001];
ll nums[1&apos;000&apos;001];

vector&amp;lt;pair&amp;lt;ll, ll&amp;gt;&amp;gt; changes;
ll fixed_[1&apos;000&apos;001];
ll n, m, k;

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; k;
    
    for(int i = 0; i &amp;lt; n; i++){
        cin &amp;gt;&amp;gt; nums[i];
        fixed_[i] = nums[i];
    }
    preSum[0] = nums[0];
    
    for(int i = 1; i &amp;lt; n; i++) preSum[i] += preSum[i-1] + nums[i];
    
    
    
    for(int i = 0; i &amp;lt; m + k; i++){
        ll a, b, c;
        
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; c;
        
        // change
        if(a == 1){
            changes.push_back({b, c - fixed_[b-1]});
            fixed_[b-1] = c;
//            cout &amp;lt;&amp;lt; c - numbers[b-1] &amp;lt;&amp;lt;&quot;\n\n&quot;;
        }
        // print
        else if(a == 2){
            ll another = 0;
            
            for(pair&amp;lt;ll, ll&amp;gt; p : changes){
                if(!(b &amp;lt;= p.first &amp;amp;&amp;amp; p.first &amp;lt;= c)) continue;
                another += p.second;
            }
            
            cout &amp;lt;&amp;lt; preSum[c-1] - preSum[b-1] + another + nums[b - 1] &amp;lt;&amp;lt; &quot;\n&quot;;
        }
        
    }
    return 0;
    
    
    
    

}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 피보나치 수의 합</title><link>https://compy07.github.io/Blog/posts/boj/2086/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2086/</guid><description>재활 12일차</description><pubDate>Mon, 06 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2086&quot;&gt;피보나치 수의 합&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;1 ≤ a ≤ b ≤ 9,000,000,000,000,000,000&lt;/td&gt;
&lt;td&gt;Math, Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;저번에 풀었던 문제의 약간 쉬운 버전이네요..&lt;/p&gt;
&lt;p&gt;약간 실수할 수 있는건 음수에 관한건데 조금 생각하면 금방 ac 할 수 있어요!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./1.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = unsigned long long;
using ld = long double;
//ll MOD = 1&apos;000&apos;000&apos;007;
ll MOD = 1&apos;000&apos;000&apos;000;
//ll MOD = 1.8446744074E19;

map&amp;lt;ll, ll&amp;gt; dp;



ll solution(ll n){
    if(dp.find(n) != dp.end()) return dp[n];
    ll result;
    
    ll a, b;
    a = solution(n/2);
    b = solution(n/2+1+(-2 *!(n%2)));
    
    if(n%2) result = ((a * a)%MOD + (b * b)%MOD)%MOD;
    else result = ((((2*b)%MOD + a)%MOD)*a)%MOD;
    dp[n] = result;
    
    return result;
}




int main() {
    ll A, B;
    cin &amp;gt;&amp;gt; A &amp;gt;&amp;gt; B;
    
    dp[0] = 0;
    dp[1] = 1;
    dp[2] = 1;
    dp[3] = 2;
    
    
    ll sumA = solution(A+1);
    ll sumB = solution(B+2);
    
    
    // 이게 F_n*F_(n+1)이여서
    
    // 아 생각해보니까 dp자체에서 mod를 쓰니까 더 작을 수 있구나..
    if(sumB &amp;lt; sumA) cout &amp;lt;&amp;lt; (sumB - sumA + MOD) % MOD;
    else cout &amp;lt;&amp;lt; (((sumB - sumA))%MOD);
    

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 두 배열의 합</title><link>https://compy07.github.io/Blog/posts/boj/2143/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2143/</guid><description>재활 17일차 아 코드를 엎으니까 되잖아ㅏ...ㅠㅜ</description><pubDate>Wed, 19 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2143&quot;&gt;두 배열의 합&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;64MB&lt;/td&gt;
&lt;td&gt;-1,000,000,000 ≤ T ≤ 1,000,000,000&amp;lt;br/&amp;gt;1 ≤ n ≤ 1,000&amp;lt;br/&amp;gt;1 ≤ m ≤ 1,000&lt;/td&gt;
&lt;td&gt;Sort, Binary Search, 누적합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;진짜 정말 너무 아쉬웠다... 처음 논리에서 별로 벗어나진 않았는데 이상한 코드를 짠 것을 그대로 쓰다가 아예 갈엎으니 바로 ac를 맞았다.
진짜 미치겠다. 어쩌다가...&lt;/p&gt;
&lt;p&gt;하튼 일단 문제는 누적합으로 조합을 쫘라락 중복되지 않도록 구하고, 이후에 정렬하고서 이분탐색으로 돌면서 그 슬라이싱된 값들을 빠르게 찾는 전략으로 진행했습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

ll t;
ll A[1&apos;000&apos;000];
ll B[1&apos;000&apos;000];

ll input(ll *current) {
    int n;
    cin &amp;gt;&amp;gt; n;
    vector&amp;lt;ll&amp;gt; arr(n);
    for (int i = 0; i &amp;lt; n; ++i) cin &amp;gt;&amp;gt; arr[i];
    int idx = 0;
    for (int i = 0; i &amp;lt; n; ++i) {
        ll sum = 0;
        for (int j = i; j &amp;lt; n; ++j) {
            sum += arr[j];
            current[idx++] = sum;
        }
    }
    return idx;
}

ll solution(ll n, ll m) {
    ll result = 0;
    sort(A, A + n);
    sort(B, B + m);

    for (int i = 0; i &amp;lt; n; ++i) {
        ll target = t - A[i];
        ll left = 0, right = m - 1;
        ll rl = -1, rr = -1;

        while (left &amp;lt;= right) {
            ll pivot = (right + left) / 2;
            if (B[pivot] &amp;gt;= target) {
                right = pivot - 1;
                if (B[pivot] == target) rl = pivot;
            } else  left = pivot + 1;
        }

        left = 0;
        right = m - 1;
        while (left &amp;lt;= right) {
            ll pivot = (right + left) / 2;
            if (B[pivot] &amp;lt;= target) {
                left = pivot + 1;
                if (B[pivot] == target) rr = pivot;
            } else right = pivot - 1;
        }

        if (rl != -1 &amp;amp;&amp;amp; rr != -1) result += (rr - rl + 1);
    }

    return result;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cin &amp;gt;&amp;gt; t;
    ll n = input(A);
    ll m = input(B);

    cout &amp;lt;&amp;lt; solution(n, m);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 다리 만들기</title><link>https://compy07.github.io/Blog/posts/boj/2146/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2146/</guid><description>재활 20일차 break를 까먹은 남자는 메모리 초과에 시달린다...</description><pubDate>Sun, 23 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2146&quot;&gt;다리 만들기&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;192MB&lt;/td&gt;
&lt;td&gt;1 ≤ N ≤ 100&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;전형적인 BFS 문제로 그냥 열심히 돌리면 정답이 나오는 문제입니다.&lt;/p&gt;
&lt;p&gt;조금 신경써야 할 것은 visit 처리를 기본적으로 해주면 쉽게 풀립니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;

using ll = long long;

int n;
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
int origin[101][101];
int board[101][101];

int solution() {
   int current = 1;
   for(int i = 0; i &amp;lt; n; i++) {
       for(int j = 0; j &amp;lt; n; j++) {
           if(origin[i][j] != 1) continue;
           
           queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q;
           q.push({i, j});
           current++;
           origin[i][j] = current;
           
           while(!q.empty()) {
               auto [y, x] = q.front(); q.pop();
               for(int k = 0; k &amp;lt; 4; k++) {
                   int nx = x + dx[k];
                   int ny = y + dy[k];
                   if(0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; origin[ny][nx] == 1) {
                       origin[ny][nx] = current;
                       q.push({ny, nx});
                   }
               }
           }
       }
   }

   int result = 1e9;
   for(int start = 2; start &amp;lt;= current; start++) {
       queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q;
       for(int i = 0; i &amp;lt; n; i++) {
           for(int j = 0; j &amp;lt; n; j++) {
               board[i][j] = -1;
               if(origin[i][j] == start) {
                   q.push({i, j});
                   board[i][j] = 0;
               }
           }
       }
       
       while(!q.empty()) {
           auto [y, x] = q.front(); q.pop();
           
           for(int k = 0; k &amp;lt; 4; k++) {
               int nx = x + dx[k];
               int ny = y + dy[k];
               if(0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n) {
                   if(origin[ny][nx] == 0 &amp;amp;&amp;amp; board[ny][nx] == -1) {
                       board[ny][nx] = board[y][x] + 1;
                       q.push({ny, nx});
                   }
                   else if(origin[ny][nx] != 0 &amp;amp;&amp;amp; origin[ny][nx] != start) {
                       result = min(result, board[y][x]);
                       break;
                   }
               }
           }
       }
   }
   
   return result == 1e9 ? -1 : result;
}

int main() {
   ios_base::sync_with_stdio(false);
   cin.tie(nullptr);
   
   cin &amp;gt;&amp;gt; n;
   for(int i = 0; i &amp;lt; n*n; i++)  cin &amp;gt;&amp;gt; origin[i/n][i%n];
   
   cout &amp;lt;&amp;lt; solution();
   return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 구간 나누기</title><link>https://compy07.github.io/Blog/posts/boj/2228/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2228/</guid><description>dpdpdpdp 너무 좋아좋아</description><pubDate>Thu, 10 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2228&quot;&gt;구간 나누기&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 &amp;lt;= N &amp;lt;= 100, 1&amp;lt;=M&amp;lt;=[N/2]&amp;lt;br/&amp;gt;-32768 &amp;lt;= num &amp;lt;= 32767  )&lt;/td&gt;
&lt;td&gt;DynamicProgramming(DP)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;N(1 ≤ N ≤ 100)개의 수로 이루어진 1차원 배열이 있다. 이 배열에서 M(1 ≤ M ≤ ⌈(N/2)⌉)개의 구간을 선택해서, 구간에 속한 수들의 총 합이 최대가 되도록 하려 한다. 단, 다음의 조건들이 만족되어야 한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;각 구간은 한 개 이상의 연속된 수들로 이루어진다.&lt;/li&gt;
&lt;li&gt;서로 다른 두 구간끼리 겹쳐있거나 인접해 있어서는 안 된다.&lt;/li&gt;
&lt;li&gt;정확히 M개의 구간이 있어야 한다. M개 미만이어서는 안 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;N개의 수들이 주어졌을 때, 답을 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;문제를 처음 보고서 바로 dp로 접근한 나에게 칭찬 드립니다!&lt;/p&gt;
&lt;p&gt;문제를 보자마자 dp에 대한 정의를 했습니다. 이후 이 dp 식에 맞도록 bottom-up으로 문제를 풀이하였습니다.&lt;/p&gt;
&lt;p&gt;저의 아이디어는
dp[i][j]는 현재 수열에서 i번째를 보고 있을 때, j번째의 구간이다.
그렇기에 i는 N보다 작으며, j는 m보다 작다.&lt;/p&gt;
&lt;p&gt;이렇게 다 구한 후 m개의 구간으로 나눠진 조합 중 제일 큰 것들을 업데이트하고 최종적으로 제일 큰 값을 고르면 최적해를 찾을 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

using namespace std;

const int pre = -2100000000;

int n, m;
int nums[101];
int dp[101][101];
int main(){
    // dp[i][j][k]는 i번째 일때, j개의 연속된 값에서 k번째의 구간이다. 라는 의미이다.
    // 다시 정의한다. dp[i][j]는 i 번째 일때, j번째의 구간이다.
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    for(int i = 0; i &amp;lt; n; i ++) cin &amp;gt;&amp;gt; nums[i];
    
    if(m == 1) {
        int mymax = pre;
        for(int i = 0; i &amp;lt; n; i++) mymax = max(mymax, nums[i]);
        cout&amp;lt;&amp;lt; mymax;
        return 0;
    }
    
    for(int i = 0; i &amp;lt; n+1; i++)
        for(int j = 0; j &amp;lt; n+1; j++) dp[i][j] = pre;
    
    dp[0][0] = nums[0];
    for(int i = 1; i &amp;lt; n; i ++){
        dp[i][0] = max(dp[i-1][0]+nums[i], nums[i]);
        for(int j = 1; j &amp;lt; m+1; j++) dp[i][j] = max(dp[i][j], dp[i-1][j] + nums[i]);
        for(int j = 0; j &amp;lt; i-1; j++){
            for(int k = 1; k &amp;lt; m+1; k++) dp[i][k]=max(dp[i][k], dp[j][k-1]+nums[i]);
        }
    }
    
    int result = pre;
    for(int i = 0; i &amp;lt; n+1; i++) result = max(dp[i][m-1], result);
    
    
    cout &amp;lt;&amp;lt; result;
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 동전 나누기 1</title><link>https://compy07.github.io/Blog/posts/boj/2239/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2239/</guid><description>재활 14일차</description><pubDate>Sun, 16 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2239&quot;&gt;동전 나누기 1&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.5s&lt;/td&gt;
&lt;td&gt;4MB&lt;/td&gt;
&lt;td&gt;1 ≤ n ≤ 100&amp;lt;br/&amp;gt;1 ≤ k ≤ 10,000&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;기본적인 dp 문제이고, 연습하기 좋은 문제였던 것 같습니다.
재미있던 문제였습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;코드와 같이 포문을 돌리면 중복 체크를 할 필요가 없어유 순서 섞이는 경우가 발생을 안 하기 때문에 이렇게 간단히 solve할 수 있습니다.&lt;/p&gt;
&lt;p&gt;처음에 메모리 제한 4mb 보고서 뭐지? 이거 어떻게 품? 이러다가 범위 보고서 딱 풀었네요&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
using ll = long long;

int n, k;
int num[101];
int dp[10001];

int main() {
    ios_base::sync_with_stdio(false); cin.tie(nullptr);
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; num[i];
    
    dp[0] = 1;
    
    for(int i = 0; i &amp;lt; n; i++) {
        for(int j = num[i]; j &amp;lt;= k; j++) dp[j] += dp[j - num[i]];
    }
    
    cout &amp;lt;&amp;lt; dp[k];
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] ReverseSort</title><link>https://compy07.github.io/Blog/posts/boj/22480/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/22480/</guid><description>재활 3일차</description><pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/22480&quot;&gt;ReverseSort&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 10)&lt;/td&gt;
&lt;td&gt;BFS, Meet In The Middle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;문제가 조금 많이 당황스러웠습니다. 처음에 N이 10 이하인데 이렇게 시간을 많이 준다고? 뭐지? 하면서 생각을 해보니
생각보다 전체 순회는 안되고, 따로 최적해를 구하는 로직이 필요한데
쪼갠다는 생각을 하고서 어떻게 구현해야되는 것인지를 생각하는게 오래걸렸네요.&lt;/p&gt;
&lt;p&gt;MITM을 풀어본적이 ctf 때나 있었던 거 같은데 ps에서 보니 반갑기도 하면서 아이디어 떠오르는게 왜 오래걸린지
잘 이해가 되지 않았던 문제였습니다.&lt;/p&gt;
&lt;p&gt;구현은 단순히 입력으로 들어온 상태와 완성된 상태에서 이어질 수 있는 경우들을 reverse 시키면서 그 상태를 저장시키고(path로 저장),
만약 그 key에 도달하면 forward list와 backward list의 key 겹치는 개수를 더해서 정답을 출력하도록 만들었습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
using namespace std;
int N;
struct Node {
    vector&amp;lt;int&amp;gt; arr;
    vector&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; ops;
};

string encode(const vector&amp;lt;int&amp;gt;&amp;amp; arr) {
    string s;
    for (int x : arr) s.push_back(char(x));
    return s;
}

template &amp;lt;typename T&amp;gt;
void listReverse(vector&amp;lt;T&amp;gt;&amp;amp; list, int left, int right){
    while(left &amp;lt; right){
        T tmp = list[left];
        list[left] = list[right];
        list[right] = tmp;
           
        left++;
        right --;
    }
}

string solution(queue&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; q,
                unordered_map&amp;lt;string, vector&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt;&amp;gt;&amp;amp; vis,
                unordered_map&amp;lt;string, vector&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt;&amp;gt;&amp;amp; other,
                bool forward){
  int sz = q.size();
  while (sz--) {
      auto cur = q.front(); q.pop();
      string ck = encode(cur);
      auto ops = vis[ck];

      int n = cur.size();
      for (int i = 0; i &amp;lt; n; i++) {
          for (int j = i+1; j &amp;lt; n; j++) {
              auto nxt = cur;
              listReverse(nxt, i, j);

              string nk = encode(nxt);
              if (!vis.count(nk)) {
                  auto newOps = ops;
                  newOps.push_back({i, j});
                  vis[nk] = newOps;
                  q.push(nxt);

                  if (other.count(nk)) {
                      return nk;
                  }
              }
          }
      }
  }
  return &quot;&quot;;
};


int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    
    cin &amp;gt;&amp;gt; N;
    vector&amp;lt;int&amp;gt; start(N);
    vector&amp;lt;int&amp;gt; goal(N);
    for (int i = 0; i &amp;lt; N; i++){
        cin &amp;gt;&amp;gt; start[i];
        goal[i] = i + 1;
    }

    if (start == goal) {
        cout &amp;lt;&amp;lt; 0 &amp;lt;&amp;lt; &quot;\n&quot;;
        return 0;
    }

    unordered_map&amp;lt;string, vector&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt;&amp;gt; fwd, bwd;
    queue&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; qf, qb;

    fwd[encode(start)] = {};
    bwd[encode(goal)] = {};
    qf.push(start);
    qb.push(goal);

    string meetKey;

    while (!qf.empty() &amp;amp;&amp;amp; !qb.empty()) {
        // 앞쪽
        meetKey = solution(qf, fwd, bwd, true);
        if (!meetKey.empty()) break;

        // 뒤쪽
        meetKey = solution(qb, bwd, fwd, false);
        if (!meetKey.empty()) break;
    }

    
    auto ops1 = fwd[meetKey];
    auto ops2 = bwd[meetKey];
    

    cout &amp;lt;&amp;lt; ops1.size() + ops2.size() &amp;lt;&amp;lt; &quot;\n&quot;;

}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 물통</title><link>https://compy07.github.io/Blog/posts/boj/2251/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2251/</guid><description>그냥 구현이더라구요</description><pubDate>Fri, 04 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2251&quot;&gt;물통&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 &amp;lt;= A, B, C &amp;lt;= 200)&lt;/td&gt;
&lt;td&gt;Implementation(구현), BFS(너비우선탐색)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;각각 부피가 A, B, C(1≤A, B, C≤200) 리터인 세 개의 물통이 있다. 처음에는 앞의 두 물통은 비어 있고, 세 번째 물통은 가득(C 리터) 차 있다. 이제 어떤 물통에 들어있는 물을 다른 물통으로 쏟아 부을 수 있는데, 이때에는 한 물통이 비거나, 다른 한 물통이 가득 찰 때까지 물을 부을 수 있다. 이 과정에서 손실되는 물은 없다고 가정한다.&lt;/p&gt;
&lt;p&gt;이와 같은 과정을 거치다보면 세 번째 물통(용량이 C인)에 담겨있는 물의 양이 변할 수도 있다. 첫 번째 물통(용량이 A인)이 비어 있을 때, 세 번째 물통(용량이 C인)에 담겨있을 수 있는 물의 양을 모두 구해내는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 문제는 시간도 널널하고, 뭐 딱히 어려운 로직도 아닌 거 같아서 조건 달아서 구현했더니 풀리는 문제였습니다.&lt;/p&gt;
&lt;p&gt;A가 0일때, C의 부피를 알아내는 과정이 그냥 조금 중복계산 줄이고, 탐색하면 될 것 같아서 BFS로 구현하여 풀이하였습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;

bool visited[201][201][201];
bool result[201];

typedef struct bottle {
    int a, b, c;
} bottle;

int main() {
    int a, b, c;
    cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; c;
    
    queue&amp;lt;bottle&amp;gt; q;
    
    q.push({0, 0, c});
    int pour;
    while(!q.empty()) {
        bottle bot = q.front(); q.pop();
        if(visited[bot.a][bot.b][bot.c]) continue;
        visited[bot.a][bot.b][bot.c] = true;
        
        if(bot.a == 0) {
            result[bot.c] = true;
        }
        
        // a -&amp;gt; b
        pour = min(bot.a, b - bot.b);
        q.push({bot.a - pour, bot.b + pour, bot.c});
        
        // a -&amp;gt; c
        pour = min(bot.a, c - bot.c);
        q.push({bot.a - pour, bot.b, bot.c + pour});
        
        // b -&amp;gt; a
        pour = min(bot.b, a - bot.a);
        q.push({bot.a + pour, bot.b - pour, bot.c});
        
        // b -&amp;gt; c
        pour = min(bot.b, c - bot.c);
        q.push({bot.a, bot.b - pour, bot.c + pour});
        
        // c -&amp;gt; a
        pour = min(bot.c, a - bot.a);
        q.push({bot.a + pour, bot.b, bot.c - pour});
        
        // c -&amp;gt; b
        pour = min(bot.c, b - bot.b);
        q.push({bot.a, bot.b + pour, bot.c - pour});
    }
    
    
    
    for(int i = 0; i &amp;lt; 201; i++){
        if(result[i]) cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; &quot;;
    }
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 3초 정렬</title><link>https://compy07.github.io/Blog/posts/boj/22963/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/22963/</guid><description>재활 9일차</description><pubDate>Tue, 11 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/22963&quot;&gt;3초 정렬&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(3 ≤ N ≤ 200000&amp;lt;br/&amp;gt; 1 ≤ A_i ≤ 1e9)&lt;/td&gt;
&lt;td&gt;DynamicProgramming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;음.. 일단 dp 정의까지는 어렵지 않았구요.. 상태 전이 시키고 역추적으로 들어가서 빼고나서 정렬하면 됩니다.
그래서 정렬 후에 출력하면 바로 맞을 수 잇어여 좋은 dp 문제 같습니다!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include&amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
using ll = long long;

int inf = 1e9+1000;

int nums[200&apos;001];
int n;
int dp[200&apos;001][4]; // dp[i][j]는 i번째 수를 보고 있을때, j번의 정렬 상태이다.

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    
    cin &amp;gt;&amp;gt; n;
    for(int i = 0; i &amp;lt; n; i++){
        cin &amp;gt;&amp;gt; nums[i];
    }
    


    dp[1][0] = nums[0];
    dp[1][1] = 1;
    dp[1][2] = 1;
    dp[1][3] = 1;
    
    
    
    for(int i = 2; i &amp;lt;= n; i++){
        dp[i][0] = inf;
        // 안돼?? 그럼 넣을게
        if(dp[i-1][0] &amp;lt;= nums[i-1]) dp[i][0] = nums[i-1];
        
        for(int j = 1; j &amp;lt; 4; j++){
            // 전 위치에서 변경하고 넣는 경우?
            dp[i][j] = dp[i-1][j-1];
            
            
            // 전 위치에서 j를 변경하지 않고, 내가 바꿔야 되는 경우
            if(dp[i-1][j] &amp;lt;= nums[i-1]) dp[i][j] = min(dp[i][j], nums[i-1]);
        }
    }
    
    
    
    
    if(min({dp[n][0], dp[n][1], dp[n][2], dp[n][3]}) == inf) cout &amp;lt;&amp;lt; &quot;NO&quot;;
    else {
        cout &amp;lt;&amp;lt; &quot;YES\n&quot;;
        vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; result;
        
        
        int count = 0;
        int minimum = inf;
        for(int i = 0; i &amp;lt; 4; i++){
            if(minimum &amp;gt; dp[n][i]){
                minimum = dp[n][i];
                count = i;
            }
        }
        
        cout &amp;lt;&amp;lt; count &amp;lt;&amp;lt; &quot;\n&quot;;
        
        
        
        for(int i = n; i &amp;gt; 0; i--){
            if(!count) break;
            if(dp[i-1][count] &amp;gt; nums[i-1] || nums[i-1] &amp;gt; dp[i-1][count-1]){
                result.push_back({i, dp[i][count--]});
                
            }
        }
        
        
        sort(result.begin(), result.end());
        for(auto p : result){
            cout &amp;lt;&amp;lt; p.first &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; p.second &amp;lt;&amp;lt; &quot;\n&quot;;
        }
        
        
        
    }
    
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 성인 게임</title><link>https://compy07.github.io/Blog/posts/boj/23256/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/23256/</guid><description>재활 13일차</description><pubDate>Sat, 15 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/23256&quot;&gt;성인 게임&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1≤T≤1,000&amp;lt;br/&amp;gt;1≤N≤1,000,000)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;우왕... 약간 처음보는 유형이라 조금 해매긴 했습니다. 나눠서 푸는 이런 느낌 오호 재밌었습니다. 이게 칼날이 막대기로 나오냐 아니냐 요걸로 나눠서 풀면 됩니다.&lt;/p&gt;
&lt;p&gt;생각보다 어려웠따! 넵&lt;/p&gt;
&lt;p&gt;아 그리구 식이 길어지니까 vim으로 하다가 이제 약간 IDE가 얼마나 편한지 알았습니다. 제가 원래 Xcode를 쓰고 있었는데... 그것도 기능이 참 많은 거였네요 허헣&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
using ll = long long;
ll mod = 1000000007;
int t, n;
ll dp[2][1&apos;000&apos;001];

void solve() {
    dp[0][0] = 1;
    dp[0][1] = 7;
    dp[1][1] = 3;
    
    for(int i = 2; i &amp;lt; 1&apos;000&apos;001; i++) {
                dp[0][i] = ((3 * dp[0][i-1]) % mod + (4 * dp[1][i-1]) % mod) % mod;
                dp[1][i] = (dp[0][i-1] + (2 * dp[1][i-1]) % mod) % mod;
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cin &amp;gt;&amp;gt; t;
    solve();
    while(t--&amp;gt;0) {
        cin &amp;gt;&amp;gt; n;
        cout &amp;lt;&amp;lt; dp[0][n] &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 모든 스택 수열</title><link>https://compy07.github.io/Blog/posts/boj/23284/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/23284/</guid><description>재활 on..</description><pubDate>Sat, 01 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/23284&quot;&gt;모든 스택 수열&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= n &amp;lt;= 10)&lt;/td&gt;
&lt;td&gt;BackTracking, Stack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;스택 (stack)은 기본적인 자료구조 중 하나로, 컴퓨터 프로그램을 작성할 때 자주 이용되는 개념이다.
스택은 자료를 넣는 (push) 입구와 자료를 뽑는 (pop) 입구가 같아 제일 나중에 들어간 자료가 제일 먼저 나오는 (LIFO, Last in First out) 특성을 가지고 있다.
1부터 n까지의 수를 스택에 넣었다가 뽑아 늘어놓음으로써, 하나의 수열을 만들 수 있다.
이때, 스택에 push하는 순서는 반드시 오름차순을 지키도록 한다고 하자. n이 주어졌을 때, 만들 수 있는 모든 스택 수열을 구해보자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;내가 이 문제를 처음에 ??? 이 문제 왜 골드냐 이러고 풀었다가 허헣 &quot;어 머야 왜 이 코드가 예시랑 다르지?&quot;&lt;/p&gt;
&lt;p&gt;내가 &quot;순서는 반드시 오름차순&quot;을 까먹고 그냥 사전순으로 모든 조합을 해버린 것이다... 허허ㅓ허허허ㅓ헣&lt;/p&gt;
&lt;p&gt;그래서 그것을 보고서 아 바꿔야하네 하고 좀 뜯어 고쳐서 나온 코드가 바로 이런데.. 사실 반드시 오름차순으로 들어오는 것을 잘 이용하면 이 코드보다 뭔가 더 좋은 코드가 나오지 않을까 하지만
일단 시간이 12ms로 굉장히 빠르게 나온 편이라 또 N이 최대 10밖에 안 나오기 때문에 딱히 문제는 없을 것 같다. 애초에 문제가 브포 문제로 나온 것 같아서 ㅎㅎ&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;stack&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;

int n;

void solution(stack&amp;lt;int&amp;gt;&amp;amp; st, vector&amp;lt;int&amp;gt;&amp;amp; current, int next, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; result) {
   if (current.size() == n) {
       result.push_back(current);
       return;
   }
   
   if (next &amp;lt;= n) {
       st.push(next);
       solution(st, current, next + 1, result);
       st.pop();
   }
   
   if (!st.empty()) {
       int top = st.top();
       st.pop();
       current.push_back(top);
       solution(st, current, next, result);
       current.pop_back();
       st.push(top);
   }
}

int main() {
   cin &amp;gt;&amp;gt; n;
   stack&amp;lt;int&amp;gt; st;
   vector&amp;lt;int&amp;gt; current;
   vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; result;
   
   solution(st, current, 1, result);
   
   for (int i = result.size() - 1; i &amp;gt; -1; i--) {
       for (int num : result[i]) {
           cout &amp;lt;&amp;lt; num &amp;lt;&amp;lt; &quot; &quot;;
       }
       cout &amp;lt;&amp;lt; &quot;\n&quot;;
   }
   
   return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 두 용액</title><link>https://compy07.github.io/Blog/posts/boj/2470/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2470/</guid><description>2470 two pointer 해설</description><pubDate>Sat, 22 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2470&quot;&gt;두 용액&lt;/a&gt;&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(2 ≤ N ≤ 100,000,-1,000,000,000 ≤ element ≤ 1,000,000,000)&lt;/td&gt;
&lt;td&gt;TwoPointer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;KOI 부설 과학연구소에서는 많은 종류의 산성 용액과 알칼리성 용액을 보유하고 있다.
각 용액에는 그 용액의 특성을 나타내는 하나의 정수가 주어져있다.
산성 용액의 특성값은 1부터 1,000,000,000까지의 양의 정수로 나타내고,
알칼리성 용액의 특성값은 -1부터 -1,000,000,000까지의 음의 정수로 나타낸다.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
같은 양의 두 용액을 혼합한 용액의 특성값은 혼합에 사용된 각 용액의 특성값의 합으로 정의한다.
이 연구소에서는 같은 양의 두 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들려고 한다.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
예를 들어, 주어진 용액들의 특성값이 [-2, 4, -99, -1, 98]인 경우에는 특성값이 -99인 용액과 특성값이 98인 용액을 혼합하면 특성값이 -1인 용액을 만들 수 있고, 이 용액이 특성값이 0에 가장 가까운 용액이다.&amp;lt;br&amp;gt;
참고로, 두 종류의 알칼리성 용액만으로나 혹은 두 종류의 산성 용액만으로 특성값이 0에 가장 가까운 혼합 용액을 만드는 경우도 존재할 수 있다.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
산성 용액과 알칼리성 용액의 특성값이 주어졌을 때, 이 중 두 개의 서로 다른 용액을 혼합하여 특성값이 &lt;strong&gt;0에 가장 가까운 용액을 만들어내는 두 용액을 찾는&lt;/strong&gt; 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;우리의 목표는 &lt;strong&gt;&quot;주어진 수(0을 제외한 범위 안에 정수)들 중에서 서로다른 2개를 골라서 0에 가장 가까운 수를 만들어라.&quot;&lt;/strong&gt; 이다.&lt;/p&gt;
&lt;p&gt;여기서 제일 먼저 생각해볼 것은 어떻게 0에 가까운 수를 만들 것인가? 이다.&lt;/p&gt;
&lt;p&gt;여러가지를 생각해보자!&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;BruteForce&lt;/li&gt;
&lt;li&gt;Sort&lt;/li&gt;
&lt;li&gt;Two Pointer&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 3가지를 생각해보자.&lt;/p&gt;
&lt;p&gt;일단 먼저 전체를 탐색하는 BruteForce이다.&lt;/p&gt;
&lt;p&gt;N의 최댓값인 100,000으로 계산을 해보자면 모든 수들과 다하고 비교하는 과정에서 최악의 경우엔 N^2 즉 O(N^2)이 되므로 시간초과에 걸리게 된다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;다음으론 Sort이다. 정렬을 하여 작은 수, 큰 수들끼리 더해서 0에 가까운 수를 만들어내는 방법이다. 사실상 이것도 정확히 어떤 값이 0에 가까운 수인지 모르기 때문에 넘어간다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;마지막으로 TwoPointer이다. 일단 문제에서 &quot;2개의 용액을 골라서&quot;를 보자마자 이 방법으로 접근해야 되겠다고 생각이 들었다. 투 포인터로 접근하세요!라고 문제에서 말하고 있어서 나는 3번째 방법인 투 포인터로 접근하여 문제를 풀이하였다.&lt;/p&gt;
&lt;p&gt;내가 선택한 방법은 일단 받은 모든 용액들을 오름차순으로 정렬 후, 각 끝(제일 작은 수, 제일 큰 수)부터 시작하여 0을 기점으로 큰지 작은지를 판단 후 작은 수 쪽의 값을 큰 값 쪽으로 당기거나 큰 수 쪽의 값을 작은 수 쪽으로 당기는 방법으로 0과 제일 가까운 수를 알아내도록 하였다.&lt;/p&gt;
&lt;p&gt;일단 내가 말한 내용을 정리해 보자면,&lt;/p&gt;
&lt;p&gt;투 포인터로 풀이하기 위해서 작은 수와 큰 수들이 모여있는 곳을 나눈다.(단조화)
작은 쪽 수에서 시작하는 포인터(left), 큰 쪽 수에서 시작하는 포인터(right) 이 2개의 포인터로 0에 가까운 용액을 찾아낸다.
현재 left와 right가 가르키고 있는 용액이 0보다 크다면, 작다면으로 나누어 right를 left쪽으로 당기거나, left를 right쪽으로 당겨와 다시 반복해 제일 0과 가까운 수를 찾도록 한다.
이렇게 요약할 수 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;단조화&lt;/li&gt;
&lt;li&gt;left, right의 포인터 시작&lt;/li&gt;
&lt;li&gt;left-&amp;gt; 큰 수쪽으로, right -&amp;gt; 작은 수 쪽으로 탐색 시작&lt;/li&gt;
&lt;li&gt;최적의 해 찾음.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이제 코드로 확인해 보자!&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;전역 변수&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int n;

vector&amp;lt;int&amp;gt; nums;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;N
&lt;ul&gt;
&lt;li&gt;몇 개의 용액이 들어오는지 알려주는 변수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;nums
&lt;ul&gt;
&lt;li&gt;각 용액의 특성값을 저장하는 벡터&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Main&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int main(){
    fast_io
    
    cin &amp;gt;&amp;gt; n;
    nums.resize(n);
    
    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; nums[i];
    
    sort(nums.begin(), nums.end());
    
    int left= 0, right = n-1;
    pair&amp;lt;int, int&amp;gt; result = {MAX, 0};
    
    while(left &amp;lt; right){
        if(diff(result.first, result.second) &amp;gt; diff(nums[left], nums[right])) result = {nums[left], nums[right]};
        
        if((nums[left] + nums[right]) &amp;lt; 0) left++;
        else right--;
    }
    
    cout &amp;lt;&amp;lt; result.first &amp;lt;&amp;lt; &quot; &quot;&amp;lt;&amp;lt; result.second;
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;main은 입력을 받고, 바로 투 포인터를 이용해서 두 용액의 합이 0과 가장 가까운 두 용액을 구하는 코드입니다.&lt;/p&gt;
&lt;p&gt;sort는 C++ STL(algorithm에 존재)에 내장 함수입니다.&lt;/p&gt;
&lt;p&gt;sort를 통해서 정렬해주고, 가장 작은 수부터 시작하는 left, 가장 큰 수부터 시작하는 right를 인덱스로 초기화 해줍니다.&lt;/p&gt;
&lt;p&gt;diff는 간단히 abs(용액1+용액2)의 연산을 해주는 함수입니다.&lt;/p&gt;
&lt;p&gt;현재 등록되어있는 두 용액의 합보다 지금 가르키고 있는 두 용액의 합이 0에 더 가깝다면, result를 업데이트 해줍니다.&lt;/p&gt;
&lt;p&gt;또한 현재의 합이 0보다 작다면 더 큰 값으로 탐색할 수 있게 left를 +1하고, 합이 0보다 크거나 같다면 right-- 해줍니다.&lt;/p&gt;
&lt;p&gt;여기서 같다면 어차피 위에서 result가 업데이트 되었기 때문에 상관할 같은 상황은 고려할 필요가 없습니다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이렇게 반복하면서 left와 right가 크로스될 때까지 돌게된다면, 간단히 두 용액의 합이 0에 가장 가까운 두 용액을 얻어낼 수 있습니다.&lt;/p&gt;
&lt;p&gt;참 간단하죠잉?&lt;/p&gt;
&lt;p&gt;다들 즐거운 PS 하세용!!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MAX 2&apos;100&apos;000&apos;000
#define LLMAX 9&apos;223&apos;372&apos;036&apos;854&apos;775&apos;807
#define fast_io ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
#include &amp;lt;ctime&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
using ll = long long;
using ull = unsigned long long;
using namespace std;

int n;

vector&amp;lt;int&amp;gt; nums;

int diff(int a, int b){ return abs(a + b); }

int main(){
    fast_io
    
    cin &amp;gt;&amp;gt; n;
    nums.resize(n);
    
    // 1 상근, 0 창영
    
    // 1 0 1 1 0 1 0 0
    
    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; nums[i];
    
    sort(nums.begin(), nums.end());
    
    int left= 0, right = n-1;
    pair&amp;lt;int, int&amp;gt; result = {MAX, 0};
    
    while(left &amp;lt; right){
        if(diff(result.first, result.second) &amp;gt; diff(nums[left], nums[right])) result = {nums[left], nums[right]};
        
        if((nums[left] + nums[right]) &amp;lt; 0) left++;
        else right--;
    }
    
    cout &amp;lt;&amp;lt; result.first &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; result.second;
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 색상환</title><link>https://compy07.github.io/Blog/posts/boj/2482/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2482/</guid><description>dpdpdpdpdpdpdpdpdp</description><pubDate>Tue, 01 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2482&quot;&gt;색상환&lt;/a&gt;&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(4 ≤ N ≤ 1,000,1 ≤ K ≤ N)&lt;/td&gt;
&lt;td&gt;DP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;색을 표현하는 기본 요소를 이용하여 표시할 수 있는 모든 색 중에서 대표적인 색을 고리 모양으로 연결하여 나타낸 것을 색상환이라고 한다. 미국의 화가 먼셀(Munsell)이 교육용으로 고안한 20색상환이 널리 알려져 있다. 아래 그림은 먼셀의 20색상환을 보여준다.&lt;/p&gt;
&lt;p&gt;색상환에서 인접한 두 색은 비슷하여 언뜻 보면 구별하기 어렵다. 위 그림의 20색상환에서 다홍은 빨강과 인접하고 또 주황과도 인접하다. 풀색은 연두, 녹색과 인접하다. 시각적 대비 효과를 얻기 위하여 인접한 두 색을 동시에 사용하지 않기로 한다.&lt;/p&gt;
&lt;p&gt;주어진 색상환에서 시각적 대비 효과를 얻기 위하여 서로 이웃하지 않은 색들을 선택하는 경우의 수를 생각해 보자.  먼셀의 20색상환에서 시각적 대비 효과를 얻을 수 있게 10개의 색을 선택하는 경우의 수는 2이지만, 시각적 대비 효과를 얻을 수 있게 11개 이상의 색을 선택할 수 없으므로 이 경우의 수는 0이다.&lt;/p&gt;
&lt;p&gt;주어진 정수 N과 K에 대하여, N개의 색으로 구성되어 있는 색상환 (N색상환)에서 어떤 인접한 두 색도 동시에 선택하지 않으면서 서로 다른 K개의 색을 선택하는 경우의 수를 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;일단 문제를 보고서 생각할 것 없이 나는 이 문제가 dp 문제인 것을 알고 들어왔다.
그래서 바로 dp 정의를 생각해 보았는데... 생각보다 어려웠다.
아직 내가 dp에 대해서 잘 모르는 것을 뼈저리게 느낀 문제였다.&lt;/p&gt;
&lt;p&gt;일단 내 풀이 방법은 간단하다.
dp[i][j][k]는 0~i 번째 까지의 색을 고려할 때, j개의 색을 고른다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;k = 0
&lt;ul&gt;
&lt;li&gt;0~i번째 까지의 색을 고려할 때, j개의 색을 고르는데 현재의 단계 즉 i번째를 제외한 j개를 고른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;k = 1
&lt;ul&gt;
&lt;li&gt;0~i번째 까지의 색을 고려할 때, j개의 색을 고르는데 현재의 단계 즉 i번째를 포함한 j개를 고른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 정의를 하고 점화식을 짜러 들어갔다.&lt;/p&gt;
&lt;p&gt;일단 현재 나 자신이 선택될 수 있는 조건이 무엇인가? 를 생각했다.
내가 선택이 가능하다는 것, 그리고 내가 설계한 dp 구조에서는 현재의 나를 마지막 보니까 &quot;내 위치 - 1&quot;의 색을 고르지만 않으면 된다.&lt;/p&gt;
&lt;p&gt;그래서 현재 나를 고를 때 나는 무엇을 고려하냐 바로 dp[i-1][j-1][0]만을 고려하여 더하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
그렇다면 현재의 나를 고르지 않는 경우의 수는 어떻게 구할 수 있을까?&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;나를 선택하지 않는다는 것은 나의 전 상태를 고려할 수 있다는 의미이다. 그래서 dp[i-1][j][1]을 더한다.
그런데 내가 활성화되지 않았어도, dp[i-1][j][0]을 고려할 수 있다 그래서 결과적으로
dp[i][j][0] = dp[i-1][j][0] + dp[i-1][j][1]
이렇게 된다.&lt;/p&gt;
&lt;p&gt;정리해보자&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;k = 0일때&lt;/strong&gt; &amp;lt;br&amp;gt;
dp[i][j][0] = dp[i-1][j][0] + dp[i-1][j][1]&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;k = 1일때&lt;/strong&gt; &amp;lt;br&amp;gt;
dp[i][j][1] = dp[i-1][j-1][0]&lt;/p&gt;
&lt;p&gt;이런 점화식을 만들 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;당연하겠지만 초기에 설정되는 값이 굉장히 중요하다. 왜냐하면 문제가 원형 문제기 때문에 N 위치에 있는 색을 고르지 않앗을 때와 N의 위치를 고를 때의
처음 값을 다르게 설정하여 문제를 풀었다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#define MOD 1000000003
using namespace std;
int N, K;
int dp[1001][1001][2];

int solution(int n, int k, bool check){
    
    if(k*2 -1 &amp;gt; n) return 0;
    
    if(dp[n][k][check] != -1) return dp[n][k][check];
    
    if(check) dp[n][k][check] = solution(n-1, k-1, false) % MOD;
    else dp[n][k][check] = (solution(n-1, k, true) + solution(n-1, k, false)) % MOD;
    return dp[n][k][check];
}

int main(){
    cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; K;
    int result = 0;
    
    if(K == 1){
        cout &amp;lt;&amp;lt; N;
        return 0;
    }
    for(int i = 0; i &amp;lt; N+1; i++){
        for(int j = 0; j &amp;lt; K+1; j++){
            dp[i][j][false] = -1;
            dp[i][j][true] = -1;
        }
        
        dp[i][0][false] = 1;
        dp[i][0][true] = 0;
        
        dp[i][1][true] = 1;
        dp[i][1][false] = i-1;
    }
    result += solution(N, K, false) % MOD;
    
    for(int i = 0; i &amp;lt; N+1; i++){
        for(int j = 0; j &amp;lt; K+1; j++){
            dp[i][j][false] = -1;
            dp[i][j][true] = -1;
  
        }
        dp[i][1][true] = 1;
        dp[i][1][false] = i-2;
  
    }
    for(int j = 0; j &amp;lt; K+1; j++){
        dp[1][j][false] = 0;
        dp[1][j][true] = 0;
        dp[0][j][false] = 0;
        dp[0][j][true] = 0;

    }

    result += solution(N, K, true);
 
    cout &amp;lt;&amp;lt; (result % MOD) &amp;lt;&amp;lt; &quot;\n&quot;;


    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 괄호의 값</title><link>https://compy07.github.io/Blog/posts/boj/2504/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2504/</guid><description>재활 14일차</description><pubDate>Wed, 08 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2504&quot;&gt;괄호의 값&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ string_length ≤ 30)&lt;/td&gt;
&lt;td&gt;Data Structure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 간단한 stack 구현 문제였습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = unsigned long long;
using ld = long double;
//ll MOD = 1&apos;000&apos;000&apos;007;
ll MOD = 1&apos;000&apos;000&apos;000;
//ll MOD = 1.8446744074E19;

struct info {
    char c;
    int val;
};

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    vector&amp;lt;info&amp;gt; stack;
    
    string input;
    cin &amp;gt;&amp;gt; input;
    
    
    for(int i = 0; i &amp;lt; input.size(); i++){
        if(stack.empty()){ stack.push_back({input[i], 0}); continue;}
        
        char current = input[i];
        info pre = stack.back();
        if(pre.c== &apos;(&apos; &amp;amp;&amp;amp; current == &apos;)&apos;){
            stack.pop_back();
            stack.push_back({0, 2});
        }else if(pre.c == &apos;[&apos; &amp;amp;&amp;amp; current == &apos;]&apos;){
            stack.pop_back();
            stack.push_back({0, 3});
        }else if(pre.c == 0 &amp;amp;&amp;amp; (current == &apos;)&apos; || current == &apos;]&apos;)){
            char limit = current ==&apos;)&apos; ? &apos;(&apos; : &apos;[&apos;;
            int result = stack.back().val * (current == &apos;)&apos; ? 2 : 3);
            stack.pop_back();
            if(stack.empty() || stack.back().c != limit){
                cout &amp;lt;&amp;lt; 0;
                return 0;
            }
            stack.pop_back();
            stack.push_back({0, result});
            
            
            
        }else{
            stack.push_back({current, 0});
        }
        
        if(!stack.empty() &amp;amp;&amp;amp; stack.back().c == 0){
            int result = 0;
            while(!stack.empty() &amp;amp;&amp;amp; stack.back().c == 0) {
                result += stack.back().val;
                stack.pop_back();
            }
            
            stack.push_back({0, result});
        }
    }
    
    int result = 0;
    while(!stack.empty() &amp;amp;&amp;amp; stack.back().val != 0){
        result += stack.back().val; stack.pop_back();
    }
    
    if(stack.size() &amp;gt; 0) cout &amp;lt;&amp;lt; 0;
    else cout &amp;lt;&amp;lt; result;
    
    return 0;
}
/*
 
 ([()()()()]()()()())
 
 
 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 시루의 백화점 구경</title><link>https://compy07.github.io/Blog/posts/boj/25307/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/25307/</guid><description>재활 2일차</description><pubDate>Tue, 04 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/25307&quot;&gt;시루의 백화점 구경&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N, M ≤ 2000, 0 ≤ K ≤ 4000)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이 문제도 전형적인 BFS 문제이구요. 조금 BFS 연습하고 싶을 때 와서 풀어보면 좋을 것 같아요. 그냥 기본 BFS로 푸는거라서... 그런데 저는 조금 이상하게 푼 ㅓㄱ 같기두 하고요...&lt;/p&gt;
&lt;p&gt;뭔가 애매하고 들어간 느낌? 하튼 재밌었습니다... 낼 학교 준비해야징..&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;풀이 영상
&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/EZ-4VwoFTC8?si=OfEU5zTqgpc9lNN3&quot; title=&quot;PS&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
using namespace std;

using ll = long long;

int inf = 1000000000;

int n, m, k;
int visited[2001][2001];
int board[2001][2001];
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, -1, 1};


void init(vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;amp; hs){
    queue&amp;lt;pair&amp;lt;int, pair&amp;lt;int,int&amp;gt;&amp;gt;&amp;gt; q;
    
    
    
    for(pair&amp;lt;int, int&amp;gt; pos : hs) {
        q.push({0, pos});
        visited[pos.first][pos.second] = -1;
    }
    while(!q.empty()){
        auto [cost, p] = q.front(); q.pop();
        visited[p.first][p.second] = -1;
//            cout &amp;lt;&amp;lt; &quot;start: &quot; &amp;lt;&amp;lt; p.first &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; p.second &amp;lt;&amp;lt; &quot;\n&quot;; 이게 아닌가...?
        for(int i = 0; i &amp;lt; 4; i++){
            int nx = p.second + dx[i];
            int ny = p.first + dy[i];

            if(cost &amp;lt; k &amp;amp;&amp;amp; 0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp;
               visited[ny][nx] != -1){
//                    cout &amp;lt;&amp;lt; ny &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; nx &amp;lt;&amp;lt; &quot;\n&quot;;
                visited[ny][nx] = -1;
                q.push({cost+1, {ny, nx}});
            }
        }
    }
//    for(int i = 0; i &amp;lt; n; i++){
//        for(int j = 0; j &amp;lt; m; j++){
//            cout &amp;lt;&amp;lt; visited[i][j] &amp;lt;&amp;lt; &quot; &quot;;
//        }
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//    }

}
int solution(pair&amp;lt;int ,int&amp;gt; player){
    
    
    
    priority_queue&amp;lt;pair&amp;lt;int, pair&amp;lt;int,int&amp;gt;&amp;gt;, vector&amp;lt;pair&amp;lt;int, pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt;, greater&amp;lt;pair&amp;lt;int, pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt;&amp;gt; q;
    q.push({0, player});
    
    while(!q.empty()){
        auto [cost, pos] = q.top(); q.pop();
        
        if(board[pos.first][pos.second] == 2) return cost;
        
        
        for(int i = 0; i &amp;lt; 4; i++){
            int nx = pos.second + dx[i];
            int ny = pos.first + dy[i];
            
            if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; n &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; m &amp;amp;&amp;amp; visited[ny][nx] &amp;gt; cost &amp;amp;&amp;amp; board[ny][nx] != 1){
                visited[ny][nx] = cost;
                q.push({cost+1, {ny, nx}});
            }
        }

    }

    return -1;
}
int main() {
    ios_base::sync_with_stdio(0); cin.tie(0);
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt;k;
    
    pair&amp;lt;int, int&amp;gt; player;
    vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; hs;
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; m; j++){
            cin &amp;gt;&amp;gt; board[i][j];
            
            switch(board[i][j]){
                case 3:
                    hs.push_back({i, j});
                    break;
                case 4:
                    player = {i, j};
                case 1: // 벽 뒤에 있는 경우도 있을듯.
                case 2:
                    break;
            }
            visited[i][j] = inf;
            
        }
    }
    
    init(hs);


    cout &amp;lt;&amp;lt; solution(player);
    
    

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 인공 신경망</title><link>https://compy07.github.io/Blog/posts/boj/25341/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/25341/</guid><description>재활 20일차 - 2</description><pubDate>Sun, 23 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/25341&quot;&gt;인공 신경망&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;문제 참고(너무 많은 관계로)&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;솔직히 처음에 이 문제 자체가 호감이었다. 일단 우리학교 문제였고, 인공지능 관련 문제였다. 또한 문제를 풀면서 굉장히 교육적으로 좋은 문제라는 것도 알게 되었는데 굉장히 잘 만든 문제처럼 보인다.&lt;/p&gt;
&lt;p&gt;처음은 당연히 그냥 구현 문제인줄 알고 그대로 구현하였다. ps하면서 처음으로 class를 사용해서 구현하는데 꽤 재미있었다. 이런 문제들이 많아지면 좋을 듯!&lt;/p&gt;
&lt;p&gt;그래서 Neural을 정의하고, 그걸 연산해서 사용하는 코드를 짰더니 당연히 O(N^3)으로 터지게 되었다. 그래서 어떻게 줄일까? 생각하다가 잠시 질문 게시판을 참고해서 아하 일차식으로 나타내서 한번 계산하면 다른 쿼리 처리를 빠르게 할 수 있구나!
라고 찾을 수 있습니다.&lt;/p&gt;
&lt;p&gt;조금 인공지능 얘기를 좀 해보겠습니다.&lt;/p&gt;
&lt;p&gt;신경망은 순수하게 선형 연산만을 합니다. 그래서 좀 보자면&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;은닉층 [input * weight] + bias&lt;/li&gt;
&lt;li&gt;출력층 [hidden * weight] + bias&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 순수 선형 연산으로 이루어져 있어서 여러개가 겹쳐져 있더라도 선형 연산을 압축 시킬 수 있다는 이런 특징을 알 수 있구요!&lt;/p&gt;
&lt;p&gt;그래서 여러개의 층들을 하나의 입력으로 보자면 어떻게 되는가?
예시로 위의 은닉, 출력을 보자면&lt;/p&gt;
&lt;p&gt;은닉층1: y = w1&lt;em&gt;x + b1&amp;lt;/br&amp;gt;
출력층:  z = w2&lt;/em&gt;y + b2&lt;/p&gt;
&lt;p&gt;&amp;lt;code&amp;gt;z = (w2*w1)&lt;em&gt;x + (w2&lt;/em&gt;b1 + b2)&amp;lt;/code&amp;gt;&lt;/p&gt;
&lt;p&gt;이렇게 표현 가능하죠?&lt;/p&gt;
&lt;p&gt;근데 이렇게 문제를 풀 수 있는데 여기서 좀 더 생각하자면.. 이러한 선형성을 띈다.. 그러면 복잡한 패턴이나 추상적 특징들을 잡아낼 수가 없어요&lt;/p&gt;
&lt;p&gt;좀 대표적인 예시를 들자면 XOR이 그때 구현이 안돼서 많이 인공지능 붐이 한번 다시 죽었을 때... 생각을 해봅시다.. 복잡한게 학습이 안되니까 이 선형성을 어떻게 죽일까... 이런 생각을 한거죠&lt;/p&gt;
&lt;p&gt;그래서 우리가 activation function을 사용합니다. 왜냐 앞서 말했던 그 &quot;선형적&quot; 특징을 좀 비선형적으로 바꾸기 위해서 사용하는 거죠.&lt;/p&gt;
&lt;p&gt;ReLU, Sigmoid를 보통 사용하구... 이 비선형성을 도입함으로써 복잡하고, 추상적인 특징들 패턴들을 알아볼 수 있게됩니다.&lt;/p&gt;
&lt;p&gt;그래서 저는 이 문제가 굉장히 많은 것을 알려주고 있다고 생각합니다.&lt;/p&gt;
&lt;p&gt;와 다시 생각해봐도 좋은 문제네요&lt;/p&gt;
&lt;p&gt;사실 저도 이렇게 깊이 생각은 안 했는데.. 다른 사람들이 푼 것들 말하는 것들을 보면서 좀 더 넓은 시야를 얻을 수 있었던 것 같습니다.
고수님들 언제나 감사합니다!&lt;/p&gt;
&lt;p&gt;(사실 밤새고 적었습니다.. 혹시나 오류가 존재하면 알려주셔요!)&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;

using ll = long long;
int input_layer[2001];

class neural{
public:
    vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; inputs; // input, weight
    int bias;
    ll calc;
    neural(vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; &amp;amp;inputs, int bias){
        this-&amp;gt;inputs = inputs;
        this-&amp;gt;bias = bias;
        calc = -1;
    }
    neural(){
        calc = -1;
    }
    
    ll calculation(int *input_layer, bool check){
        if(!check) return calc;
        
        ll result = 0;
        for(pair&amp;lt;int, int&amp;gt; p : inputs){
            auto [input, weight] = p;
            
            int input_value = input_layer[input];
            result += input_value * weight;
        }
        calc = result + (ll) bias;
        return result + (ll) bias;
    }
    
    ll calculation(neural *hidden){
        ll result = 0;
        for(pair&amp;lt;int, int&amp;gt; p : inputs){
            auto [input, weight] = p;
            
            neural input_value = hidden[input];
            result += input_value.calculation(input_layer, false) * weight;
        }
        return result + (ll) bias;
    }
    
    
    
};

neural hidden_layer[2001];

void solution(){
    int n, m, q;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; q;
    
    vector&amp;lt;ll&amp;gt; final_coefficients(n + 1, 0);
    ll final_bias = 0;

    // hidden layer input
    int c;
    for(int i = 0; i &amp;lt; m; i++){
        cin &amp;gt;&amp;gt; c;
        vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; current(c);
        for(int j = 0; j &amp;lt; c; j++) cin &amp;gt;&amp;gt; current[j].first;
        for(int j = 0; j &amp;lt; c; j++) cin &amp;gt;&amp;gt; current[j].second;
        int bias;
        cin &amp;gt;&amp;gt; bias;
        
        neural current_neural = neural(current, bias);
        
        
        hidden_layer[i] = current_neural;
    }
    
    vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; current(m);
    for(int i = 0; i &amp;lt; m; i++) current[i].first = i;
    for(int i = 0; i &amp;lt; m; i++) cin &amp;gt;&amp;gt; current[i].second;
    int bias;
    cin &amp;gt;&amp;gt; bias;
    
    neural output = neural(current, bias);
    for(int h = 0; h &amp;lt; m; h++){
        for(auto [input_idx, weight] : hidden_layer[h].inputs) final_coefficients[input_idx] += (ll)weight * output.inputs[h].second;
        
        final_bias += (ll)hidden_layer[output.inputs[h].first].bias * output.inputs[h].second;
    }
    final_bias += (ll)output.bias;
    
    while(q--){
        ll result = final_bias;
        for(int i = 1; i &amp;lt;= n; i++){
            int input;
            cin &amp;gt;&amp;gt; input;
            result += input * final_coefficients[i];
        }
        cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
    }
}

int main(){
    
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    
    
    solution();
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] Shooting Game</title><link>https://compy07.github.io/Blog/posts/boj/25607/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/25607/</guid><description>다시 돌아온 빡센 dp</description><pubDate>Mon, 31 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/25607&quot;&gt;Shooting Game&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(5 ≤ N ≤ 100, 1 ≤ K ≤ 15)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;처음에는 그냥 간단히 bfs로 풀이할 수 있을거라고 생각했다..&lt;/p&gt;
&lt;p&gt;그래서&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;tuple&amp;gt;

#define inf 1000000000
using namespace std;
using ll = long long;

int n, m;

pair&amp;lt;int, int&amp;gt; bomb[16];

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    for(int i = 0; i &amp;lt; m; i++) cin &amp;gt;&amp;gt; bomb[i].first &amp;gt;&amp;gt; bomb[i].second;
    
    int cp;
    cin &amp;gt;&amp;gt; cp;
    
    priority_queue&amp;lt;pair&amp;lt;int, pair&amp;lt;int, short&amp;gt;&amp;gt;, vector&amp;lt;pair&amp;lt;int, pair&amp;lt;int, short&amp;gt;&amp;gt;&amp;gt;, greater&amp;lt;pair&amp;lt;int, pair&amp;lt;int, short&amp;gt;&amp;gt;&amp;gt;&amp;gt; q; // {current_time, {current_position, current_visited}}
    
    q.push({0, {cp, 0}});
    
    short complete = 1;
    for(int i = 0; i &amp;lt; m; i++) complete |= 1 &amp;lt;&amp;lt; i;
    
    
    
    while(!q.empty()){
        auto [time, _] = q.top(); q.pop();
        auto [pos, visited] = _;
        
//        cout &amp;lt;&amp;lt; &quot;time: &quot; &amp;lt;&amp;lt; time &amp;lt;&amp;lt; &quot;\npos: &quot; &amp;lt;&amp;lt; pos &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; visited &amp;lt;&amp;lt; &quot;\n&quot;;
        
        if(visited == complete) {
            cout &amp;lt;&amp;lt; time;
            return 0;
        }
        
        bool impossible = false;
        for(int i = 0; i &amp;lt; m; i++){
            if(visited &amp;amp; (1 &amp;lt;&amp;lt; i)) continue;
//            cout &amp;lt;&amp;lt; &quot;time: &quot; &amp;lt;&amp;lt; bomb[i].first + time + 1 &amp;lt;&amp;lt; &quot;\npos: &quot; &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &quot;\n&quot;;

            if(abs(pos - bomb[i].second) + time + 1 &amp;gt;= n || bomb[i].first + time &amp;gt;= n){
                impossible = true;
                break;
            }
            
        }
        
        if(impossible) continue;
        
        for(int i = 0; i &amp;lt; m; i++){
            if(visited &amp;amp; (1 &amp;lt;&amp;lt; i)) continue;
            q.push({time + abs(pos - bomb[i].second) + 1, {bomb[i].second, visited | (1 &amp;lt;&amp;lt; i)}});
        }
        
        
    }
    
    cout &amp;lt;&amp;lt; -1;
    
    
    
    return 0;
    
    
    
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;역시.. 당연히 시간초과가 나왔다..
이때 든 생각... &quot;dp냐 너?&quot;&lt;/p&gt;
&lt;p&gt;보통 이런 어이없는 결과를 보게된다면 의심해보게 된다.&lt;/p&gt;
&lt;p&gt;아 근데 결국 dp로 접근을 못하고 그냥 bfs를 잘 최적화해보기로 맘을 먹고서 각 위치, 시간, 그리고 방문한 상태를 가지고 중복 실행이 되지 않도록 만들고, 이거를 토대로 돌렸다.&lt;/p&gt;
&lt;p&gt;이 과정에서 bitset이라는 썼는데 또 xcode는 include 안해도 알아서 처리를 해줘서 boj에서 많이 틀리는 원인이 되었다.. 허허...&lt;/p&gt;
&lt;p&gt;결국 최종적으로는 그냥 visited처리를 좀 더 잘한 코드로 ac를 받았다...&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;tuple&amp;gt;
#include &amp;lt;bitset&amp;gt;

#define inf 1000000000
using namespace std;
using ll = long long;

int n, m;

pair&amp;lt;int, int&amp;gt; bomb[16];
bitset&amp;lt;(1&amp;lt;&amp;lt;15)&amp;gt; dp[101][101];


void print(int current){
    cout &amp;lt;&amp;lt; &quot;state: &quot;;
    while(current &amp;gt; 0){
        cout &amp;lt;&amp;lt; (current &amp;amp; 1) &amp;lt;&amp;lt; &quot; &quot;;
        current/=2;
    }
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    for(int i = 0; i &amp;lt; m; i++) cin &amp;gt;&amp;gt; bomb[i].first &amp;gt;&amp;gt; bomb[i].second;
    
    int cp;
    cin &amp;gt;&amp;gt; cp;
    
    
    priority_queue&amp;lt;pair&amp;lt;int, pair&amp;lt;int, short&amp;gt;&amp;gt;, vector&amp;lt;pair&amp;lt;int, pair&amp;lt;int, short&amp;gt;&amp;gt;&amp;gt;, greater&amp;lt;pair&amp;lt;int, pair&amp;lt;int, short&amp;gt;&amp;gt;&amp;gt;&amp;gt; q; // {current_time, {current_position, current_visited}}
    
    q.push({0, {cp, 0}});
    
    short complete = 1;
    for(int i = 0; i &amp;lt; m; i++) complete |= 1 &amp;lt;&amp;lt; i;
    
    
    
    while(!q.empty()){
        auto [time, _] = q.top(); q.pop();
        auto [pos, visited] = _;
        
//        cout &amp;lt;&amp;lt; &quot;time: &quot; &amp;lt;&amp;lt; time &amp;lt;&amp;lt; &quot;\npos: &quot; &amp;lt;&amp;lt; pos &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; visited &amp;lt;&amp;lt; &quot;\n&quot;;
//        cout &amp;lt;&amp;lt; &quot;time: &quot; &amp;lt;&amp;lt; time &amp;lt;&amp;lt; &quot;\n&quot;;
//        print(visited);
        
        if(visited == complete) {
            cout &amp;lt;&amp;lt; time;
            return 0;
        }
        
        bool impossible = false;
        for(int i = 0; i &amp;lt; m; i++){
            if(visited &amp;amp; (1 &amp;lt;&amp;lt; i)) continue;
            if(abs(pos - bomb[i].second) + time + 1 &amp;gt;= n || bomb[i].first + time + abs(pos - bomb[i].second) &amp;gt;= n){
                impossible = true;
                break;
            }
        }
        
        if(impossible) continue;
        
        for(int i = 0; i &amp;lt; m; i++){
            if(visited &amp;amp; (1 &amp;lt;&amp;lt; i)) continue;
            if(dp[time + abs(pos - bomb[i].second) + 1][bomb[i].second][visited | (1 &amp;lt;&amp;lt; i)]) continue;
            q.push({time + abs(pos - bomb[i].second) + 1, {bomb[i].second, visited | (1 &amp;lt;&amp;lt; i)}});
            dp[time + abs(pos - bomb[i].second) + 1][bomb[i].second][visited | (1 &amp;lt;&amp;lt; i)] = true;
        }
        
        
    }
    
    cout &amp;lt;&amp;lt; -1;
    

    
    
    
    
    
    
    return 0;
    
    
    
}


/*
 
 3 1
 2 1
 2

 5 3
 1 2
 1 4
 1 5
 3

 
 4 3
 3 1
 2 1
 1 1
 1
 
 
 1 1
 1 1
 1
 
 
 
 3 2
 1 1
 1 2
 
 
 
 100 1
 96 1
 5
 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 싫은데요</title><link>https://compy07.github.io/Blog/posts/boj/25916/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/25916/</guid><description>재활 10일차 - 2</description><pubDate>Sat, 15 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/25916&quot;&gt;싫은데요&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 500,000&amp;lt;br/&amp;gt;1 ≤ M, A_i ≤ 1e9&amp;lt;br/&amp;gt;)&lt;/td&gt;
&lt;td&gt;Two Pointer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;범위를 슬라이싱 해야되는데 N이 5*10^5거든요? O(N^2)이 1초에 절대 안 돌아가기 때문에 다른 방법을 생각해야 했고.. 그게
바로 &quot;투 포인터&quot; 였습니다. 그래서 그냥 전형적인 같은 시작 투포인터를 이용하면 됩니당&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;


int nums[500002];

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int n, m;
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    
    
    for(int i = 1; i &amp;lt;= n; i++){
        cin &amp;gt;&amp;gt; nums[i];
    }
    
    int left = 1, right = 1;
    
    
    
    ll current = nums[1];
    ll result = 0;
    while(right &amp;lt;= n){
        if(right &amp;lt; left){
            right++;
            current += nums[right];
        }
        
        if(current &amp;lt;= m){
            result = max(result, current);
            right++;
            current += nums[right];
        }else{
            current-=nums[left];
            left++;
        }

    }
        
    cout &amp;lt;&amp;lt; result;
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] Jagged Skyline</title><link>https://compy07.github.io/Blog/posts/boj/26001/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/26001/</guid><description>재활 4일차</description><pubDate>Tue, 08 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/26001&quot;&gt;Jagged Skyline&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1 ≤ w ≤ 10000, 1 ≤ h ≤ 10^18)&lt;/td&gt;
&lt;td&gt;Random(?), Binary Search&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;와.. 이거는 접근은 잘 됐는데.. 이분탐색에서 if문 조건을 착각해서 시간을 날린 문제.. 헉&lt;/p&gt;
&lt;p&gt;접근까지는 그렇게 오래 안 걸리고 뭔가 어느정도 몇개 랜덤으로 보다보면 큰게 나와서 아래거를 좀 제거할 수 있을거 같은데? 이러한 아이디어에서 출발했습니다.
근데 나중에 if문 조건을 잘못 적어버려서 혼났네요.. ㅏ아아아ㅏ아아아아앙 분노의 연속 10번 제출을 해버려서 ㅠㅜ&lt;/p&gt;
&lt;p&gt;하튼 재밌었습니다.&lt;/p&gt;
&lt;p&gt;endl 쓰는거는 cout flush 해줘야해서 써준 겁니당. 그리고 인터렉트 이거 처음 풀어봤는데 재밌네요!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;random&amp;gt;
using namespace std;
using ll = long long;
ll w, h;
ll max_height = 0;
ll result_pos = 1;
ll global_cnt;
bool visited[10001];
bool check(ll w, ll h){
    cout &amp;lt;&amp;lt; &quot;? &quot; &amp;lt;&amp;lt; w &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; h &amp;lt;&amp;lt; endl;
    string response;
    cin &amp;gt;&amp;gt; response;
    return response == &quot;sky&quot;;
}

void solution(ll pos){
    global_cnt++;
    if(check(pos, max_height+1)) return;
    
    ll low = max_height;
    ll top = h;
    
    
    while(low &amp;lt; top){
        ll pivot = (low+top) / 2;
        global_cnt++;
        
        if(check(pos, pivot+1)) top = pivot;
        else low = pivot + 1;
    }
    
    if(top &amp;gt; max_height){
        max_height = top;
        result_pos = pos;
    }
    
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    
    global_cnt = 0;
    cin &amp;gt;&amp;gt; w &amp;gt;&amp;gt; h;
    random_device rd;
    mt19937 gen(rd());
    
    uniform_int_distribution&amp;lt;int&amp;gt; rand_(1, (int)w);
    
    for(int i = 0; i &amp;lt; w; i++){
        int width = rand_(gen);
        while(visited[width]) width = rand_(gen);
        
        visited[width] = true;
        if(global_cnt &amp;gt;= 12000) break;
        solution(width);
        
        if(max_height == h) break;
    }
    
    cout &amp;lt;&amp;lt; &quot;! &quot; &amp;lt;&amp;lt; result_pos &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; max_height;
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 로마 숫자</title><link>https://compy07.github.io/Blog/posts/boj/2608/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2608/</guid><description>재활 8일차.. 앱잼 이후에 다시</description><pubDate>Mon, 10 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2608&quot;&gt;로마 숫자&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;1≤ each password ≤ 2,000&amp;lt;br/&amp;gt;2,000 ≤ sum of the password ≤ 4,000&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;솔직히 나는 구현을 디게 멍청하게 풀었다. 풀다가 아 이길이 아닌데.. 이거 다르게 풀면 빠른데.. 이걸 알았지만 문제 뭐냐 여태까지 풀었던 코드가 꽤 길어서
그냥 오기로 내 방식대로 풀고 효율적인 코드도 작성해보고 둘 다 제출해봤다. 다행이 둘다 AC를 바로 맞아서 뿌듯하긴 했으나..&lt;/p&gt;
&lt;p&gt;실력이 많이 안 좋아진 것을 실감하는 계기가 되었다.... 무슨 몇 달 쉬면 퇴물이 되버리냐 더 열심히 해야겠다.
아 그리고 문제 이해가 약간 가볍게 읽으면 이상한데 빠지니 좀 잘 읽어보고 풀도록 하자!&lt;/p&gt;
&lt;p&gt;오늘의 정답 코드는 두개가 올라갑니다아&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드들 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;
using ll = long long;

char postNumbers[] = {
    &apos;I&apos; ,
    &apos;V&apos;,
    &apos;X&apos;,
    &apos;L&apos;,
    &apos;C&apos;,
    &apos;D&apos;,
    &apos;M&apos;};

int values[] = {
    1,
    5,
    10,
    50,
    100,
    500,
    1000
};


int solution(string current){
    int result = 0;
    int size = 7;
    int idx = 0;
    int currentStack = 0;
    while(idx &amp;lt; current.size()){
        bool check = false;
        int currentCost = 0;
        for(int i = 6; i &amp;gt; -1; i--){
            if(current[idx] == postNumbers[i]){
                if(i &amp;lt;= size){
                    size = i;
                    check = true;
                }
                currentCost = values[i];
                break;
            }
        }
        currentStack = values[size];
        idx++;
        result += currentCost - currentStack*(!check)*2;

    }
    return result;
    
}

int pow(int a, int b){
    int result = a * 1000;
    while(b--&amp;gt;0) result /= 10;
    return result;
}
string getResult(int num) {
    string result = &quot;&quot;;
    
    int values[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
    string numerals[] = {&quot;M&quot;, &quot;CM&quot;, &quot;D&quot;, &quot;CD&quot;, &quot;C&quot;, &quot;XC&quot;, &quot;L&quot;, &quot;XL&quot;, &quot;X&quot;, &quot;IX&quot;, &quot;V&quot;, &quot;IV&quot;, &quot;I&quot;};
    
    for(int i = 0; i &amp;lt; 13; i++) {
        while(num &amp;gt;= values[i]) {
            result += numerals[i];
            num -= values[i];
            if(numerals[i].size() &amp;gt; 1) break;
        }
    }
    
    return result;
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    string a, b;
    
    cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;
    
    int resultNumber = solution(a) + solution(b);
    
    
    cout &amp;lt;&amp;lt; resultNumber&amp;lt;&amp;lt;&quot;\n&quot;&amp;lt;&amp;lt;getResult(resultNumber);
    
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;
using ll = long long;

char postNumbers[] = {
    &apos;I&apos;,
    &apos;V&apos;,
    &apos;X&apos;,
    &apos;L&apos;,
    &apos;C&apos;,
    &apos;D&apos;,
    &apos;M&apos;
};

int values[] = {
    1,
    5,
    10,
    50,
    100,
    500,
    1000
};

int solution(string current) {
    int result = 0;
    int size = 7;
    int idx = 0;
    int currentStack = 0;
    
    while(idx &amp;lt; current.size()) {
        bool check = false;
        int currentCost = 0;
        
        for(int i = 6; i &amp;gt; -1; i--) {
            if(current[idx] == postNumbers[i]) {
                if(i &amp;lt;= size) {
                    size = i;
                    check = true;
                }
                currentCost = values[i];
                break;
            }
        }
        
        currentStack = values[size];
        idx++;
        result += currentCost - currentStack*(!check)*2;
    }
    return result;
}

int pow(int a, int b) {
    int result = a * 1000;
    while(b-- &amp;gt; 0) result /= 10;
    return result;
}

string getResult(string value) {
    string result = &quot;&quot;;
    bool check[] = {0, 0, 0, 0, 0, 0};
    string checkString[] = {
        &quot;IV&quot;,
        &quot;IX&quot;,
        &quot;XL&quot;,
        &quot;XC&quot;,
        &quot;CD&quot;,
        &quot;CM&quot;
    };
    
    int current = 0;
    while(current &amp;lt; value.size()) {
        int currentValue = value[current] - &apos;0&apos;;
        int position = value.size() - 1 - current;
        
        for(int i = 3; i &amp;gt; 1; i--) {
            int limit = i * i;
            if(currentValue == limit) {
                int idx = position * 2 + !(limit == 4);
                
                if(check[idx]) continue;
                check[idx] = true;
                result += checkString[idx];
                currentValue -= limit;
                
            }
            
        }
        
        for(int i = 6; i &amp;gt; -1; i--) {
            int power = 1;
            for(int j = 0; j &amp;lt; position; j++) power *= 10;
            
            if(values[i] &amp;gt; power * 5 || values[i] &amp;lt; power) continue;
            
            while(currentValue &amp;gt;= values[i] / power) {
                result += postNumbers[i];
                currentValue -= values[i] / power;
            }
        }
        current++;
    }
    return result;
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    string a, b;
    cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;
    
    int resultNumber = solution(a) + solution(b);
    
    string resultString = &quot;&quot;;
    int t = 1;
    while(resultNumber &amp;gt;= t) {
        resultString += &apos;0&apos; + resultNumber%(t*10)/t;
        t *= 10;
    }
    reverse(resultString.begin(), resultString.end());
    
    cout &amp;lt;&amp;lt; resultNumber&amp;lt;&amp;lt;&quot;\n&quot; &amp;lt;&amp;lt; getResult(resultString);
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 치즈</title><link>https://compy07.github.io/Blog/posts/boj/2638/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2638/</guid><description>자 치즈!!</description><pubDate>Mon, 01 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2638&quot;&gt;치즈&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(5&amp;lt;= N, M &amp;lt;= 100)&lt;/td&gt;
&lt;td&gt;BFS(너비우선탐색)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;N×M의 모눈종이 위에 아주 얇은 치즈가 &amp;lt;그림 1&amp;gt;과 같이 표시되어 있다. 단, N 은 세로 격자의 수이고, M 은 가로 격자의 수이다. 이 치즈는 냉동 보관을 해야만 하는데 실내온도에 내어놓으면 공기와 접촉하여 천천히 녹는다. 그런데 이러한 모눈종이 모양의 치즈에서 각 치즈 격자(작 은 정사각형 모양)의 4변 중에서 적어도 2변 이상이 실내온도의 공기와 접촉한 것은 정확히 한시간만에 녹아 없어져 버린다. 따라서 아래 &amp;lt;그림 1&amp;gt; 모양과 같은 치즈(회색으로 표시된 부분)라면 C로 표시된 모든 치즈 격자는 한 시간 후에 사라진다.&lt;/p&gt;
&lt;p&gt;&amp;lt;그림 2&amp;gt;와 같이 치즈 내부에 있는 공간은 치즈 외부 공기와 접촉하지 않는 것으로 가정한다. 그러므 로 이 공간에 접촉한 치즈 격자는 녹지 않고 C로 표시된 치즈 격자만 사라진다. 그러나 한 시간 후, 이 공간으로 외부공기가 유입되면 &amp;lt;그림 3&amp;gt;에서와 같이 C로 표시된 치즈 격자들이 사라지게 된다.&lt;/p&gt;
&lt;p&gt;모눈종이의 맨 가장자리에는 치즈가 놓이지 않는 것으로 가정한다. 입력으로 주어진 치즈가 모두 녹아 없어지는데 걸리는 정확한 시간을 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;p&gt;(그림은 boj를 참고해 주세요)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;문제를 읽어보니 대충 요약을 해보자면 &lt;strong&gt;&quot;격자로 구성되어 있는 치즈 중에서 &apos;바깥 공기&apos;와 &apos;두 변이 접촉&apos;한 치즈는 다음 시간에 사라진다. 모든 치즈가 사라진 정확한 시간을 구하는 것이 목표&quot;&lt;/strong&gt; 로 말할 수 있다.&lt;/p&gt;
&lt;p&gt;요약한 내용과 문제에서 주어진 정보들을 정리해 보자면&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;치즈는 &apos;바깥 공기&apos;와 &apos;두 변이 접촉&apos;하면 녹는다.&lt;/li&gt;
&lt;li&gt;모든 치즈가 사라지지 않는 경우의 수는 없다.(어떻게든 두 변이 닿는 치즈가 존재함)&lt;/li&gt;
&lt;li&gt;치즈가 녹는 시간은 접촉과 동시에 정확히 1시간이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;우리가 해결해야될 문제들을 더욱 구체화 시켜서 보도록 하자.(구현해야될 기능 정리)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;녹는 치즈들을 선별해내는 기능&lt;/li&gt;
&lt;li&gt;녹는 치즈들이 없어지고, 다음 공기(바깥 공기가 아닌)들과 접촉되는 기능&lt;/li&gt;
&lt;li&gt;BFS에서 시간 복잡도와 공간 복잡도를 최적화하기 위해서 visited.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;전역 변수들&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int n, m;
bool board[102][102], visited[102][102];
int costBoard[102][102];
bool test[102][102];
int dy[] = {1, -1, 0, 0}, dx[] = {0, 0, 1, -1};
int result = 0, cnt = 0;
queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q, air;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;N, M
&lt;ul&gt;
&lt;li&gt;입력으로 주어지는 n*m 크기의 치즈가 들어온다고 알려주는 2개의 크기 값.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;board
&lt;ul&gt;
&lt;li&gt;board[y][x]값이 true이면 치즈, false이면 공기라는 것을 저장하는 2차원 배열&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;visited
&lt;ul&gt;
&lt;li&gt;visited[y][x]가 방문했었는지의 정보를 저장하고 있는 2차원 배열&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;costBoard
&lt;ul&gt;
&lt;li&gt;costBoard[y][x]는 바깥 공기와 몇 개의 변이 닿아있는지를 저장하는 2차원 배열&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;dy, dx
&lt;ul&gt;
&lt;li&gt;동서남북으로 확산되는 것을 for문으로 간단히 처리하기 위해서 선언한 배열&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;result
&lt;ul&gt;
&lt;li&gt;현재 몇 시간이 지났는지 저장하는 변수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;cnt
&lt;ul&gt;
&lt;li&gt;현재 녹지않은 치즈의 개수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Q
&lt;ul&gt;
&lt;li&gt;&quot;한&quot; 시간에 녹아내릴 치즈를 알아내는 Queue&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;air
&lt;ul&gt;
&lt;li&gt;공기들이 순차적으로 들어와 순회하면서 치즈를 녹일 수 있도록하는 Queue
&amp;lt;/details&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;전역 변수들을 선언하고 이제 main으로 들어가보자&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define fast_io ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);

int main(){
    fast_io
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
  
    
    int tmp;
    
    
    for(int i = 1; i &amp;lt;= n; i++){
        
        for(int j = 1; j &amp;lt;= m; j++){
            cin &amp;gt;&amp;gt; tmp;
            board[i][j] = tmp;
            if(board[i][j]) cnt++;
        }
        if(!board[i][1]) air.push({i, 1});
        if(!board[i][m]) air.push({i, n});
    }
    for(int i = 1; i &amp;lt;= m; i++){
        if(!board[1][i]) air.push({1, i});
        if(!board[n][i]) air.push({n, i});
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;작성된 main의 일부분입니다.&lt;/p&gt;
&lt;p&gt;이름부터 알겠지만 fast_io는 빠른 입출력을 해주는 친구들이구요!(C 언어 사용자 분들이 간혹 위에 cin.tie(0)을 쓰시고 scanf를 쓰는 경우를 봤는데요. 그러시면 안됩니다. cin.tie(0)을 사용하시면 cin만 사용해야 됩니다.)&lt;/p&gt;
&lt;p&gt;아래부터 입력을 받습니다.&lt;/p&gt;
&lt;p&gt;n, m을 먼저 입력받고 n*m만큼 돌면서 치즈를 입력으로 다 받아옵니다.&lt;/p&gt;
&lt;p&gt;여기서 &quot;모눈종이의 맨 가장자리에는 치즈가 놓이지 않는 것으로 가정&quot;이 조건을 이용해서 저는 맨 끝의 공기들을 전부 air에 넣어서 탐색을 진행하겠습니다.&lt;/p&gt;
&lt;p&gt;if문으로 처리를 해놓은 것은 제가 처음에 저 조건을 못 보고서 모두 처리를 진행했기에 남아있는 겁니당..(index가 1부터 시작하는 것도 그 때문)&lt;/p&gt;
&lt;p&gt;이제 주요코드인 BFS 코드를 살펴봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while(!air.empty() &amp;amp;&amp;amp; cnt &amp;gt; 0){
        int length = (int) air.size();
        
        for(int i = 0; i &amp;lt; length &amp;amp;&amp;amp; cnt &amp;gt; 0; i++){
            
            pair&amp;lt;int, int&amp;gt; pos = air.front(); air.pop();
            if(visited[pos.first][pos.second]) continue;
            q.push(pos);
            board[pos.first][pos.second] = false;
            while(!q.empty() &amp;amp;&amp;amp; cnt &amp;gt; 0){
                
                pos = q.front(); q.pop();
                // cout &amp;lt;&amp;lt; pos.first &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; pos.second &amp;lt;&amp;lt; &quot;\n&quot;;
                if(visited[pos.first][pos.second]) continue;
                if(board[pos.first][pos.second]){
                    costBoard[pos.first][pos.second]++;
                    if(costBoard[pos.first][pos.second] &amp;gt; 1){
                        cnt--;
                        costBoard[pos.first][pos.second] = -MAX;
                        air.push(pos);
                    }
                    continue;
                }
                test[pos.first][pos.second] = true;

                visited[pos.first][pos.second] = true;
                
                
                for(int next = 0; next &amp;lt; 4; next++){
                    int ny = dy[next] + pos.first;
                    int nx = dx[next] + pos.second;
                    
                    if(0 &amp;lt; ny &amp;amp;&amp;amp; ny &amp;lt;= n &amp;amp;&amp;amp; 0 &amp;lt; nx &amp;amp;&amp;amp; nx &amp;lt;= m &amp;amp;&amp;amp; !visited[ny][nx]) q.push({ny, nx});
                    
                }
                

            }
            

        }

        result++;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;코드에서는 먼저 air(바깥 공기들)가 비어있지 않은 상태에서 현재 알고있는 바깥 공기들을 전부 for문을 돌면서 방문하지 않았던 공기를 하나씩 가져옵니다. 그 가져온 공기에서 BFS를 진행시켜서 확산을 통해서 바깥 공기와 접촉한 치즈들을 접촉한 수 만큼 costBoard[치즈의 y 위치][치즈의 x 위치]++을 해줌으로써 닿아있는 변이 하나 더 있다는 것을 세어줍니다. 한번 방문한 공기는 방문처리를 통해서 다시 방문하지 않도록하여, 시간 및 공간을 최적화하였습니다.&lt;/p&gt;
&lt;p&gt;그러다 2 변 이상이 한 치즈와 접촉하고 있다면 그 치즈를 다음 시간에는 녹아 없어질 것으로 확정짓고, air에 위치를 넣은 후 다시 재탐색하는 방법으로 접근하였습니다.&lt;/p&gt;
&lt;p&gt;이 방법으로 result를 카운트하면 저희가 원하는 &quot;주어진 치즈가 모두 녹아 없어지는데 걸리는 정확한 시간&quot;을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;(새벽 감성 코딩이라 좀 이상합니다..)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int main(){
    fast_io
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
  
    
    int tmp;
    
    
    for(int i = 1; i &amp;lt;= n; i++){
        
        for(int j = 1; j &amp;lt;= m; j++){
            cin &amp;gt;&amp;gt; tmp;
            board[i][j] = tmp;
            if(board[i][j]) cnt++;
        }
        if(!board[i][1]) air.push({i, 1});
        if(!board[i][m]) air.push({i, n});
    }
    for(int i = 1; i &amp;lt;= m; i++){
        if(!board[1][i]) air.push({1, i});
        if(!board[n][i]) air.push({n, i});
    }
    
    
    // cout &amp;lt;&amp;lt; cnt &amp;lt;&amp;lt; &quot;\n&quot;;
    
    while(!air.empty() &amp;amp;&amp;amp; cnt &amp;gt; 0){
        int length = (int) air.size();
        
        for(int i = 0; i &amp;lt; length &amp;amp;&amp;amp; cnt &amp;gt; 0; i++){
            
            pair&amp;lt;int, int&amp;gt; pos = air.front(); air.pop();
            if(visited[pos.first][pos.second]) continue;
            q.push(pos);
            board[pos.first][pos.second] = false;
            while(!q.empty() &amp;amp;&amp;amp; cnt &amp;gt; 0){
                
                pos = q.front(); q.pop();
                // cout &amp;lt;&amp;lt; pos.first &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; pos.second &amp;lt;&amp;lt; &quot;\n&quot;;
                if(visited[pos.first][pos.second]) continue;
                if(board[pos.first][pos.second]){
                    costBoard[pos.first][pos.second]++;
                    if(costBoard[pos.first][pos.second] &amp;gt; 1){
                        cnt--;
                        costBoard[pos.first][pos.second] = -MAX;
                        air.push(pos);
                    }
                    continue;
                }
                test[pos.first][pos.second] = true;

                visited[pos.first][pos.second] = true;
                
                
                for(int next = 0; next &amp;lt; 4; next++){
                    int ny = dy[next] + pos.first;
                    int nx = dx[next] + pos.second;
                    
                    if(0 &amp;lt; ny &amp;amp;&amp;amp; ny &amp;lt;= n &amp;amp;&amp;amp; 0 &amp;lt; nx &amp;amp;&amp;amp; nx &amp;lt;= m &amp;amp;&amp;amp; !visited[ny][nx]) q.push({ny, nx});
                    
                }
                

            }
            

        }

        result++;
    }
    
    cout &amp;lt;&amp;lt; result;
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 십자카드 문제</title><link>https://compy07.github.io/Blog/posts/boj/2659/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2659/</guid><description>돌려돌려!</description><pubDate>Tue, 04 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2659&quot;&gt;십자카드 문제&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= E_i &amp;lt;= 10)&lt;/td&gt;
&lt;td&gt;BruteForce&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./first.png&quot; alt=&quot;3227&quot; /&gt;
&amp;lt;/center&amp;gt;
위와 같은 십자모양의 한 장의 카드에서, 네 모서리에 1 이상 9 이하의 숫자가 하나씩 씌여 있다. 이 네 개의 숫자 중에는 같은 숫자도 있을 수 있다.&lt;/p&gt;
&lt;p&gt;모든 가능한 십자 카드가 주어질 때, 각각의 카드는 다음과 같은 &apos;시계수&apos;라는 번호를 가진다. 시계수는 카드의 숫자들을 시계 방향으로 읽어서 만들어지는 네 자리 수들 중에서 가장 작은 수이다. 위 그림의 카드는 시계방향으로 3227, 2273, 2732, 7322로 읽을 수 있으므로, 이 카드의 시계수는 가장 작은 수인 2273이다.&lt;/p&gt;
&lt;p&gt;입력으로 주어진 카드의 시계수를 계산하여, 그 시계수가 모든 시계수들 중에서 몇 번째로 작은 시계수인지를 알아내는 프로그램을 작성하시오.&lt;/p&gt;
&lt;p&gt;예를 들어서, 다음과 같은 십자 카드의 시계수는 1122이며, 이 시계수보다 작은 시계수들은 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119 뿐이므로 1122는 10번째로 작은 시계수다. (여기서 십자카드는 0 이 나타날 수 없으므로 1120은 시계수가 될 수 없다. 또한 1121 이 적혀있는 카드의 시계수는 1112이므로, 1121은 시계수가 될 수 없다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./second.png&quot; alt=&quot;2112&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;일단 시간보고, 범위 봤더니 아 브포구나 알았구요.. 문제가 돌린다는게 정확히 뭘 구하라는건지 좀 생각을 해봤더니 그렇게 막 꼬아놓은 문제는 아니더군요!&lt;/p&gt;
&lt;p&gt;그래서 그냥 브포로 구했습니다.. 처음에 코드를 쓰면서 아.. 더 효율적인 방법이 없나 하면서 풀었네요 허허&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;참 많이도 돌아갔네요 진짜.. 어휴 코드가 왜 이래.. 진짜 ㅠㅜ&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;


void swap(int* a, int* b){
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int dice[4];

bool visited[10][10][10][10];

bool check(int value){
    int current[] = {value / 1000, value / 100 % 10, value % 100 / 10, value % 10};
    if(!(current[0] &amp;amp;&amp;amp; current[1] &amp;amp;&amp;amp; current[2] &amp;amp;&amp;amp; current[3])) return true;
    bool flag = false;
    for(int i = 0; i &amp;lt; 4; i++)
        if(visited[current[i]][current[(i+1)%4]][current[(i+2)%4]][current[(i+3)%4]]) flag = true;
    return flag;
}
int main() {
    for(int i = 0; i &amp;lt; 4; i++) cin &amp;gt;&amp;gt; dice[i];    
    int current = 10000000000;
    for(int i = 0; i &amp;lt; 4; i++){
        int currentValue = 0;
        for(int j = 0; j &amp;lt; 4; j++) {
            currentValue*=10;
            currentValue+=dice[j];
        }
        current = min(current, currentValue);

        int first = dice[0];
        for(int j = 0; j &amp;lt; 3; j++) swap(dice[j], dice[j+1]);   
        dice[3] = first;
    }
    
    int result = 1;
    for(int i = 1111; i &amp;lt; current; i++){
        if(check(i)) continue;        
        visited[i / 1000][i / 100 % 10][i % 100 / 10][i % 10] = true;
        result++;
    }
    cout &amp;lt;&amp;lt; result;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] Cowties</title><link>https://compy07.github.io/Blog/posts/boj/27061/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/27061/</guid><description>재활 12일차</description><pubDate>Mon, 06 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/27061&quot;&gt;Cowties&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(3 ≤ N ≤ 100)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 대놓고 dp 문제 준거여서 그대로 읽고 그대로 풀면 되었씁니다. 약간 조심할거는...&lt;/p&gt;
&lt;p&gt;&quot;처음 시작했던 점이랑 이어져야한다는 것&quot; 정도 겠네요.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = unsigned long long;
using ld = long double;
//ll MOD = 1&apos;000&apos;000&apos;007;
//ll MOD = 1.8446744074E19;

int N;

// 그냥 dp로 풀면 될걱 ㅏㅌ긴한ㄷ
//ld dp[101][
// 그니까 i번째 cow를 보고 잇느 상황에서.. j번째 위치로 선택했ㅇ르 때 최소값을 얻어가면 되는거자너

// dp[i][j]를 i번째 cow의 j번째 position으로 확정할 때 그 전까지의 loop length 최솟값으로 정하자. // 이거 길이가 분수가 나오니까 double 로 하고 마지막에만 100 곱하기로

// 이게 그냥 간단히 끝나는건 아니였구만..

ld dp[101][101];

vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; cows[101];

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    ld a = LLONG_MAX;
    
    
    cin &amp;gt;&amp;gt; N;
    
    for(int i = 0; i &amp;lt; N; i++){
        int tmp;
        cin &amp;gt;&amp;gt; tmp;
        
        for(int j = 0; j &amp;lt; tmp; j++){
            int y, x;
            cin &amp;gt;&amp;gt; y &amp;gt;&amp;gt; x;
            
            cows[i].push_back({y, x});
        }
    }
    
    
    ld result = LLONG_MAX;
    for(int first = 0; first &amp;lt; cows[0].size(); first++){
        
        for(int i = 1; i &amp;lt; N; i++){
            for(int j = 0; j &amp;lt; cows[i].size(); j++){
                dp[i][j] = LLONG_MAX;
                if(i == 1){
                    auto [cy, cx] = cows[i][j];
                    auto [py, px] = cows[0][first];
                    
                    ld distance = sqrtl((cy - py) * (cy - py) + (cx - px) * (cx - px));
                    
                    dp[i][j] = distance;
                }
            }
        }
        
        
        
        for(int idx = 2; idx &amp;lt; N; idx++){
            for(int i = 0; i &amp;lt; cows[idx].size(); i++){
                for(int j = 0; j &amp;lt; cows[idx-1].size(); j++){
                    ld pre_cost = dp[idx-1][j];
                    
                    auto [cy, cx] = cows[idx][i];
                    auto [py, px] = cows[idx-1][j];
                    
                    ld distance = sqrtl((cy - py) * (cy - py) + (cx - px) * (cx - px));
                    
                    dp[idx][i] = min(dp[idx][i], pre_cost + distance);
                }
            }
        }
        
        for(int i = 0; i &amp;lt; cows[N-1].size(); i++){
            ld current = dp[N-1][i];
            
            auto [cy, cx] = cows[N-1][i];
            auto [py, px] = cows[0][first];
            ld distance = sqrtl((cy - py) * (cy - py) + (cx - px) * (cx - px));
            
            result = min(result, current + distance);
        }
    }
    
    
    
    cout &amp;lt;&amp;lt; (ll)(result * 100);

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 수열 재배열</title><link>https://compy07.github.io/Blog/posts/boj/27739/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/27739/</guid><description>재활 4일차</description><pubDate>Thu, 06 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/27739&quot;&gt;수열 재배열&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;(3 ≤ N ≤ 2000, 2 ≤ K ≤ N)&lt;/td&gt;
&lt;td&gt;BruteForce&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;내가 연속된 구간 k개를 고르고 앞만 이어진다. 뒤만 이어진다. 앞뒤만 이어진다. 아니면 나만 보겠다... 이것만 고려해서 나눠서 풀면 되겠스비다.&lt;/p&gt;
&lt;p&gt;이것도 학교에서 풀어서 영상은 없네용&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include&amp;lt;set&amp;gt;
using namespace std;
int inf = 1e9;


int solution(int n, int k, vector&amp;lt;int&amp;gt;&amp;amp; nums){
    
    
    // 그냥 나뒀을 때.. 는 이미 정렬할 대 되는거니까ㅏ상관 x
    // 내가 장렬됨에 따라서 뒤와 앞이 이어지는경우
    // 내가 정렬됨에 따라서 앞에를 버리고 뒤랑만 이어지는 경우
    // 내가 정렬됨에 따라서 뒤를 버리고 앞만 이어지는경우
    
    // 중복된거를 (length - 중복 개수)
    
    vector&amp;lt;int&amp;gt; origin(n), reverse(n);
    
    
    int result = 1;
    
    
    
    origin[0] = 1;
    reverse[n-1] = 1;
    
    for(int i = 1; i &amp;lt; n; i++){
        if(nums[i] &amp;lt;= nums[i-1]){
            
            origin[i] = 1;
        }else{
            origin[i] = origin[i-1]+1;
            result = max(origin[i], result);
        }
    }
    
    
    
    
    for(int i = n-2; i &amp;gt; -1; i--){
        if(nums[i+1] &amp;lt;= nums[i]) reverse[i] = 1;
        else reverse[i] = reverse[i+1]+1;
        
    }
    

    
    
    set&amp;lt;int&amp;gt; current_set;
    
    int start, end;
    for(int i = 0; i &amp;lt;= n-k; i++){
        current_set.clear();
        
        for(int j = 0; j &amp;lt; k; j++) current_set.insert(nums[j+i]);
        
        vector&amp;lt;int&amp;gt; current(current_set.begin(), current_set.end()); // 보통 정렬돼서 나오는 것처럼 보임..
        sort(current.begin(), current.end()); // 그래도 불안해
        
        
        // 앞뒤 이어지는 경우
        if(current.size() == k &amp;amp;&amp;amp; i &amp;gt; 0 &amp;amp;&amp;amp; i &amp;lt; n-k){
            start = 0;
            end = current.size()-1;
            if(current[start] &amp;gt; nums[i-1] &amp;amp;&amp;amp; current[end] &amp;lt; nums[i+k]) result = max(result, end - start + 1 + origin[i-1] + reverse[i+k]);
        }

        // 앞만 이어지는 경우 | 앞 &amp;lt;- current -&amp;gt; 뒤
        if(i &amp;gt; 0){
            start = 0;
            while(start &amp;lt; current.size()-1 &amp;amp;&amp;amp; current[start] &amp;lt;= nums[i-1]) start++;
            
            
            if(current[start] &amp;gt; nums[i-1]) result = max(result, origin[i-1] + (int) current.size() - start);
        }

        // 뒤만 이어지는 경우
        if(i &amp;lt; n-k){
            end = current.size()-1;
            while(0 &amp;lt; end &amp;amp;&amp;amp; current[end] &amp;gt;= nums[i+k]) end--;
            
            if(current[end] &amp;lt; nums[i+k]) result = max(result, reverse[i+k] + end + 1);
        }

        result = max(result, (int)current_set.size());

        
    }
    
    return result;
}


int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    int answer;
    int n, k;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
    
    
    vector&amp;lt;int&amp;gt; nums(n);
    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; nums[i];
    
    int result = solution(n, k, nums);
    cout &amp;lt;&amp;lt; result;
    /*
     10 3
     1 8 2 4 8 5 1 8 9 3
     5 4
     
     */
    return 0;
}




&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 괄호 제거</title><link>https://compy07.github.io/Blog/posts/boj/2800/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2800/</guid><description>재활 1일차</description><pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2800&quot;&gt;괄호 제거&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 &amp;lt;= 괄호 개수 &amp;lt;= 10)&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;오랜만에 문제를 풀기 때문에 가벼운 문제 입니다. ㅠ&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = long long;
using ld = long double;
ll MOD = 1&apos;000&apos;000&apos;000;

set&amp;lt;string&amp;gt; results;

string input;



set&amp;lt;int&amp;gt; visited;



void solution(int left){
    if(left &amp;gt;= input.size()) return;
    
    if(input[left] == &apos;(&apos;){
        int right_idx = input.size()-1;
        visited.insert(left);
        int cnt = 1;
        for(int i = left+1; i &amp;lt; input.size(); i++){
            if(input[i] == &apos;)&apos;) cnt--;
            else if(input[i] == &apos;(&apos;) cnt++;
        
            if(cnt == 0){
                right_idx = i;
                break;
            }
        }
        visited.insert(right_idx);
        solution(left+1);
        
        
        string result = &quot;&quot;;
        for(int i = 0; i &amp;lt; input.size(); i++){
            if(visited.find(i) == visited.end())
                result += input[i];
        }
        results.insert(result);
        
        visited.erase(left);
        visited.erase(right_idx);
        
        
        solution(left+1);
    }
    else{
        solution(left+1);
    }
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; input;
    
    
    solution(0);
    
    
    
    
    
    for(string result : results) cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
   
        
        
    
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 자리 전쟁</title><link>https://compy07.github.io/Blog/posts/boj/2886/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/2886/</guid><description>어흑.. 2</description><pubDate>Sat, 29 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2886&quot;&gt;자리 전쟁&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ R, C ≤ 100)&lt;/td&gt;
&lt;td&gt;Greedy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;일단 생각보다 고려할게 많았던 문제였습니다.&lt;/p&gt;
&lt;p&gt;한 관점에서만 가까운 것을 고르는 것은 문제가 있고, 전부 고려해서 제일 작은 것들을 미리 처리하고를
반복해서 없애는 방식을 채택했고, 처음에는 이거를 그냥 단순 visited 처리로 for문 돌렸는데 O((r*c)^3) 이정도 나오는 것을 보고서 음.. 안되겠군 하고서
그냥 처음에 생각했던 방법으로 접근했습니다. 그런데 이 과정에서 굉장히 많은 오류가 있고,,... 그냥 하던 코드 유지해야지..
이러다가 시간을 날려 먹은 것 같네요.. 여러분ㄴ들은 꼭 아닌거 같다 싶으면 다시 짜보시길...&lt;/p&gt;
&lt;p&gt;그러면 보통 금방 풀리더라구요&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;tuple&amp;gt;
using namespace std;
using ll = long long;
int r, c;

char board[101][101];
bool peopleVisited[10001];
int chairCount[10001];

ll getMinDistance(pair&amp;lt;int, int&amp;gt; a, pair&amp;lt;int, int&amp;gt; b){
    return (a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second);
}

void calculateWarfield(const vector&amp;lt;int&amp;gt;&amp;amp; chairIndices, int&amp;amp; result) {
    for(int idx : chairIndices) {
        chairCount[idx]++;
        if(chairCount[idx] == 2) {
            result++;
        }
    }
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; r &amp;gt;&amp;gt; c;
    
    vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; chairs, people;
    for(int i = 0; i &amp;lt; r; i++){
        for(int j = 0; j &amp;lt; c; j++){
            cin &amp;gt;&amp;gt; board[i][j];
            if(board[i][j] == &apos;X&apos;) people.push_back({i, j});
            else if(board[i][j] == &apos;L&apos;) chairs.push_back({i, j});
        }
    }

    priority_queue&amp;lt;tuple&amp;lt;ll, int, int&amp;gt;, vector&amp;lt;tuple&amp;lt;ll, int, int&amp;gt;&amp;gt;, greater&amp;lt;tuple&amp;lt;ll, int, int&amp;gt;&amp;gt;&amp;gt; pq;
    
    for(int i = 0; i &amp;lt; people.size(); i++){
        for(int j = 0; j &amp;lt; chairs.size(); j++){
            ll dist = getMinDistance(people[i], chairs[j]);
            pq.push({dist, i, j});
        }
    }

    int result = 0;
    vector&amp;lt;int&amp;gt; currentChairIndices;
    ll currentDist = -1;

    while(!pq.empty()){
        auto [dist, personIdx, chairIdx] = pq.top();
        pq.pop();
        
        if(dist != currentDist){
            if(!currentChairIndices.empty()){
                calculateWarfield(currentChairIndices, result);
                currentChairIndices.clear();
            }
            currentDist = dist;
        }
        
        if(peopleVisited[personIdx] || chairCount[chairIdx] &amp;gt; 0) continue;
        
        currentChairIndices.push_back(chairIdx);
        peopleVisited[personIdx] = true;
    }
    
    if(!currentChairIndices.empty()) calculateWarfield(currentChairIndices, result);
    
    
    cout &amp;lt;&amp;lt; result;
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] DDR 체력 관리</title><link>https://compy07.github.io/Blog/posts/boj/29756/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/29756/</guid><description>재활 1일차</description><pubDate>Mon, 16 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/29756&quot;&gt;DDR 체력 관리&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 1000, 1 ≤ K ≤ 10, 1 ≤ sᵢ ≤ 1000, 1 ≤ hᵢ ≤ 100&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이것도 간단한 dp 문제로 그냥 dp[i][j]가 있을 때 i번째 게임을 할 때, j번째 체력인 경우로 나눠서 정의하고
상태 전이를 해버리면 쉽게 풀리는데 문제가 원하는 순서와 조건에 잘 맞춰야되기 때문에 이상한 곳에서 틀릴 수 있으니 조심하십셔.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
    #include &amp;lt;iostream&amp;gt;
    #include &amp;lt;queue&amp;gt;
    #include &amp;lt;set&amp;gt;
    #include &amp;lt;algorithm&amp;gt;
    #include &amp;lt;random&amp;gt;

    #define mod 1000000007LL
    #define MAX 1&apos;000&apos;000LL
    using namespace std;
    using ll = long long;

    ll n;

    ll dp[2001][101]; // dp[i][j] 일 때, i번째 game에서 hp가 j일 때의 점수
    ll uk;
    pair&amp;lt;ll, ll&amp;gt; games[2001];

    int main(){
        cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; uk;
        ll result = 0;
        
        for(int i = 1; i &amp;lt;= n; i++) cin &amp;gt;&amp;gt; games[i].first; // score
        for(int i = 1; i &amp;lt;= n; i++){
            cin &amp;gt;&amp;gt; games[i].second; // health
            
            
        }
        for(int i = 0; i &amp;lt;= n; i++){
            for(int j = 0; j &amp;lt; 101; j++){
                dp[i][j] = -1;
            }
        }
        
        
        
        
    //
    //    for(int i = 1; i &amp;lt; n; i++){
    //        for(int j = i+1; j &amp;lt;= n; j++){
    //            for(int k = 1; k &amp;lt; 101; k++){
    //
    //                dp[j][min(k+(uk* (j - i)), 100LL)] = max(dp[i][k], dp[j][min(k+(uk* (j - i)), 100LL)]);
    //                if(!dp[i][k] || k+(uk* (j - i)) - games[j].second &amp;lt; 0) continue;
    //                ll ch = min(100LL, k+(uk* (j - i)) - games[j].second);
    //                dp[j][ch] = max(dp[i][k] + games[j].first, dp[j][ch]);
    //                result= max(dp[j][ch], result);
    //            }
    //        }
    //    }
        dp[0][100] = 0;
        for(int i = 0; i &amp;lt; n; i++){
            for(int j = 0; j &amp;lt;= 100; j++){
                if(dp[i][j] == -1) continue;
                ll ch = min(j+uk, 100LL);
                dp[i+1][ch] = max(dp[i+1][ch], dp[i][j]);
                if(ch - games[i+1].second &amp;gt;= 0){
                    dp[i+1][ch - games[i+1].second] = max(dp[i+1][ch - games[i+1].second], dp[i][j] + games[i+1].first);
                    result = max(result, dp[i+1][ch - games[i+1].second]);
                }
//                cout &amp;lt;&amp;lt; dp[i+1][ch] &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; ch &amp;lt;&amp;lt; &quot;  &quot; &amp;lt;&amp;lt; i+1 &amp;lt;&amp;lt; &quot;\n&quot;;
                result = max(result, dp[i+1][min(j+uk, 100LL)]);
            }
        }

        cout &amp;lt;&amp;lt; result;
        
        return 0;
    }
    /*
     2 0
     1 2
     99 1
     
     1 5
     1
     100
     */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 오아시스 재결합</title><link>https://compy07.github.io/Blog/posts/boj/3015/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/3015/</guid><description>재활 18일차</description><pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3015&quot;&gt;오아시스 재결합&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 500,000)&lt;/td&gt;
&lt;td&gt;Stack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이 문제는 저번에 얘기했던 모노톤 스택을 좀 더 활용한 문제로 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;저는 같은 값들을 가진 값들에 대해서 압축시키는 형식으로 처리를 했는데요. 더욱 모노톤 스택에 가깝게 풀이를 하려면
Binary search를 활용하는 방법도 가능할 것 같습니다.&lt;/p&gt;
&lt;p&gt;단조성을 활용하다 보니 자연스럽게 정렬이 되구요. 이를 이용해서 이분 탐색을 돌릴 수 있고, 그러면 시간 안에 무조건 돌아가도록 만들 수 있습니다.&lt;/p&gt;
&lt;p&gt;하튼 이문제도 여러 방법으로 풀 수 있기도 하고, 좋은 문제인 것 같습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;
using ll = long long;

vector&amp;lt;int&amp;gt; s;
vector&amp;lt;int&amp;gt; cnt;
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    ll result = 0;
    
    int n;
    cin &amp;gt;&amp;gt; n;
    
    int current;
    while(n--&amp;gt;0){
        cin &amp;gt;&amp;gt; current;
        
        if(s.empty()){
            s.push_back(current);
            cnt.push_back(1);
            continue;
        }
        
        
        int p = 1;
        if(s.back() &amp;lt; current){
            
            while(!s.empty() &amp;amp;&amp;amp; s.back() &amp;lt; current){
                s.pop_back();
                
                p+=cnt.back();
                cnt.pop_back();
            }
            p -= s.empty();
        }
        
        if(!s.empty() &amp;amp;&amp;amp; s.back() == current){
            p+= cnt.back() - (cnt.size() == 1);
             
            cnt.back()++;
            s.pop_back();
        }else{
            cnt.push_back(1);
        }
        s.push_back(current);
        result += p;
        

    }

    cout &amp;lt;&amp;lt; result;

    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] ChatGPT의 역작</title><link>https://compy07.github.io/Blog/posts/boj/30871/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/30871/</guid><description>재활 8일차</description><pubDate>Thu, 02 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/30871&quot;&gt;ChatGPT의 역작&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 200,000, 1 ≤ L_i, R_i ≤ 10^18)&lt;/td&gt;
&lt;td&gt;Parametric Search, Binary Search&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;문제가 그냥 엄청 어렵게 느껴졌습니다. 처음에는 함수를 분석해서 해보려고 했는데, 들어오는 배열 L, R에 대해서 그냥 엄청 바뀌어버리고 뭔 규칙도 없고
사실상 다른 방법으로 풀어야하는데 범위 때문에 이분탐색을 돌려볼까? 생각을 하게 되었어요. 사실 단조성 자체가 없어서 이게 왜 될까 라는 생각 때문에 코드도 못 짜고 있어서,
일단 짜고 내본다음에 다른 방법 생각하자 해서 작성해서 냈더니? %가 많이 올라가더라구요? 그래서 다행히 풀었습니다.&lt;/p&gt;
&lt;p&gt;나중에 아이디어 보니까 뭐 제 코드에서도 그렇게 작성하긴 했지만, 무조건 성립하는 뭔가가 있답니다.
허허 신기하네요...&lt;/p&gt;
&lt;p&gt;조금 더 설명을 정확히 해보면, 함수에서 x=0일 때는 무조건 False, x=10^18+1은 무조건 True가 나오기 때문에
그 사이에 값은 무조건 교차할 것이다 생각하고 풀어야하는..
하튼 디지게 어려웠던 문제였습니다. 발상 자체가 힘들었어요..&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
using namespace std;
using ll = unsigned long long;
//ll MOD = 1&apos;000&apos;000&apos;007;
ll MOD = 1.8446744074E19;

ll N;

ll L[200&apos;001], R[200&apos;001];


bool func(ll x){
    ll value = x;
    for(int i = 0; i &amp;lt; N; i++){
        ll l = L[i];
        ll r = R[i];
        if(l &amp;lt;= x &amp;amp;&amp;amp; x &amp;lt;= r) value^=(((x|l)+(x&amp;amp;r)*(l^r)) % MOD);
    }
    return (value &amp;gt;= 0x0123456789ABCDEF);
}

void solution(){
    ll left = 0;
    ll right = 1E18;
    
    bool left_value = func(left);
    bool right_value = func(right);
    
    while(left &amp;lt;= right){
        ll pivot = (left + right) / 2;
        
        bool pivot_value = func(pivot);
        
        if(!pivot_value &amp;amp;&amp;amp; func(pivot + 1)){
            cout &amp;lt;&amp;lt; pivot;
            return;
        }
        
        
        if(!left_value &amp;amp;&amp;amp; pivot_value){
            right = pivot;
            right_value = pivot_value;
        }else if(!pivot_value &amp;amp;&amp;amp; right_value){
            left=pivot;
            left_value = pivot_value;
        }else{
            left=pivot+1;
            left_value = func(pivot+1);
        }
    
    }
    
    cout &amp;lt;&amp;lt; -1;
    return;
}

int main() {
    
    cin &amp;gt;&amp;gt; N;
    for(int i = 0; i &amp;lt; N; i++) cin &amp;gt;&amp;gt; L[i];
    for(int i = 0; i &amp;lt; N; i++) cin &amp;gt;&amp;gt; R[i];
    
    solution();
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 사격</title><link>https://compy07.github.io/Blog/posts/boj/31264/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/31264/</guid><description>이진탐색, 매개변수탐색 레츠꼬</description><pubDate>Fri, 04 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/31264&quot;&gt;사격&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;=N, M, Sᵢ &amp;lt;= 10⁵,1&amp;lt;=A&amp;lt;=10¹⁰)&lt;/td&gt;
&lt;td&gt;binary search&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;성현이네 부대의 24-1분기 사격 훈련이 시작되었다!
성현이네 부대의 사격 훈련장에는
$N$개의 표적이 있으며, 그중
$i$번째 표적의 점수는
$s_i$이다. 점수가
$s_i$인 표적을 맞히기 위해선 사격 실력이 최소
$s_i$ 이상이어야 한다. 만약 해당 표적을 성공적으로 맞혔다면, 표적의 점수만큼 사격 점수를 획득하며 사격 실력도 동일한 만큼 증가한다.
이번 사격 훈련에서는 최대
$M$번 사격할 수 있으며, 성현이가 진급에 성공하기 위해서는 획득한 사격 점수의 총합이
$A$점 이상이어야 한다.
사격 훈련을 시작하기 전 초기 사격 점수는
$0$점이다. 성현이는 항상 자신이 맞힐 수 있는 표적 중 얻을 수 있는 사격 점수가 가장 높은 표적을 맞히며, 동일한 표적을 여러 번 맞히는 것도 가능하다.
진급이 절실했던 성현이는 사격 훈련이 시작하기 전 초기 사격 실력이 어느 정도여야 진급에 성공할 수 있을지 궁금해하고 있다. 성현이를 위해, 진급에 성공하기 위한 초기 사격 실력의 최솟값을 구해주자. 항상 진급에 성공할 수 있는 경우만 입력으로 주어진다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;우리는 문제를 보는 순간 아! 라고 외치지 않을까 한다.&lt;/p&gt;
&lt;p&gt;느므느므 큰 범위를 보고, 값을 찾는 문제여서 바로 이분탐색으로 접근하였다.&lt;/p&gt;
&lt;p&gt;(나중에 더욱 자세히 쓸 예정...)&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MAX 2&apos;100&apos;000&apos;000
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;stack&amp;gt;
#include &amp;lt;math.h&amp;gt;
#include &amp;lt;random&amp;gt;
using namespace std;

using ull = unsigned long long;
using ll = long long;
int score[100001];
ull n, m, a;
bool visited[100001];
bool debug_ = false;
ull current_ = 0;
ull binary_search_(ull current, int depth){
    if(depth &amp;gt;= m|| current &amp;gt;= a) return current;
    int left = 0, right = n-1;
    current += current_;
    int val = -1;

    while(left &amp;lt; right ){
        int pivot = (left+ right) / 2;
        if(score[pivot] &amp;lt; current){
            if(pivot+1 &amp;lt; n &amp;amp;&amp;amp; score[pivot+1] &amp;gt; current){
                val = score[pivot];
                break;
            }
            left = pivot+1;
            
            if(left &amp;lt; 0 || score[left] &amp;gt; current) continue;
            val = max(val, score[left]);
        }
        else if(score[pivot] &amp;gt; current){
            right = pivot - 1;
            if(right &amp;lt; 0|| score[right] &amp;gt; current) continue;
            val = max(val, score[right]);
        }
        else {val = score[pivot]; break;}

    }


    if(val == -1) val = score[left];
    if(val &amp;gt; current) return 0;
    current-=current_;
    ull result = binary_search_(current + val, depth+1);
    return result;
}

int main(){
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; a;
    int limit = 0;
    for(int i = 0; i &amp;lt; n; i++){ cin &amp;gt;&amp;gt; score[i]; limit = max(limit, score[i]);}
    sort(score, score+n);
    if(n == 1){
        cout &amp;lt;&amp;lt; score[0];
        return 0;
    }
    
    int left = 1, right = limit;
    ull result = MAX;
    while(left &amp;lt;= right){
        current_ = (left + right) / 2;
        ull tmp = binary_search_(0, 0);
        if(tmp &amp;gt;= a){
            result = min(current_, result);
            right = current_-1;
        }else{
            left = current_+1;
        }
    }
    cout &amp;lt;&amp;lt; result;
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 당근 게임</title><link>https://compy07.github.io/Blog/posts/boj/31434/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/31434/</guid><description>재활 6일차</description><pubDate>Sat, 08 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/31434&quot;&gt;당근 게임&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N, K ≤ 100, 1 ≤ A_i, B_i ≤ 50)&lt;/td&gt;
&lt;td&gt;DynamicProgramming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;일단 전형적인 dp문제고 아이디어도 기본적인 예제에서 많이 볼 수 있는 유형이여서 재밌게 풀었던 것 같습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include&amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
using ll = long long;

int inf = 1e9;
int n, k_;

int dp[101][5005]; // dp[i][j] -&amp;gt; i초 일대 j만큼의 s인 상태에서 가지고 있는 당근의 수

pair&amp;lt;int, int&amp;gt; upgrades[101]; // {i, j} i만큼 지불해서 +j 만큼 더 얻을 수 있음

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k_;
    
    
    for(int i = 1; i &amp;lt;= n; i++){
        int a, b;
        
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;
        upgrades[i] = {a, b};
    }

    for(int i = 0; i &amp;lt;= k_; i++){
        for(int j = 0; j &amp;lt; 5005; j++) dp[i][j] = -1;
    }
    
    
    dp[0][1] = 0;
    
    for(int i = 0; i &amp;lt; k_; i++){
        
        for(int j = 1; j &amp;lt; 5005; j++){
            
            if(dp[i][j] == -1) continue;
            
            for(int k = 1; k &amp;lt;= n; k++){
                auto [a, b] = upgrades[k];
                
                
                if(dp[i][j] &amp;lt; a) continue;
                
                dp[i+1][j+b] = max(dp[i][j] - a, dp[i+1][j+b]);
            }
            
            dp[i+1][j] = max(dp[i][j] + j, dp[i+1][j]);
        }
        
    }
    
    int result = 0;
    for(int j = 1; j &amp;lt; 5005; j++) result = max(dp[k_][j], result);
    
    cout &amp;lt;&amp;lt; result;
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 두 배</title><link>https://compy07.github.io/Blog/posts/boj/31963/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/31963/</guid><description>이진탐색 아니구 그냥 수학</description><pubDate>Sat, 12 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/31963&quot;&gt;두 배&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;=N &amp;lt;= 250000, 1 &amp;lt;= A_i &amp;lt;= 1000000)&lt;/td&gt;
&lt;td&gt;Math(수학)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&amp;gt;
&amp;lt;mtext&amp;gt;길이 &amp;lt;/mtext&amp;gt;
&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;
&amp;lt;mtext&amp;gt;인 양의 정수열 &amp;lt;/mtext&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;,&amp;lt;/mo&amp;gt;
&amp;lt;mo&amp;gt;…&amp;lt;/mo&amp;gt;
&amp;lt;mo&amp;gt;,&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mtext&amp;gt;이 주어진다. 이 수열을 오름차순으로 만들려 한다. 수열 &amp;lt;/mtext&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;,&amp;lt;/mo&amp;gt;
&amp;lt;mo&amp;gt;…&amp;lt;/mo&amp;gt;
&amp;lt;mo&amp;gt;,&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mtext&amp;gt;이 오름차순이라는 것은, &amp;lt;/mtext&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;mo&amp;gt;∀&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;
&amp;lt;mo&amp;gt;≤&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;≤&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;-&amp;lt;/mo&amp;gt;
&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;mtext&amp;gt;에 대해 &amp;lt;/mtext&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;≤&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mrow&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/mrow&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mtext&amp;gt;이라는 것이다.&amp;lt;/mtext&amp;gt;
&amp;lt;/math&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&amp;gt;
&amp;lt;mtext&amp;gt;수열 &amp;lt;/mtext&amp;gt;
&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;
&amp;lt;mtext&amp;gt;를 오름차순으로 만들기 위해, 수열 &amp;lt;/mtext&amp;gt;
&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;
&amp;lt;mtext&amp;gt;에 다음 연산을 몇 번이든 반복해서 적용할 수 있다.&amp;lt;/mtext&amp;gt;
&amp;lt;/math&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&amp;gt;
&amp;lt;mtext&amp;gt;• 어떤 &amp;lt;/mtext&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mtext&amp;gt; &amp;lt;/mtext&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;
&amp;lt;mo&amp;gt;≤&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;≤&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;mtext&amp;gt;에 대해 &amp;lt;/mtext&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mtext&amp;gt;에 2를 곱한다.&amp;lt;/mtext&amp;gt;
&amp;lt;/math&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&amp;gt;
&amp;lt;mtext&amp;gt;연산을 최소 횟수로 적용해서 &amp;lt;/mtext&amp;gt;
&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;
&amp;lt;mtext&amp;gt;를 오름차순으로 만들고 싶다. 이때, 최소 횟수를 구하라.&amp;lt;/mtext&amp;gt;
&amp;lt;/math&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;저는 처음 접근할 때, 범위를 제대로 보지 않고서 접근하다가 아예 이분탐색을 돌려버렸습니다. 그러다가 진짜 왜 틀린지 모르고 있다가 범위를 알고나서 부터
이분 탐색이라는 방법말고 다른 방법을 떠오르게 되었어요.&lt;/p&gt;
&lt;p&gt;이제 그 아이디어에 대해서 말해보겠습니다.&lt;/p&gt;
&lt;p&gt;일단 정의부터 시작하여 아이디어의 기초적인 기반을 다져봅시다.&amp;lt;br&amp;gt;
&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&amp;gt;
&amp;lt;mtext&amp;gt;Let  &amp;lt;/mtext&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;=&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;
&amp;lt;mtext&amp;gt;  and  &amp;lt;/mtext&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mrow&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/mrow&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;=&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;B&amp;lt;/mi&amp;gt;
&amp;lt;/math&amp;gt;
&amp;lt;br&amp;gt; -&amp;gt; then
&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;⋅&amp;lt;/mo&amp;gt;
&amp;lt;msup&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;mi&amp;gt;x&amp;lt;/mi&amp;gt;&amp;lt;/msup&amp;gt;
&amp;lt;mo&amp;gt;≤&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mrow&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/mrow&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;⋅&amp;lt;/mo&amp;gt;
&amp;lt;msup&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;mi&amp;gt;y&amp;lt;/mi&amp;gt;&amp;lt;/msup&amp;gt;
&amp;lt;/math&amp;gt;
&amp;lt;br&amp;gt;-&amp;gt;
&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&amp;gt;
&amp;lt;mfrac&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;⋅&amp;lt;/mo&amp;gt;
&amp;lt;msup&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;mi&amp;gt;x&amp;lt;/mi&amp;gt;&amp;lt;/msup&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mrow&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/mrow&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;/mfrac&amp;gt;
&amp;lt;mo&amp;gt;≤&amp;lt;/mo&amp;gt;
&amp;lt;msup&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;mi&amp;gt;y&amp;lt;/mi&amp;gt;&amp;lt;/msup&amp;gt;
&amp;lt;/math&amp;gt;
&amp;lt;br&amp;gt;-&amp;gt;
&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&amp;gt;
&amp;lt;mi&amp;gt;x&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;log&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;mo&amp;gt;-&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;log&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mrow&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/mrow&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;mo&amp;gt;≤&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;y&amp;lt;/mi&amp;gt;
&amp;lt;/math&amp;gt;
&amp;lt;br&amp;gt;-&amp;gt;
&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&amp;gt;
&amp;lt;mi&amp;gt;x&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;
&amp;lt;mfrac&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;log&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;log&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mrow&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/mrow&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;/mfrac&amp;gt;
&amp;lt;mo&amp;gt;≤&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;y&amp;lt;/mi&amp;gt;
&amp;lt;/math&amp;gt;
&amp;lt;br&amp;gt;-&amp;gt;
&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&amp;gt;
&amp;lt;mi&amp;gt;y&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;=&amp;lt;/mo&amp;gt;
&amp;lt;mo&amp;gt;⌈&amp;lt;/mo&amp;gt;
&amp;lt;mi&amp;gt;x&amp;lt;/mi&amp;gt;
&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;
&amp;lt;mfrac&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;log&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;mrow&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;log&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;
&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;N&amp;lt;/mi&amp;gt;&amp;lt;mrow&amp;gt;&amp;lt;mi&amp;gt;i&amp;lt;/mi&amp;gt;&amp;lt;mo&amp;gt;+&amp;lt;/mo&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/mrow&amp;gt;&amp;lt;/msub&amp;gt;
&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;/mrow&amp;gt;
&amp;lt;/mfrac&amp;gt;
&amp;lt;mo&amp;gt;⌉&amp;lt;/mo&amp;gt;
&amp;lt;/math&amp;gt;&lt;/p&gt;
&lt;p&gt;지금 여기서 ceil(올림)이 사용되는 이유는 나눴을 때, x.n(x&amp;gt;=0인 정수, n!=0)이 나오는 꼴이면 최소 곱을 알고 싶기 때문에 이것보다 더 크다면
우리는 x보다 크고 최대한 작은 값을 고르면 된다. 그래서 올림을 통해서 이를 해결한다.&lt;/p&gt;
&lt;p&gt;이것을 통해서 알 수 있는 것은 이건 오류가 발생할 가능성이 존재한다는 것이다.
(저는 코드짜서 넣고 그 다음에 블로그 쓰면서 생각해보니 이렇더라구요.... 완벽한 코드는 아닌 것 같아 남깁니다..)&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;cmath&amp;gt;
using namespace std;
using ll = unsigned long long;
int n;
int board[250&apos;001];
int results[250&apos;001];
int solution(int current){
    return max((int)(log2((double)board[current-1] / (double)board[current]) + (double)results[current - 1]), 0);//board[current-1])
}

int main(){
    // i번까지 모든 조건이 만족한다고 하면, i+1번은 i번보다 크다 2를 곱하는 과정을 반복문으로 하는 것이 아니라 바로 몇 번 곱해야 하는지 알 수 있는 방법을
    // 찾아보자 -&amp;gt; 그냥 수학으로 풀어야하는 거다!
    
    // N_i를 A라고 하자. N_{i+1}을 B라고 하자.
    // N_i * 2^x &amp;lt;= N_{i+1} * 2^y
    // (N_i*2^x)/N_{i+1} &amp;lt;= 2^y
    
    // 저기 y를 어떻게 내리냐 log 씌우면 될라나
    // (x + log_2(N_i)/log_2(N_{i+1}) &amp;lt;= y
    // 옹 나왔따! 근데 정답이 integer데 어떻게 나오냐
    // 아 그거구나
    // 그냥 올림처리하면 될라나
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin &amp;gt;&amp;gt; n;

    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; board[i];
    
    ll result = 0;
    for(int i = 1; i &amp;lt; n; i++) {
        results[i] = solution(i); result += results[i];
//        cout &amp;lt;&amp;lt; results[i] &amp;lt;&amp;lt; &quot; &quot;;
    }
//    cout &amp;lt;&amp;lt; &quot;\n&quot;;
    
    
    cout &amp;lt;&amp;lt; result;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 백조의 호수</title><link>https://compy07.github.io/Blog/posts/boj/3197/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/3197/</guid><description>이거 그냥 BFS로 풀리는데요?</description><pubDate>Tue, 05 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3197&quot;&gt;백조의 호수&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= R, C &amp;lt;= 1500)&lt;/td&gt;
&lt;td&gt;BFS(너비우선탐색), Implementation(구현)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;두 마리의 백조가 호수에서 살고 있었다. 그렇지만 두 마리는 호수를 덮고 있는 빙판으로 만나지 못한다.&lt;/p&gt;
&lt;p&gt;호수는 행이 R개, 열이 C개인 직사각형 모양이다. 어떤 칸은 얼음으로 덮여있다.&lt;/p&gt;
&lt;p&gt;호수는 차례로 녹는데, 매일 물 공간과 접촉한 모든 빙판 공간은 녹는다. 두 개의 공간이 접촉하려면 가로나 세로로 닿아 있는 것만 (대각선은 고려하지 않는다) 생각한다.&lt;/p&gt;
&lt;p&gt;아래에는 세 가지 예가 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;code&amp;gt;...XXXXXX..XX.XXX ....XXXX.......XX .....XX..........
....XXXXXXXXX.XXX .....XXXX..X..... ......X..........
...XXXXXXXXXXXX.. ....XXX..XXXX.... .....X.....X.....
..XXXXX..XXXXXX.. ...XXX....XXXX... ....X......XX....
.XXXXXX..XXXXXX.. ..XXXX....XXXX... ...XX......XX....
XXXXXXX...XXXX... ..XXXX.....XX.... ....X............
..XXXXX...XXX.... ....XX.....X..... .................
....XXXXX.XXX.... .....XX....X..... .................&amp;lt;/code&amp;gt;&lt;/p&gt;
&lt;p&gt;처음               첫째 날             둘째 날
백조는 오직 물 공간에서 세로나 가로로만(대각선은 제외한다) 움직일 수 있다.&lt;/p&gt;
&lt;p&gt;며칠이 지나야 백조들이 만날 수 있는 지 계산하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;처음은 되게 간단하게 bfs 한번만 돌려서, 대충 계산으로 해결하려고 했다가 군데 군데 뚫려있는 &apos;.&apos;이 있으면 성립이 안되는 경우가 발생&lt;/p&gt;
&lt;p&gt;노선을 틀어서 먼저 각 빙하마다 시간이 얼마나 지나야 녹는지를 선행적으로 구함. 이후에 여기에서 BFS를 전체적으로 돌면서 백조와 백조끼리의
제일 빠르게 갈 수 있는 cost를 탐색하고 반환한다.&lt;/p&gt;
&lt;p&gt;이러한 로직을 따랐습니다.&lt;/p&gt;
&lt;p&gt;모두 즐거운 PS 되세요!!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;다른 분들이 하시는거 보면 엄청 간단하고 효율적으로 풀이하셨던데... ㄷㄷ&lt;/p&gt;
&lt;p&gt;아직도 제 실력이 많이 부족한가 봅니다.&lt;/p&gt;
&lt;p&gt;더 열심히 해야겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;

int r, c;

char board[1501][1501];
int visited[1501][1501];
int visited_[1501][1501];

const int MAX = 1000000007;
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};

struct pos{
    int cost;
    int y, x;
};

struct compare {
    bool operator()(const pos&amp;amp; p1, const pos&amp;amp; p2) {
        return p1.cost &amp;gt; p2.cost;
    }
};

void solution(){
    queue&amp;lt;pos&amp;gt; q;
    for(int i = 0; i &amp;lt; r; i ++){
        for(int j = 0; j &amp;lt; c; j++){
            if(board[i][j] != &apos;X&apos;) q.push({0, i, j});
        }
    }
    
    while(!q.empty()){
        auto [cost, y, x] = q.front(); q.pop();
        
        if(visited[y][x] &amp;lt;= cost) continue;
        visited[y][x] = cost;
        
        
        for(int i = 0; i &amp;lt; 4; i++){
            int ny = y + dy[i], nx = x + dx[i];
            if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; r &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; c &amp;amp;&amp;amp; visited[ny][nx] &amp;gt; cost+(board[ny][nx] == &apos;X&apos;)){
                if(board[ny][nx] != &apos;X&apos;) q.push({0, ny, nx});
                else q.push({cost+(board[ny][nx] == &apos;X&apos;), ny, nx});
            }
        }
    }
}

int main(){
    cin &amp;gt;&amp;gt; r &amp;gt;&amp;gt; c;
    priority_queue&amp;lt;pos, vector&amp;lt;pos&amp;gt;, compare&amp;gt; q;
    pair&amp;lt;int, int&amp;gt; target;

    for(int i = 0; i &amp;lt; r; i++){
        for(int j = 0; j &amp;lt; c; j++){
            cin &amp;gt;&amp;gt; board[i][j];
            if(board[i][j] == &apos;L&apos;){
                if(q.empty()) q.push({0, i, j});
                else target = {i, j};
            }
            visited[i][j] = MAX;
            visited_[i][j] = MAX;
        }
    }
    
    solution();
    while(!q.empty()){
        auto [cost, y, x] = q.top(); q.pop();
        if(visited_[y][x] &amp;lt;= cost) continue;
        visited_[y][x] = cost;
        cost = max(visited[y][x], cost);
        
        for(int i = 0; i &amp;lt; 4; i++){
            int ny = y + dy[i], nx = x + dx[i];
            if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; r &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; c &amp;amp;&amp;amp; visited_[ny][nx] &amp;gt; cost){
                q.push({cost, ny, nx});
            }
        }
        
    }
//    
//    
//    for(int i = 0; i &amp;lt; r; i++){
//        for(int j = 0; j &amp;lt; c; j++){
//            cout &amp;lt;&amp;lt; visited_[i][j] &amp;lt;&amp;lt; &quot; &quot;;
//        }
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//    }
    cout &amp;lt;&amp;lt; visited_[target.first][target.second];

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 즐거운 회의</title><link>https://compy07.github.io/Blog/posts/boj/31997/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/31997/</guid><description>31997 풀이</description><pubDate>Sat, 29 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/31997&quot;&gt;즐거운 회의&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1번부터 N번까지 N명의 사람들이 시각 t=0에서 t=T까지 진행되는 회의에 참석한다.
i번 사람은 시각 t=a_i에 와서 t=b_i에 떠난다. 서로 다른 A번 사람과 B번 사람이 서로 친하면 두 사람이 회의에 참석하는 동안 즐거운 대화를 나눌 수 있다.&lt;/p&gt;
&lt;p&gt;사람들이 회의를 오고 떠나는 시각과 어떤 사람들이 서로 친한지 주어진다. 각 시각 t=0.5, t=1.5, ..., t=T-0.5에 즐거운 대화를 나누고 있는 사람들이 총 몇 쌍 있는지 구하여라.&lt;/p&gt;
&lt;p&gt;사람들의 쌍을 셀 때, 순서는 고려하지 않는다. 즉, A번 사람과 B번 사람의 쌍은 B번 사람과 A번 사람의 쌍과 같다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;문제가 굉장히 간단하기 때문에 그냥 바로 풀어버렸다.&lt;/p&gt;
&lt;p&gt;코드를 보면 바로 이해할 수 있다.&lt;/p&gt;
&lt;p&gt;그냥 현재의 시간과 다음 시간 조건만 확인하면 풀이를 할 수 있다.
(정말 간단한 로직으로 풀이가 가능하다.)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;result = [0] * (t+1)
timetable = {i: () for i in range(n + 1)}

for i in range(n):
    a, b = map(int, input().split())
    limit = b - a
    timetable[i] = (a, b)

for i in range(m):
    a, b = map(int, input().split())

    a -= 1
    b -= 1

    if timetable[b][0] &amp;gt; timetable[a][1] or timetable[a][0] &amp;gt; timetable[b][1]: continue

    if timetable[a][0] &amp;gt; timetable[b][0]: result[timetable[a][0]]+=1
    else: result[timetable[b][0]] += 1

    result[min(timetable[a][1], timetable[b][1])] -= 1

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 단 한번의 루프로 다 돌릴 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;n, m, t = map(int, input().split())
result = [0] * (t+1)
timetable = {i: () for i in range(n + 1)}

for i in range(n):
    a, b = map(int, input().split())
    limit = b - a
    timetable[i] = (a, b)

for i in range(m):
    a, b = map(int, input().split())

    a -= 1
    b -= 1

    if timetable[b][0] &amp;gt; timetable[a][1] or timetable[a][0] &amp;gt; timetable[b][1]: continue

    if timetable[a][0] &amp;gt; timetable[b][0]: result[timetable[a][0]]+=1
    else: result[timetable[b][0]] += 1

    result[min(timetable[a][1], timetable[b][1])] -= 1


current = 0
for i in range(t):
    current+=result[i]
    print(current)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] Street Development</title><link>https://compy07.github.io/Blog/posts/boj/32838/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/32838/</guid><description>재활 16일차</description><pubDate>Wed, 15 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/32838&quot;&gt;Street Development&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;2048MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 10^6)&lt;/td&gt;
&lt;td&gt;Binary Search, Greedy, Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;아니 문제가 아이디어는 그대로 사용했는데...
제 로직에서 over flow를 예상해서 long long을 사용했지만 또 overflow가 나올 것을 생각하지 못하고 정말 오랫동안
생각했네요.. ㅠㅜㅜㅜ&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
#include &amp;lt;math.h&amp;gt;
using namespace std;
using ll = long long;
using ld = long double;
//ll MOD = 1&apos;000&apos;000&apos;007;
ll MOD = 1&apos;000&apos;000&apos;000;
//ll MOD = 1.8446744074E19;

int L, N;
ll robots[1000001];
ll dis[1000001];

// 문제를 너무 대충 봄.
// 그냥 간격 자체를 찾는거롤 하는게 맞는데 뭐... 뭐를 한거지 젠장
bool calculation(ll power){
    ll right = robots[N-1] - power;
    dis[N-1] = right;
    
    // right
    for(int i = N-2; i &amp;gt; -1; i--){
        ll remaining = power;
        if(right &amp;gt; robots[i]) remaining -= (right - robots[i]) * 2;
        
        right = robots[i] - remaining;
        if(right &amp;gt; L) right = L;
        dis[i] = right;
//        cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; dis[i] &amp;lt;&amp;lt;&quot;\n&quot;;
    }
    
    ll left = robots[0] + power;
    
    if(left &amp;gt;= dis[1]) return true;
    
    for(int i = 1; i &amp;lt; N-1; i++){
        ll remaining = power;
        if(left &amp;lt; robots[i]) remaining -= (robots[i] - left) * 2;
        
        
        left = robots[i] + remaining;
        if(left &amp;lt; 0) left = 0;
//        cout &amp;lt;&amp;lt; left &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; dis[i+1] &amp;lt;&amp;lt; &quot;\n&quot;;
        if(left &amp;gt;= dis[i+1]) return true;
    }
    
    return false;
}
ll solution(){
    
    ll result = LLONG_MAX;
    
    ll left = 0, right = robots[N-1] + 1;
    
    while(left &amp;lt;= right){
        ll pivot = (left + right) /2;
        
        
        if(calculation(pivot)){
            result = min(result, pivot);
            right = pivot-1;
            
        }
        else left = pivot+1;
    }
    
//    cout &amp;lt;&amp;lt; left &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; right &amp;lt;&amp;lt; &quot;\n&quot;;
    
    return (result);
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin &amp;gt;&amp;gt; L &amp;gt;&amp;gt; N;
    
    for(int i = 0; i &amp;lt; N; i++) cin &amp;gt;&amp;gt; robots[i];
    
    cout &amp;lt;&amp;lt; solution();
//    cout &amp;lt;&amp;lt; calculation(64975);
//    for(int i = L; i &amp;gt; -1; i--){
//        if(calculation(i)) continue;
//        cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt;&quot;\n&quot;;
//        break;
//    }
    return 0;
}
/*
 20 1
 10
 
 100 5
 
 10000 8
 0 1 3 5 7 11 13 17
 
 100000 3
 10 100 101
 100 5
 10 20 30 40 50
 
 10000 8
 0 1 3 5 7 11 13 17
 
*/



&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 재미있는 파이프 퍼즐</title><link>https://compy07.github.io/Blog/posts/boj/32964/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/32964/</guid><description>재활 5일차</description><pubDate>Fri, 07 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/32964&quot;&gt;재미있는 파이프 퍼즐&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;2048MB&lt;/td&gt;
&lt;td&gt;(2 ≤ N ≤ 200000)&lt;/td&gt;
&lt;td&gt;GraphTraversal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 탐색을 돌리며 되는 문제였어서 그냥 풀었던 것 같아요&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include&amp;lt;set&amp;gt;
using namespace std;


int inf = 1e9;

int n;


char board[200&apos;001][2];
bool visited[200&apos;001][2];
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin &amp;gt;&amp;gt; n;
    
    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; board[i][0];
    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; board[i][1];
    
    
    queue&amp;lt;pair&amp;lt;bool, int&amp;gt;&amp;gt; q;
    
    q.push({0, 0});
    if(board[0][1] == &apos;L&apos;) q.push({1, 0});
    
    
    
    while(!q.empty() &amp;amp;&amp;amp; !visited[n-1][1]){
        auto [y, x] = q.front(); q.pop();
        
        int nx = x+1;
        bool ny = y;
        if(board[nx][y] ==&apos;L&apos;){
            ny = !y;
            if(board[nx][ny] == &apos;I&apos;) continue;
        }

        if(0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; n){
            visited[nx][ny] = true;
            q.push({ny, nx});
        }
        
    }
    

    
    if(visited[n-1][1]) cout &amp;lt;&amp;lt; &quot;YES&quot;;
    else cout &amp;lt;&amp;lt; &quot;NO&quot;;
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 타율 분석</title><link>https://compy07.github.io/Blog/posts/boj/34029/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/34029/</guid><description>재활 3일차</description><pubDate>Wed, 18 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/34029&quot;&gt;타율 분석&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(0.162 ≤ A ≤ 0.412, 1 ≤ B ≤ 300)&lt;/td&gt;
&lt;td&gt;Dynamic Programming, Math&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이걸 의도한건진 모르겠지만 그냥 각 상황별 경우의 수를 구해주고 나눠주는 절차를 밟는데
전체 가짓수가 A고 선택하는 수가 n이라고 하면&lt;/p&gt;
&lt;p&gt;A!/n!(A - n)!이 되는데 이때 식을 보면 결국 대칭성을 가지고 있음을 바로 알 수 있다.
그래서 그거 이용해서 150까지 팩토리얼 구하고나서 그거 대칭성 이용해서 300까지 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;그러고는 그냥 간단한 확률 계산이고 누적해서 더해주면 답을 간단히 구할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;random&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;

#define MOD 1000000007LL
#define MAX 1&apos;000&apos;000LL
using namespace std;
using ll = long long;

double A;
int B;


double fact[301]; // 골랐던 거, 안 골랐던 거
double dp[152];

int main() {
    cin &amp;gt;&amp;gt; A &amp;gt;&amp;gt; B;
    
    double y = A;
    double n = 1 - A;
    double std = 0.05;
    
    fact[0] = 1;
    fact[1] = 1;
    fact[2] = 2;
    fact[3] = 6;
    fact[4] = 24;
    fact[5] = 120;
    
    for(int i = 6; i &amp;lt; B; i++) fact[i] = fact[i-1] * i; // 어차피 내가 구하려는게 대칭성을 띄고 이ㅣㅆ음.
    
    dp[0] = 1;
    for(int i = 1; i &amp;lt; B / 2 + 2; i++) dp[i] = (dp[i-1] * (B - i + 1)) / i;
//    cout &amp;lt;&amp;lt; dp[149] &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; dp[151] &amp;lt;&amp;lt; &quot;\n&quot;;
    
    double result = 0;
    for(int i = 0; i &amp;lt;= B; i++){
        double ok = pow(y, i);
        double no = pow(n, B - i);
//        cout &amp;lt;&amp;lt; ok &amp;lt;&amp;lt;&quot; &quot; &amp;lt;&amp;lt; no &amp;lt;&amp;lt;&quot; &quot; &amp;lt;&amp;lt; ok * no &amp;lt;&amp;lt;&quot;\n&quot;;
        double c;
        if(i &amp;gt; B / 2) c = dp[B *  2 - i] * ok * no;
        else c = (dp[i] * ok * no);
        
//        cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; dp[i] &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; c &amp;lt;&amp;lt;&quot;\n\n&quot;;
        result += c;
        if(result &amp;gt;= std) {
            cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot;\n&quot;;
            break;
        }
    }

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] So☆Lucky</title><link>https://compy07.github.io/Blog/posts/boj/34030/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/34030/</guid><description>재활 2일차 - 2</description><pubDate>Tue, 17 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/34030&quot;&gt;So☆Lucky&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(0 ≤ N ≤ 200&apos;000, 1 ≤ A_i ≤ 10^9)&lt;/td&gt;
&lt;td&gt;Implementation, Math&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 간단하게 정렬할면 한쪽으로 밀어버리면 된다. 라는 아이디어로 구현하면 됩니다.&lt;/p&gt;
&lt;p&gt;쉽게 풀이가 가능합니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;random&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;
#define MOD 1000000007LL
#define MAX 1&apos;000&apos;000LL
using namespace std;
using ll = long long;

//double A, B;


ll nums[200&apos;001];

int main() {
    ll n;
    cin &amp;gt;&amp;gt; n;
    
    for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; nums[i];
    
    // first method
    bool check = false;
    ll back = nums[0];
    for(int i = 1; i &amp;lt; n; i++){
        if(nums[i] &amp;gt;= back){
            back = nums[i];
        }else{
            if(!((nums[i]+back) % 2)){
                check = true;
                break;
            }
        }
        
    }
    
    if(check) cout &amp;lt;&amp;lt; &quot;Unlucky&quot;;
    else cout &amp;lt;&amp;lt; &quot;So Lucky&quot;;
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
    // second method
    check = false;
    back = nums[0];
    for(int i = 1; i &amp;lt; n; i++){
        if(nums[i] &amp;gt;= back){
            back = nums[i];
        }else{
            if(((nums[i]+back) % 2)){
                check = true;
                break;
            }
        }
        
    }
    
    if(check) cout &amp;lt;&amp;lt; &quot;Unlucky&quot;;
    else cout &amp;lt;&amp;lt; &quot;So Lucky&quot;;
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 하이터치☆메모리</title><link>https://compy07.github.io/Blog/posts/boj/34031/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/34031/</guid><description>재활 2일차</description><pubDate>Tue, 17 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/29756&quot;&gt;하이터치☆메모리&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;1024MB&lt;/td&gt;
&lt;td&gt;(1 ≤ A, B ≤ 2&apos;000&apos;000)&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 고능하게 개수 세보면 정답이다.&lt;/p&gt;
&lt;p&gt;왼쪽에 있는 A인 경우 왼쪽 열려있는 것보다 오른쪽이 열려있는 상황이 생기면 무조건 필요없는 상황이 생긴다.
왜냐하면 애초에 올바른 괄호가 아니기 때문에 볼 필요도 없다.&lt;/p&gt;
&lt;p&gt;이와 같은 논리로 B에도 적용하여 stacked을 이용해서 현재 열려있는 괄호가 있으면 안 보도록 만들고, 아니면 이어 붙여졌을 때의 개수를 더해서 구한다.&lt;/p&gt;
&lt;p&gt;문제 자체는 어려운 문제는 아닌데 이게 왜 골드인 느낌이 좀 강하다. 하지만 뭐 조합론 붙어있는걸로 봐서는 수학으로 뭔가 할 수 있을 것 같은데, 현재 내 풀이도 O(N)이여서 그냥 이대로 끝내려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;random&amp;gt;

#define mod 1000000007LL
#define MAX 1&apos;000&apos;000LL
using namespace std;
using ll = long long;

ll counts[200&apos;001];

int main(){
    
    string A, B;
    
    cin &amp;gt;&amp;gt; A;
    cin &amp;gt;&amp;gt; B;
    ll result = 0;
    ll left_open = 0, right_open = 0;
    ll stacked = 0;
    for(int i = 0; i &amp;lt; A.size(); i++){
        if(A[i] == &apos;)&apos;) left_open ++;
        else right_open ++;
        if(right_open - left_open &amp;lt; 0) break;
        counts[right_open - left_open]++;
    }
    
    
    left_open = 0;
    right_open = 0;
    
    for(int i = 0; i &amp;lt; B.size(); i++){
        if(B[i] == &apos;)&apos;){
            left_open ++;
            if(stacked &amp;gt; 0) {
                right_open++;
                stacked--;
            }
        }
        else{
            // right_open ++;
            stacked ++;
        }
        
        if(left_open - right_open &amp;lt; 0 || stacked) continue;
//        cout &amp;lt;&amp;lt; left_open &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; right_open &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; left_open - right_open &amp;lt;&amp;lt; counts[left_open - right_open] &amp;lt;&amp;lt; &quot;\n&quot;;
        result += counts[left_open - right_open];
    }
    cout &amp;lt;&amp;lt; result;
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 막대 정사각형</title><link>https://compy07.github.io/Blog/posts/boj/4348/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/4348/</guid><description>재활 9일차</description><pubDate>Fri, 03 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/4348&quot;&gt;막대 정사각형&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 20, 1 ≤ stick_length ≤ 10,000)&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;약간 날먹 느낌의 dp였구용. 친구가 추천해줘서 풀었는데 그래도 조금 괜찮은 플레 dp 문제였습니다.&lt;/p&gt;
&lt;p&gt;어차피 만들어야할 정사각형은 정해져잇으니 그거 targeting해서 문제풀면 쉬웠던 문제였어용.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
using namespace std;
using ll = unsigned long long;
//ll MOD = 1&apos;000&apos;000&apos;007;
ll MOD = 1.8446744074E19;

int T, N;
int sticks[21];


int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin &amp;gt;&amp;gt; T;
    
    while(T --&amp;gt; 0){
        cin &amp;gt;&amp;gt; N;
        
        int sum = 0;
        for(int i = 0; i &amp;lt; N; i++) {
            cin &amp;gt;&amp;gt; sticks[i];
            sum += sticks[i];
        }
        
        int dp[(1 &amp;lt;&amp;lt; N) + 1];
        int length = (1 &amp;lt;&amp;lt; N) + 1;
        for(int i = 0; i &amp;lt; length; i++) dp[i] = -1;
        
        
        // visited 를 dp[i]로 처리한다. 즉 뭐를 처리했는가에 대한 dp식으로 간주하고, dp[i]는
        // 그리고 결국 만들 수 있는 최대 정사각형은.. 20 * 20이 최대일 것이다...
        // 그래서 width와 height가 n인 정사각형인 것을 알고서 말하면 될 듯?
        
        if(sum % 4 != 0) {
            cout &amp;lt;&amp;lt; &quot;no\n&quot;;
            continue;
        }
        
        int target = sum / 4;
        
        
        dp[0] = 0;
        for(int i = 0; i &amp;lt; length; i++){
            if(dp[i] == -1) continue;
            for(int n = 0; n &amp;lt; N; n++){
                if(i &amp;amp; (1 &amp;lt;&amp;lt; n)) continue;
                int next = dp[i] + sticks[n];
                if(next &amp;lt;= target) dp[i | (1 &amp;lt;&amp;lt; n)] = next % target;
//                dp[i | (1 &amp;lt;&amp;lt; n)] =
            }
        }
    
        if(dp[(1 &amp;lt;&amp;lt; N)-1] == 0) cout &amp;lt;&amp;lt; &quot;yes&quot;;
        else cout &amp;lt;&amp;lt; &quot;no&quot;;
//        cout &amp;lt;&amp;lt; dp[(1 &amp;lt;&amp;lt; N)-1] &amp;lt;&amp;lt; &quot;\n&quot;;
        cout &amp;lt;&amp;lt; &quot;\n&quot;;
        
        
        
        
        
    }
    
    
    
    
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] HTML 에디터</title><link>https://compy07.github.io/Blog/posts/boj/4752/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/4752/</guid><description>재활 3일차</description><pubDate>Wed, 05 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/4752&quot;&gt;HTML 에디터&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(0 ≤ B ≤ E ≤ TEXT의 길이, 1 ≤ TEXT의 길이 ≤ 200자)&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;엄... 이거 범위를 +1을 안해서 틀리는 이슈.... 화이팅 다들.. ㅠ&lt;/p&gt;
&lt;p&gt;오늘은 학교에서 푼거라서 영상이 없어유&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
using namespace std;

using ll = long long;
bool replace(std::string&amp;amp; str, const std::string&amp;amp; from, const std::string&amp;amp; to) {
    size_t start_pos = str.find(from);
    if(start_pos == std::string::npos)
        return false;
    str.replace(start_pos, from.length(), to);
    return true;
}
void solution(int start, int end, string&amp;amp; input){
    
    vector&amp;lt;string&amp;gt; tags;
    
    bool state = false;
    string current = &quot;&quot;;
    string result = &quot;&quot;;
    for(int i = 1; i &amp;lt; end; i++){
        char c = input[i];
        
        if(c == &apos;&amp;lt;&apos;){
            state = true;
            result+=current;
            current=&quot;&quot;;
        }else if(c == &apos;&amp;gt;&apos;){
            if(!state){
                current+=c;
                continue;
            }
            
            if(current[0] ==&apos;/&apos;){
                if(i &amp;lt; start){
                    replace(result, &quot;&amp;lt;&quot;+tags.back()+&quot;&amp;gt;&quot;, &quot;&quot;);
                    tags.pop_back();
                    current=&quot;&quot;;
                    state = false;
                    continue;
                }
                
                tags.pop_back();
            }
            else tags.push_back(current);
            
            result += &quot;&amp;lt;&quot;+current +&quot;&amp;gt;&quot;;
            
            current=&quot;&quot;;
            state = false;
        } else if(state || (!state &amp;amp;&amp;amp; i &amp;gt;= start)) current+=c;
    }
    
    result+= current;
    cout &amp;lt;&amp;lt; result;
    
    while(!tags.empty()){
        string tag = tags.back(); tags.pop_back();
        
        cout &amp;lt;&amp;lt; &quot;&amp;lt;/&quot; &amp;lt;&amp;lt; tag &amp;lt;&amp;lt; &quot;&amp;gt;&quot;;
    }
    
}

int main() {
    ios_base::sync_with_stdio(0); cin.tie(0);
    int start, end;
    string current;
    
    while(1){
        cin &amp;gt;&amp;gt; start &amp;gt;&amp;gt; end;
        if(start == -1) break;
        
        getline(cin, current);
        
        solution(start+1, end+1, current);
        cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }

    return 0;
}
// &amp;lt;b&amp;gt;&amp;lt;/b&amp;gt; &amp;lt;a&amp;gt;a&amp;lt;/a&amp;gt; &amp;lt;a&amp;gt;&amp;lt;/a&amp;gt;
// 7 17 &amp;lt;b&amp;gt;&amp;lt;/b&amp;gt; &amp;lt;a&amp;gt;a&amp;lt;/a&amp;gt; &amp;lt;a&amp;gt;&amp;lt;/a&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 숫자 세기</title><link>https://compy07.github.io/Blog/posts/boj/5696/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/5696/</guid><description>정올이 밀리니 시험 이틀전.. 못한다.. 나쁜넘들.. 하려고하니까 이렇게 막네</description><pubDate>Thu, 05 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/5696&quot;&gt;숫자 세기&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ A ≤ B ≤ 108)&lt;/td&gt;
&lt;td&gt;Implementation, Math&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 0~9까지의 숫자가 현재 자리에서 얼마나 반복되는지 더하는 것을 주어진 A, B에 맞춰서 둘 다 구해주고 빼버리면 그 사이에 있는 값을 알 수 있어요!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;random&amp;gt;

#define mod 1000000007LL

using namespace std;
using ll = long long;
int n, m;

vector&amp;lt;ll&amp;gt; solution(ll n) {
    vector&amp;lt;ll&amp;gt; cnt(10, 0);
    if (n &amp;lt; 0) return cnt;
    
    ll place = 1;
    ll lower = 0;
    ll current, higher;
    
    while (n / place &amp;gt; 0) {
        current = (n / place) % 10; // 앞을 본다
        higher = n / (place * 10); // 앞앞을 본다 - 이정도는 무조건 돌아가니까 내가 현재위 factor로 가리키고 있는 현재의 나는 어차피 돌면서 무조건 앞앞의 개수만큼 나오니까 바로 곱해버리기. 근데 잠시만
    // 그러면 매번 올라갈대마다 그 위치의 값을 곱해버리는건데 이게 맞을까?
        // 왜 이게 맞게 나오지 잠시만 뭐지
        // 아니 난 빡대가린가 방금까지 생각하던걸 까먹고 이상한데서 고뇌에 빠지네 ㅂㅅ
        // 아니 당연히 현재 위치가 반복되는 횟수를 곱한건데 어 왜 다 곱하는거지 ㅇㅈㄹ
        // 진자 기억력 요즘 문제 심각하다. 제발 고치자 제발 좀 제발 거의 금붕어 느낌이다
        
        // 변수 이름을 느낌있게 바꾸자
        for (int i = 0; i &amp;lt; 10; i++) {
            cnt[i] += higher * place;
            
            if (i &amp;lt; current) cnt[i] += place;
            else if (i == current) cnt[i] += lower + 1;
            
            if (i == 0) cnt[i] -= place;
        }
        
        lower = n % (place * 10);
        place *= 10;
    }
    
    return cnt;
}

int main() {
    ll A, B;
    
    while (cin &amp;gt;&amp;gt; A &amp;gt;&amp;gt; B) {
        if (A == 0 &amp;amp;&amp;amp; B == 0) break;
        vector&amp;lt;ll&amp;gt; cntB = solution(B);
        vector&amp;lt;ll&amp;gt; cntA = solution(A - 1);
        
        for (int i = 0; i &amp;lt; 10; i++) cout &amp;lt;&amp;lt; cntB[i] - cntA[i] &amp;lt;&amp;lt; &quot; &quot;;
        cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 레이저 통신</title><link>https://compy07.github.io/Blog/posts/boj/6087/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/6087/</guid><description>재활 24일차.. 왜 틀린지 모르겠음.. 아직도 아니 코드 로직이 똑같은데 틀렸다네?</description><pubDate>Thu, 27 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/6087&quot;&gt;레이저 통신&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1 ≤ W, H ≤ 100)&lt;/td&gt;
&lt;td&gt;BFS(너비우선탐색)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이거는 어제 풀었던 문제랑 그냥 똑같다고 생각하면 되는데요..
그래서 짜둔 코드를 안 지워서 그대로 필요한 것들만 빼고 지운 다음에 제출하니까 틀렸습니다... 그래서 음 뭐지? 하고 반례를 다 넣어봤는데 다 정답.. 시간 초과도 아님.. 애초에 무조건 돌아가는 코드라 생각도 안 했는데.. 이게 무슨?&lt;/p&gt;
&lt;p&gt;그래서 조금 다시 체계적으로 작성하려고 시간을 좀 씀.. 그랬더니? 정답.. 뭐지? 도대체 왜 이러는거냐 백준&lt;/p&gt;
&lt;p&gt;아우.. 이런 것때문에 시간이 소모된게 너무 아까움.. 다들 조심하시고 만약 막히면 그냥 다 지우고 다시 짜세요 그게 답입니다.&lt;/p&gt;
&lt;p&gt;하튼 즐거운 ps 되시길&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;

struct pos {
    int dir;
    int y, x;
    int cost;
};

int dx[] = {0,1,-1,0,0}, dy[] = {0,0,0,1,-1};
int W, H;
char board[101][101];
int cost[101][101][4];

int main() {
    ios_base::sync_with_stdio(0); cin.tie(0);
    
    cin &amp;gt;&amp;gt; W &amp;gt;&amp;gt; H;
    pos start = {-1,-1,-1,0}, end;
    bool check = true;
    
    for(int i=0; i&amp;lt;H; i++) for(int j=0; j&amp;lt;W; j++) {
        cin &amp;gt;&amp;gt; board[i][j];
        if(board[i][j] == &apos;C&apos;) {
            if(check) { start = {0,i,j,0}; check = false; }
            else end = {0,i,j,0};
            board[i][j] = &apos;.&apos;;
        }
        for(int d=0;d&amp;lt;4;d++) cost[i][j][d] = 1e9;
    }
    
    queue&amp;lt;pos&amp;gt; q;
    q.push({1, start.y, start.x, 0});
    q.push({2, start.y, start.x, 0});
    q.push({3, start.y, start.x, 0});
    q.push({4, start.y, start.x, 0});
    
    
    while(!q.empty()){
        pos cur = q.front(); q.pop();
        
        int y = cur.y, x = cur.x, dir = cur.dir, c = cur.cost;
        
//        cout &amp;lt;&amp;lt;&quot;check!: &quot;&amp;lt;&amp;lt; c &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; cost[y][x][dir-1] &amp;lt;&amp;lt; &quot;\n&quot;;
        if(c &amp;gt; cost[y][x][dir-1]) continue;
        
        if(y == end.y &amp;amp;&amp;amp; x == end.x) continue;
        
        for(int nd=1; nd&amp;lt;=4; nd++){
            int ny = y + dy[nd];
            int nx = x + dx[nd];
            
            if(ny&amp;lt;0 || nx&amp;lt;0 || ny&amp;gt;=H || nx&amp;gt;=W) continue;
            if(board[ny][nx] == &apos;*&apos;) continue;
            
            // 1:동, 2:서, 3:남, 4:북
            if(dir != nd &amp;amp;&amp;amp; ((dir&amp;lt;=2 &amp;amp;&amp;amp; nd&amp;lt;=2) || (dir&amp;gt;=3 &amp;amp;&amp;amp; nd&amp;gt;=3))) continue;
            
            int newCost = c + (dir != nd);
            
            if(cost[ny][nx][nd-1] &amp;gt; newCost){
                cost[ny][nx][nd-1] = newCost;
                q.push({nd, ny, nx, newCost});
            }
        }
    }
    
//    for(int i = 0; i &amp;lt; H; i++){ for(int j = 0; j &amp;lt; W; j++){
//        if(cost[i][j][0] == 1e9) cout &amp;lt;&amp;lt; &quot;o &quot;;
//        else cout &amp;lt;&amp;lt; cost[i][j][0] &amp;lt;&amp;lt; &quot; &quot;;
//    }
//        cout &amp;lt;&amp;lt; &quot;\n&quot;;
//    }

    
    
    int ans = 1e9;
    for(int d=0; d&amp;lt;4; d++)
        ans = min(ans, cost[end.y][end.x][d]);
    
    cout &amp;lt;&amp;lt; ans;
    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] Zombie’s Treasure Chest</title><link>https://compy07.github.io/Blog/posts/boj/6278/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/6278/</guid><description>아니 근데 이거는 테그를 보면 안되는 거 였을라나... 왜 없냐...</description><pubDate>Wed, 09 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/6278&quot;&gt;Zombie’s Treasure Chest&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(T &amp;lt;= 200, 1 &amp;lt;= N, S1, S2, V1, V2 &amp;lt;= 2³¹-1)&lt;/td&gt;
&lt;td&gt;BruteForce(완전탐색), Math(수학)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;Some brave warriors come to a lost village. They are very lucky and find a lot of treasures and a big treasure chest, but with angry zombies.&lt;/p&gt;
&lt;p&gt;The warriors are so brave that they decide to defeat the zombies and then bring all the treasures back. A brutal long-drawn-out battle lasts from morning to night and the warriors find the zombies are undead and invincible.&lt;/p&gt;
&lt;p&gt;Of course, the treasures should not be left here. Unfortunately, the warriors cannot carry all the treasures by the treasure chest due to the limitation of the capacity of the chest. Indeed, there are only two types of treasures: emerald and sapphire. All of the emeralds are equal in size and value, and with infinite quantities. So are sapphires.&lt;/p&gt;
&lt;p&gt;Being the priest of the warriors with the magic artifact: computer, and given the size of the chest, the value and size of each types of gem, you should compute the maximum value of treasures our warriors could bring back.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;자... 저는 이 문제를 처음에 각 에메랄드, 사파이어에 대해서 두개 다 단위 가치를 계산했습니다.
그런 후에 효율 좋은 친구로 다 채워버리고 남은 공간에 남은 친구 넣을 수 있는 친구를 넣어서 결과를 냈거든용??&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;6
10 5 999 2 500
9 4 250 2 100
14 13 28 3 3
5 2 2 3 3
7 7 13 2 4
14 11 13 3 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기까지는 잘 되더라구요 그래서 딱 내봤더니? 터지구 펑펑펑!&lt;/p&gt;
&lt;p&gt;그래서 정말 좀 더 뭘 해볼까 계속해서 고민해보다가 모르겠어서 이 로직 끝에 대충 for문 하나 넣어서 조합을 다 검사했거든용?&lt;/p&gt;
&lt;p&gt;당연히 최악의 경우 2^31이라서 절대 불가능하죠...&lt;/p&gt;
&lt;p&gt;그래서 좀 더 생각한게.. &quot;범위를 어떻게 줄이지?&quot; 였습니다. 이게 진짜 다행인게.. 다른 곳으로 빠졌으면 진짜 답이 없었거든용..&lt;/p&gt;
&lt;p&gt;저는 일단 일단 원래 사용하던 방식을 조금 사용합니다.
그러면 기본적인 아이디어를 나열해 보겠습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;일단 내가 선택하는 보석을 다 선택하면 남은 한 개의 보석을 최대한 많이 선택할거에요.(한 개의 보석을 다 얻었는데, 공간이 남았다.. 그러면 다른 보석을 넣을 수 있으면 무조건 최대한 많이 넣는다)&lt;/li&gt;
&lt;li&gt;n &amp;gt; s1*s2이면 v1을 s2개 또는 v2를 s1개를 들고온다
&lt;ul&gt;
&lt;li&gt;&amp;lt;code&amp;gt;n &amp;gt; s1 * s2&amp;lt;/code&amp;gt;이면 넣을 수 있는 공간이 두 보석의 크기를 곱한 것보다 크다는 의미이죠&lt;/li&gt;
&lt;li&gt;그래서 한 종류의 보석을 다른 보석의 크기만큼 채우는게 가능합니다&lt;/li&gt;
&lt;li&gt;예를 들자면, v1을 s2개 선택하면 s1 * s2 정도의 공간을 차지하고, 남은 공간은 s2로 최대한 채울 수 있습니다&lt;/li&gt;
&lt;li&gt;이렇게 되면 두 보석의 조합을 모두 고려해서 최적해를 보장합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이제 다른 경우는 sqrt(N) 이하의 경우니까 요걸 bruteforce로 합니다
&lt;ul&gt;
&lt;li&gt;N ≤ s1*s2인 경우, s1과 s2 중 적어도 하나는 sqrt(N) 이하입니다&lt;/li&gt;
&lt;li&gt;그래서 공간을 적게 차지하는 보석을 기준으로 브포를 돌리면 최악이여도 sqrt(N) 정도로 해결 가능합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 아이디어를 그냥 구현하면 됩니다. 아 그리고 구현을 좀 단순화하기 위해서 swap을 통해서 그냥 크기 정렬 해줬습니다&lt;/p&gt;
&lt;p&gt;(아니 근데 이 문제 브포 테그 안 달려 있었다니까요?!?!??)&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cmath&amp;gt;
using namespace std;

using ll = long long;
void swap(ll&amp;amp; a, ll&amp;amp; b){
    ll tmp;
    tmp = a;
    a = b;
    b = tmp;
}
ll solution(ll n, ll s1, ll v1, ll s2, ll v2) {
    ll result = 0;
    
    // cout &amp;lt;&amp;lt; s1 &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; s2 &amp;lt;&amp;lt; &quot;\n&quot;;
    if (s1 &amp;gt; s2) {
        swap(s1, s2);
        swap(v1, v2);
    }
    // cout &amp;lt;&amp;lt; s1 &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; s2 &amp;lt;&amp;lt; &quot;\n&quot;;
    if (s2 &amp;gt; sqrt(n)) {
        for (ll i = 0; i * s2 &amp;lt;= n; i++) result = max(result, (n - i * s2) / s1 * v1 + i * v2);
        return result;
    }
    if (v1 * s2 &amp;gt; v2 * s1) {
        swap(s1, s2);
        swap(v1, v2);
    }
    for (ll i = 0; i &amp;lt;= s2 &amp;amp;&amp;amp; n - s1 * i &amp;gt;= 0; i++) result = max(result, (n - i * s1) / s2 * v2 + i * v1);
    
    return result;
}

int main() {
    int t;
    cin &amp;gt;&amp;gt; t;
    for (int i = 1; i &amp;lt;= t; ++i) {
        ll n, s1, v1, s2, v2;
        cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; s1 &amp;gt;&amp;gt; v1 &amp;gt;&amp;gt; s2 &amp;gt;&amp;gt; v2;
        cout &amp;lt;&amp;lt; &quot;Case #&quot; &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot;: &quot; &amp;lt;&amp;lt; solution(n, s1, v1, s2, v2) &amp;lt;&amp;lt; endl;
    }
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 히스토그램에서 가장 큰 직사각형</title><link>https://compy07.github.io/Blog/posts/boj/6549/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/6549/</guid><description>가장 큰 직사각형!!</description><pubDate>Sun, 07 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/6549&quot;&gt;히스토그램에서 가장 큰 직사각형&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= N &amp;lt;= 100,000&amp;lt;br&amp;gt;0&amp;lt;=h&amp;lt;=1,000,000,000)&lt;/td&gt;
&lt;td&gt;Stack(자료구조 스택)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;히스토그램은 직사각형 여러 개가 아래쪽으로 정렬되어 있는 도형이다. 각 직사각형은 같은 너비를 가지고 있지만, 높이는 서로 다를 수도 있다. 예를 들어, 왼쪽 그림은 높이가 2, 1, 4, 5, 1, 3, 3이고 너비가 1인 직사각형으로 이루어진 히스토그램이다.
&lt;img src=&quot;https://www.acmicpc.net/upload/images/histogram.png&quot; alt=&quot;problem_image&quot; /&gt;
히스토그램에서 가장 넓이가 큰 직사각형을 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;문제는 굉장히 심플한데 대게 이러한 문제들이 걍 어려운것 같다.
처음 이 문제를 접했을 때 사실 잘 감이 안오긴 했다. 그러다 계속 생각해보니까 스택을 풀이를 할 수 있을 것 같다는 생각이 들어서 구현해서 풀었더니 잘 풀렸던 문제였다.&lt;/p&gt;
&lt;p&gt;이러한 유형의 스택 문제를 보통 &quot;모노톤 스택&quot;이라고 부르더라... 나는 사실 알고리즘 이름을 알고 다니진 않아서...
하튼 사실 이러한 유형을 처음 본 나로써는 여기까지 구상하는게 쬐끔 어려웠지만 지금와서는 이러한 방법론을 생각해내는게 조금 익숙해진 것 같다.&lt;/p&gt;
&lt;p&gt;나는 이런 문제를 추천한다. 연습하기도 생각하기도 좋은 문제이면서 구현도 꽤 교육적인 것처럼 느껴졌다.(적어도 나는)&lt;/p&gt;
&lt;p&gt;자 이제 풀이를 해보겠다.&lt;/p&gt;
&lt;p&gt;일단 이 문제는 나처럼 코드를 구현하면 단 한번만의 검사로 끝낼 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rects = list(map(int, input().split()))
if rects[0] == 0: return -1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;요번 포스팅에서는 입력까지 다뤄보겠다.
먼저 현재 상태가 들어왔다. 만약 이 rects[0]이 0이라면 다음 입력이 없기때문에 함수에서 나온다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; s = []
 n = rects.pop(0)
 result = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;s는 우리가 사용할 stack이고, n은 바로 현재 그 직사각형의 개수를 나타내기에 빼내준다.&lt;/p&gt;
&lt;p&gt;reuslt는 말그대로 정답을 저장할 변수이다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for i in range(n):
            while s and rects[s[-1]] &amp;gt; rects[i]:
                h = s.pop()  # height
                w = i - s[-1] - 1 if s else i
                result = result if result &amp;gt; w * rects[h] else w * rects[h]
            s.append(i)
            
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;정말 놀랍겠지만 아이디어만 나온다면 사실 그렇게 긴 코드도 필요 없는거 같다.&lt;/p&gt;
&lt;p&gt;코드를 좀 요약해보겠다.&lt;/p&gt;
&lt;p&gt;입력 및 종료 조건 확인&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rects = list(map(int, input().split())) : 입력&lt;/li&gt;
&lt;li&gt;if rects[0] == 0: return -1 : 종료 조건&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;스택 초기화 및 첫 번째 값 처리&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;s = [] : 우리가 쓸 스택 초기화&lt;/li&gt;
&lt;li&gt;n = rects.pop(0) : 위에서 설명했죵?&lt;/li&gt;
&lt;li&gt;result = 0 : 결과&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;직사각형 순회하며 스택에 저장 및 넓이 계산&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;for i in range(n):&lt;/li&gt;
&lt;li&gt;while s and rects[s[-1]] &amp;gt; rects[i] : 현재 직사각형 높이가 스택의 마지막 직사각형 높이보다 작을 때, 스택에서 높이를 꺼내 넓이를 계산합니다.(모노톤 스택)&lt;/li&gt;
&lt;li&gt;h = s.pop() : 스택 top을 꺼내기&lt;/li&gt;
&lt;li&gt;w = i - s[-1] - 1 if s else i : 너비를 계산합니다. 스택이 비어 있으면 i, 비어 있지 않으면 (i - 스택의 마지막 인덱스 - 1)로 계산합니다.&lt;/li&gt;
&lt;li&gt;result = result if result &amp;gt; w * rects[h] else w * rects[h] : 넓이를 계산하여 최대 넓이를 갱신&lt;/li&gt;
&lt;li&gt;s.append(i) : 현재 자기자신 push&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;남아 있는 직사각형 처리&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;while s: : 스택에 남아 있는 직사각형을 모두 처리&lt;/li&gt;
&lt;li&gt;h = s.pop()&lt;/li&gt;
&lt;li&gt;w = n - s[-1] - 1 if s else n : 너비를 계산&lt;/li&gt;
&lt;li&gt;result = result if result &amp;gt; w * rects[h] else w * rects[h] : 최대 넓이를 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이런 과정을 거치면 처리합니다.&lt;/p&gt;
&lt;p&gt;사실 이 문제가 너무 잘 알려져있어서 딱히 써야된다는 생각을 안 하고 있었는데, 어떠한 특별한 일 때문에
cpp로 짜게되었는데 다시 이 문제를 보니까 굉장히 교육적인 것 같아서 좀 적게 되었다.&lt;/p&gt;
&lt;p&gt;오늘도 happy PS 하세용!!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
def solution():
    while True:
        rects = list(map(int, input().split()))
        if rects[0] == 0: return -1

        s = []
        n = rects.pop(0)
        result = 0

        for i in range(n):
            while s and rects[s[-1]] &amp;gt; rects[i]:
                h = s.pop()  # height
                w = i - s[-1] - 1 if s else i
                result = result if result &amp;gt; w * rects[h] else w * rects[h]
            s.append(i)

        #남은 친구들 다 빼서 처리
        while s:
            h = s.pop()
            w = n - s[-1] - 1 if s else n
            result = result if result &amp;gt; w * rects[h] else w * rects[h]
        # return result
        print(result)

solution()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] HTML</title><link>https://compy07.github.io/Blog/posts/boj/6581/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/6581/</guid><description>음... ㅇㅁㅇ....</description><pubDate>Sun, 06 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/6581&quot;&gt;HTML&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;EOF 처리&lt;/td&gt;
&lt;td&gt;implementation(구현)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;오로지  &amp;lt;br&amp;gt;, &amp;lt;hr&amp;gt; 태그와 그 외 평문으로만 주어지는 HTML 문서가 있을 때, 그 결과를 보여주는 코드를 작성하시오.&lt;/p&gt;
&lt;p&gt;한 줄에는 80자보다 많은 글자가 출력되어서는 안 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;진짜 말 그대로 구현 문제인데... 조금 약간 이상하게 구현하면 바로 틀립니다.. 그리고 약간 문제가 제시하는 입력에 부합하지 않아도 들어가는 코드도 존재하구요...&lt;/p&gt;
&lt;p&gt;일단 구현으로 풀리는 문제긴 합니다. 즐거운 ps 되십쇼오오&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

const string HR_LINE(80, &apos;-&apos;);

void print(string line) {
    if (!line.empty()) cout &amp;lt;&amp;lt; line &amp;lt;&amp;lt; &apos;\n&apos;;
    
}

void solution(string&amp;amp; line, const string&amp;amp; word, bool&amp;amp; new_line) {
    if (word == &quot;&amp;lt;br&amp;gt;&quot;) {
        print(line);
        if (new_line) cout &amp;lt;&amp;lt; &apos;\n&apos;;
        line = &quot;&quot;;
        new_line = true;
    } else if (word == &quot;&amp;lt;hr&amp;gt;&quot;) {
        print(line); cout &amp;lt;&amp;lt; HR_LINE &amp;lt;&amp;lt; &apos;\n&apos;;
        line = &quot;&quot;;
        new_line = true;
    } else {
        if (line.length() + (!line.empty() ? 1 : 0) + word.length() &amp;gt; 80) {
            print(line);
            line = word;
        } else {
            if (!line.empty()) line += &apos; &apos;;
            line += word;
        }
        new_line = false;
    }
}

int main() {
    string word, line;
    bool new_line = true;

    while (cin &amp;gt;&amp;gt; word) {
        solution(line, word, new_line);
    }

    print(line);

    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 앱</title><link>https://compy07.github.io/Blog/posts/boj/7579/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/7579/</guid><description>오랜만에 dp였다... 재활 6일차</description><pubDate>Thu, 06 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/7579&quot;&gt;앱&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;1 ≤ N ≤ 100 &amp;lt;br&amp;gt;1 ≤ M ≤ 10,000,000&amp;lt;br&amp;gt;1 ≤ mi ≤ 10,000,000&amp;lt;br&amp;gt;0 ≤ ci ≤ 100&amp;lt;br&amp;gt;M ≤ m1 + m2 + ... + mN&lt;/td&gt;
&lt;td&gt;Dynamic Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;우리는 스마트폰을 사용하면서 여러 가지 앱(App)을 실행하게 된다. 대개의 경우 화면에 보이는 ‘실행 중’인 앱은 하나뿐이지만 보이지 않는 상태로 많은 앱이 &apos;활성화&apos;되어 있다. 앱들이 활성화 되어 있다는 것은 화면에 보이지 않더라도 메인 메모리에 직전의 상태가 기록되어 있는 것을 말한다. 현재 실행 중이 아니더라도 이렇게 메모리에 남겨두는 이유는 사용자가 이전에 실행하던 앱을 다시 불러올 때에 직전의 상태를 메인 메모리로부터 읽어 들여 실행 준비를 빠르게 마치기 위해서이다.&lt;/p&gt;
&lt;p&gt;하지만 스마트폰의 메모리는 제한적이기 때문에 한번이라도 실행했던 모든 앱을 활성화된 채로 메인 메모리에 남겨두다 보면 메모리 부족 상태가 오기 쉽다. 새로운 앱을 실행시키기 위해 필요한 메모리가 부족해지면 스마트폰의 운영체제는 활성화 되어 있는 앱들 중 몇 개를 선택하여 메모리로부터 삭제하는 수밖에 없다. 이러한 과정을 앱의 ‘비활성화’라고 한다.&lt;/p&gt;
&lt;p&gt;메모리 부족 상황에서 활성화 되어 있는 앱들을 무작위로 필요한 메모리만큼 비활성화 하는 것은 좋은 방법이 아니다. 비활성화된 앱들을 재실행할 경우 그만큼 시간이 더 필요하기 때문이다. 여러분은 이러한 앱의 비활성화 문제를 스마트하게 해결하기 위한 프로그램을 작성해야 한다&lt;/p&gt;
&lt;p&gt;현재 N개의 앱, A1, ..., AN이 활성화 되어 있다고 가정하자. 이들 앱 Ai는 각각 mi 바이트만큼의 메모리를 사용하고 있다. 또한, 앱 Ai를 비활성화한 후에 다시 실행하고자 할 경우, 추가적으로 들어가는 비용(시간 등)을 수치화 한 것을 ci 라고 하자. 이러한 상황에서 사용자가 새로운 앱 B를 실행하고자 하여, 추가로 M 바이트의 메모리가 필요하다고 하자. 즉, 현재 활성화 되어 있는 앱 A1, ..., AN 중에서 몇 개를 비활성화 하여 M 바이트 이상의 메모리를 추가로 확보해야 하는 것이다. 여러분은 그 중에서 비활성화 했을 경우의 비용 ci의 합을 최소화하여 필요한 메모리 M 바이트를 확보하는 방법을 찾아야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;문제를 처음에 볼 때, priority_queue를 써서 하다고 바로 제출했다가 메모리 초과 나서 다시 보니 2^100이여서 헙.. 하고 다시 풀었다.&lt;/p&gt;
&lt;p&gt;ㅋㅌㅋㅎㅎㅋㅌㅎ 진짜 dp인가? 뭐지 뭐지 하다가 한번 해보니 아 이렇게 될 수 있구나 하고 풀어서 맞추니 다행이였다. 확실히 dp는 뭔가 dp인 것 같아도
접근하기가 쉽지 않다. 일단 상태 정의부터 시작해서 두 사이간의 어떤 관계가 있는지를 잘 꽤뚫어야 하는데 정말 힘이든다.&lt;/p&gt;
&lt;p&gt;일단 풀었으니 개추&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

int n, m;

pair&amp;lt;ll, ll&amp;gt; memory[101];

ll dp[101][10001];

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    
    for(int i = 1; i &amp;lt;= n; i++){
        cin &amp;gt;&amp;gt; memory[i].second;
    }
    for(int i = 1; i &amp;lt;= n; i++){
        cin &amp;gt;&amp;gt; memory[i].first;
    }
    
    for(int i = 1; i &amp;lt;= n; i++){
        auto [free, cost] = memory[i];
//        cout &amp;lt;&amp;lt; free &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; cost &amp;lt;&amp;lt; &quot;\n&quot;;
        for(int j = 0; j &amp;lt; 10001; j++){
            dp[i][j] = max(dp[i][j], dp[i-1][j]);
            if(free+j &amp;gt;= 10001) continue;
            dp[i][free+j] = max(dp[i-1][j] + cost, dp[i][free+j]);
        }
    }

    for(int i = 0; i &amp;lt; 10001; i++){
        if(dp[n][i] &amp;gt;= m) {
            cout &amp;lt;&amp;lt; i;
            return 0;
        }
    }
    

    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 막대 배치</title><link>https://compy07.github.io/Blog/posts/boj/8895/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/8895/</guid><description>재활 11일차</description><pubDate>Sun, 05 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/8895&quot;&gt;막대 배치&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1 ≤ N ≤ 20, 1 ≤ L, R ≤ N)&lt;/td&gt;
&lt;td&gt;Math&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;솔직히 날먹이었따!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://compy07.github.io/Blog/posts/boj/1328/&quot;&gt;고층 빌딩&lt;/a&gt; 이거 재탕 문제여서 그냥 거의 바로 풀었던거 같아요.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
using namespace std;
using ll = unsigned long long;
//ll MOD = 1&apos;000&apos;000&apos;007;
ll MOD = 1.8446744074E19;

int N, L, R;

ll dp[101][101][101];

ll solution(ll n, ll l, ll r){
    ll result = 0;
    if (l &amp;lt;= 0 || r &amp;lt;= 0 || l + r &amp;gt; n + 1) return 0;
    if(n == 1) return (l == 1 &amp;amp;&amp;amp; r == 1);
    
    if(dp[n][l][r] != -1) return dp[n][l][r];
    
    // solution(n+1, r, l) = solution(n, r-1, l) + solution(n, r, l-1) + solution(n, r, l) * n
    result = ((solution(n-1, l, r-1)%MOD + solution(n-1, l-1, r)%MOD)%MOD + (solution(n-1, l, r) * (n-2))%MOD)%MOD;
    dp[n][l][r] = result;
    return result;
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    
    for(int i = 0; i &amp;lt; 101; i++){
        for(int j = 0; j &amp;lt; 101; j++){
            
            for(int k = 0; k &amp;lt; 101; k++){
                dp[i][j][k] = -1;
            }
        }
    }
    int T;
    cin &amp;gt;&amp;gt; T;
    while(T --&amp;gt; 0){
        cin &amp;gt;&amp;gt; N &amp;gt;&amp;gt; L &amp;gt;&amp;gt; R;
        cout &amp;lt;&amp;lt; solution(N, L, R) &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    
    return 0;
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 두 수의 합</title><link>https://compy07.github.io/Blog/posts/boj/9024/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/9024/</guid><description>재활 4일차</description><pubDate>Thu, 19 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9024&quot;&gt;두 수의 합&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(2 ≤ N ≤ 1&apos;000&apos;000, -10^8 ≤ k ≤ 10^8)&lt;/td&gt;
&lt;td&gt;Binary Search, Two Pointer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;조건에 서로 다른 수들만 들어오기 때문에 성립하구요. 직관적이고 전형적인 바이너리서치 문제여서
금방 풀었습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;이거 fastio 없으면 시간초과 납니다. 조심하셔요&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;random&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;

#define MOD 1000000007LL
#define MAX 1&apos;000&apos;000LL
using namespace std;
using ll = long long;


ll nums[1&apos;000&apos;001];

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int t;
    cin &amp;gt;&amp;gt; t;
    
    while(t --&amp;gt; 0){
        int n, k;
        cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
        
        
        for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; nums[i];
        sort(nums, nums+n);
        
        int left = 0, right = n-1;
        
        ll diff = 1&apos;000&apos;000&apos;000&apos;000;
        ll count = 0;
        
        while(left &amp;lt; right){
            ll sumOfLR = nums[left] + nums[right];
            if(sumOfLR &amp;gt; k)
                right--;
            else
                left++;
            
            if(diff == abs(k - sumOfLR)) count++;
            else if(diff &amp;gt; abs(k - sumOfLR)){
                count = 1;
                diff = abs(k - sumOfLR);
            }
        }
        
        cout &amp;lt;&amp;lt; count &amp;lt;&amp;lt; &quot;\n&quot;;
        
    }
   

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 열쇠</title><link>https://compy07.github.io/Blog/posts/boj/9328/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/9328/</guid><description>재활 13일차</description><pubDate>Tue, 18 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9328&quot;&gt;열쇠&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;(1 ≤ T ≤ 10, 2 ≤ h, w ≤ 100)&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;그냥 BFS를 좀 돌리면 되는데 여기서 팁 같은걸 드리자면 바깥쪽도 돌 수 있도록 입력으로 들어오는 매을 (1, 1)부터 시작해서 바같도 돌 수 있도록 해주고, 열쇠 열었을 때 다 처리되도록 잘만 짜면 AC 맞을 수 있어요!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

int inf = 1e9+7;

int n ,m;

char board[105][105];
bool key[130];
bool visited[105][105];
vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; pos[130];
int dx[] = {1, -1, 0, 0}, dy[] =  {0, 0, 1, -1};


int solution(){
    queue&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; q;
    
    q.push({0, 0});
    visited[0][0] = true;
    
    int result = 0;
    
    while(!q.empty()){
        auto [y, x] = q.front(); q.pop();
        
        if(board[y][x] == &apos;$&apos;) result++;
        
//        cout &amp;lt;&amp;lt; board[y][x] &amp;lt;&amp;lt; &quot;\n&quot;;
        for(int i = 0; i &amp;lt; 4; i++){
            int ny = y + dy[i];
            int nx = x + dx[i];
            
            
            if(0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt;= n+2 &amp;amp;&amp;amp; 0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt;= m+2 &amp;amp;&amp;amp; board[ny][nx] != &apos;*&apos; &amp;amp;&amp;amp; !visited[ny][nx]){
                visited[ny][nx] = true;
                if(&apos;a&apos; &amp;lt;= board[ny][nx] &amp;amp;&amp;amp; board[ny][nx] &amp;lt;= &apos;z&apos;){
                    key[board[ny][nx]] = true;
                    for(pair&amp;lt;int, int&amp;gt; door : pos[board[ny][nx] - &apos;a&apos; + &apos;A&apos;]){
                        if(visited[door.first][door.second]) q.push(door);
                    }
                    
                    
                }
                
                else if(&apos;A&apos; &amp;lt;= board[ny][nx] &amp;amp;&amp;amp; board[ny][nx] &amp;lt;= &apos;Z&apos; &amp;amp;&amp;amp; !key[board[ny][nx] + &apos;a&apos; - &apos;A&apos;]) continue;
                
                q.push({ny, nx});
                
            }
            
        }
        
    }
//    cout &amp;lt;&amp;lt; &apos;\n&apos;;
//    for(int i = 0; i &amp;lt;= n+1; i++){
//        for(int j = 0; j &amp;lt;= m+1; j++) cout &amp;lt;&amp;lt; visited[i][j] &amp;lt;&amp;lt; &quot; &quot;;
//        cout &amp;lt;&amp;lt; &apos;\n&apos;;
//    }
    
    return result;;
}


int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    int t;
    cin &amp;gt;&amp;gt; t;
    
    
    
    while(t--&amp;gt;0){
        cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
        
        for(int i = 0; i &amp;lt; 105; i++){
            for(int j = 0; j &amp;lt; 105; j++) {
                board[i][j] = &apos;.&apos;;
                visited[i][j] = false;
            }
        }
        
        
        for(int i = 0; i &amp;lt; 131; i++){
            pos[i].clear();
            key[i] = false;
        }
        
        for(int i = 1; i &amp;lt;= n; i++){
            for(int j = 1; j &amp;lt;= m; j++){
                cin &amp;gt;&amp;gt; board[i][j];
                
                if(&apos;A&apos; &amp;lt;= board[i][j] &amp;amp;&amp;amp; board[i][j] &amp;lt;= &apos;Z&apos;) pos[board[i][j]].push_back({i, j});
                
            }
        }
        
        string keys;
        cin &amp;gt;&amp;gt; keys;
        
        for(char k : keys) key[k] = true;
        
        
        
        
        cout &amp;lt;&amp;lt; solution() &amp;lt;&amp;lt; &quot;\n&quot;;
        
        
        
        
    }
    
    
    
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 미확인 도착지</title><link>https://compy07.github.io/Blog/posts/boj/9370/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/9370/</guid><description>재활 4일차</description><pubDate>Sat, 27 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9370&quot;&gt;미확인 도착지&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;1 ≤ T ≤ 100&amp;lt;br&amp;gt;2 ≤ N ≤ 2000&amp;lt;br&amp;gt;1 ≤ M ≤ 50,000&amp;lt;br&amp;gt;1 ≤ t ≤ 100&amp;lt;br&amp;gt;1 ≤ s,g,h ≤ N, g ≠ h&amp;lt;br&amp;gt;1 ≤ a &amp;lt; b ≤ N&amp;lt;br&amp;gt;1 ≤ d ≤ 1000&lt;/td&gt;
&lt;td&gt;Dijkstra&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;문제를 잘 읽으면 전혀 어려울 것이 없는 문제였습니다.
문제에서 말하는 바를 정확히 이해하면 다익스트라 딸깍 딸깍으로 풀 수 있는데.. 그걸 알아내는데 20분을 날렸답니다 ㅎ하핳&lt;/p&gt;
&lt;p&gt;가는 start에서 g - h, h - g를 거치지 않고 가는 경로가 있더라도 지나가도 괜찮구요..&lt;/p&gt;
&lt;p&gt;그래서 그냥 start에서의 모든 최단 경로, g에서의 모든 최단 경로, h에서의 모든 최단 경로를 구하고&lt;/p&gt;
&lt;p&gt;start에서 g로 가는 cost와 g에서 h로 가는 cost 그리고 h에서 target까지 가는 cost를 더해서
start 최단 경로랑 겹치면 그게 답이다. 이런식으로 답을 도출할 수 있습니다..&lt;/p&gt;
&lt;p&gt;결국은&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if(start[g] + gn[h] + hn[target] == start[target] ||
           start[h] + hn[g] + gn[target] == start[target])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그냥 이거 도출하면 띡하고 나오는.. 문제 ㅠㅜ&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;중간에 깨달았다고 뻘짓해서 ㅠㅜ 더 걸렸슴둥..&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;set&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
#include &amp;lt;unordered_map&amp;gt;
#include &amp;lt;climits&amp;gt;
using namespace std;
using ll = long long;



vector&amp;lt;ll&amp;gt; finder(int start, vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt;&amp;amp; graph, int n){
    vector&amp;lt;ll&amp;gt; visited(n+1);
    for(int i = 1; i &amp;lt; n+1; i++) visited[i] = LLONG_MAX;
    priority_queue&amp;lt;pair&amp;lt;int,int&amp;gt;, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;, greater&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; q;
    visited[start] = 0;
    q.push({0, start});
    while(!q.empty()){
        
        auto [cost, idx] = q.top(); q.pop();
        
        
        for(int i = 0; i &amp;lt; graph[idx].size(); i++){
            auto [next, distance] = graph[idx][i];
            
            if(visited[next] &amp;lt;= distance + cost) continue;
            visited[next] = distance + cost;
            q.push({distance + cost, next});
        }
    }
    
    return visited;
}


void solution(){
    int n, m, t;
    int s, g, h; // 타겟들이 시작한 위치 = s, g랑 h를 이어주는 edge를 지남. 즉, 다시는 여기 방문할 일이 없다 이거임.
    
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; t;
    cin &amp;gt;&amp;gt; s &amp;gt;&amp;gt; g &amp;gt;&amp;gt; h;
    
    
    vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;gt; graph(n+1);// idx, cost
//    vector&amp;lt;pair&amp;lt;int, int&amp;gt; &amp;gt; graph[n+1];// idx, cost
    // 이거 중복 처리가 필요없음.  &quot;교차로 사이에는 도로가 많아봐야 1개이다&quot;
    
    vector&amp;lt;int&amp;gt; targets(t);
    
    for(int i = 0; i &amp;lt; m; i++){
        int a, b, c;
        
        cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; c;
        
        graph[a].push_back({b, c});
        graph[b].push_back({a, c});
    }
    
    for(int i = 0; i &amp;lt; t; i++) cin &amp;gt;&amp;gt; targets[i];
    // 이거는 어떻게 생각하면 되냐면, g랑 h를 둘 다 다익스트라를 돌려서 그냥 후보들 중에서 가까운 놈 고르면 됨.
    // 왜냐면 결국 제일 빠른 길로 가려고는데 왜 멀어지는 길을 택한 것이냐.. 이것도 문제여서
    // 아 근데 이거는 3번 돌려야됨. 왜냐면 시작 위치부터 다 돌려야되는데, 그래서 시작 위치에서 먼 더 코스트가 먼 친구를 골라야되는 것였다.. 아 알았네요.. 이상하게 들어갈 뻔..
    
    
    
    
    // 아 그냥 3개 구해서 하면 되는거였네..
    
    vector&amp;lt;ll&amp;gt; start = finder(s, graph, n);
    vector&amp;lt;ll&amp;gt; gn = finder(g, graph, n);
    vector&amp;lt;ll&amp;gt; hn = finder(h, graph, n);
    
    
    vector&amp;lt;int&amp;gt; result;
    
    for(int target : targets){
        if(start[g] + gn[h] + hn[target] == start[target] ||
           start[h] + hn[g] + gn[target] == start[target]) result.push_back(target);
    }
    
    sort(result.begin(), result.end());
    
    for(int ans : result) cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &quot; &quot;;
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    
    int t;
    cin &amp;gt;&amp;gt; t;
    while(t --&amp;gt; 0){
        solution();
        cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 탈옥</title><link>https://compy07.github.io/Blog/posts/boj/9376/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/9376/</guid><description>재활... 끊기고 다시 시작 1일차.. 근데 많이 어려운 문제를 만나다..</description><pubDate>Sun, 02 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9376&quot;&gt;탈옥&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;2 ≤ h, w ≤ 100&lt;/td&gt;
&lt;td&gt;Dijkstra&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;와.. 문제 드릅게 어렵슨디ㅣㅏ...&lt;/p&gt;
&lt;p&gt;아니 심지어 중간에 질문 게시판 열심히 들어가봤는데도 반례가 안 나와서 오답 코드들 몇 개 보고서 좀 비교를 해서 그냥 새롭게 코드를 작성한 후 AC를 맞았습니다..&lt;/p&gt;
&lt;p&gt;일단 원래는 그냥 죄수들을 기준으로 중복된 문을 없애고 개수 세기였는데 어떤 case에서 안되는 반례가 있나봅니다. 그래서 다익스트라를 각 죄수들과 출구에서부터 들어오는 걸로 총 3번 실행해서 AC를 맞을 수 있엇는데&lt;/p&gt;
&lt;p&gt;아이디어가 진짜 생각해내기 많이 힘들었따....&lt;/p&gt;
&lt;p&gt;솔직히 이런 문제는 좋은 문제인 것 같아요 요런 아이디어 아주 재미잇네요... 근데 어렵네요 허허ㅓ허허허허ㅓ허...&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;풀이 영상&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/_hXgZYjNXOk?si=8nvscn6WcvVxv97v&quot; title=&quot;ps&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
using namespace std;

using ll = long long;

char stateBoard[105][105];
ll board[105][105][3];
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};

struct info {
    ll cost;
    int y;
    int x;
};

bool operator&amp;gt;(const info&amp;amp; a, const info&amp;amp; b) {
    return a.cost &amp;gt; b.cost;
}

void bfs(int idx, pair&amp;lt;int, int&amp;gt; start, int h, int w) {
    priority_queue&amp;lt;info, vector&amp;lt;info&amp;gt;, greater&amp;lt;info&amp;gt;&amp;gt; q;
    
    for (int i = 0; i &amp;lt;= h+1; i++) {
        for (int j = 0; j &amp;lt;= w+1; j++) {
            board[i][j][idx] = 1e15;
        }
    }
    
    board[start.first][start.second][idx] = 0;
    q.push({0, start.first, start.second});
    
    while (!q.empty()) {
        info current = q.top(); q.pop();
        ll cost = current.cost;
        int y = current.y;
        int x = current.x;
        
        if (cost &amp;gt; board[y][x][idx]) continue;
        
        for (int i = 0; i &amp;lt; 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            
            if (ny &amp;lt; 0 || nx &amp;lt; 0 || ny &amp;gt; h+1 || nx &amp;gt; w+1) continue;
            
            if (stateBoard[ny][nx] == &apos;*&apos;) continue;
            
            ll next_cost = cost + (stateBoard[ny][nx] == &apos;#&apos; ? 1 : 0);
            
            if (next_cost &amp;lt; board[ny][nx][idx]) {
                board[ny][nx][idx] = next_cost;
                q.push({next_cost, ny, nx});
            }
        }
    }
}

ll solution(int h, int w, vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt;&amp;amp; starts) {
    for (auto&amp;amp; pos : starts) stateBoard[pos.first][pos.second] = &apos;.&apos;;
    
    bfs(0, {0, 0}, h, w);
    
    bfs(1, starts[0], h, w);
    
    bfs(2, starts[1], h, w);
    
    ll result = 1e15;
    for (int i = 0; i &amp;lt;= h+1; i++) {
        for (int j = 0; j &amp;lt;= w+1; j++) {
            if (stateBoard[i][j] == &apos;*&apos;) continue;
            ll total = board[i][j][0] + board[i][j][1] + board[i][j][2];
            total -= 2 * (stateBoard[i][j] == &apos;#&apos;);
            result = min(result, total);
        }
    }
    
    return result;
}

int main() {
    ios_base::sync_with_stdio(0); cin.tie(0);

    int t;
    cin &amp;gt;&amp;gt; t;
    while (t-- &amp;gt; 0) {
        int h, w;
        cin &amp;gt;&amp;gt; h &amp;gt;&amp;gt; w;
        
        for (int i = 0; i &amp;lt;= h+1; i++) {
            for (int j = 0; j &amp;lt;= w+1; j++) stateBoard[i][j] = &apos;.&apos;;
        }
        
        vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; starts;
        
        for (int i = 1; i &amp;lt;= h; i++) {
            for (int j = 1; j &amp;lt;= w; j++) {
                cin &amp;gt;&amp;gt; stateBoard[i][j];
                if (stateBoard[i][j] == &apos;$&apos;) starts.push_back({i, j});
            }
        }
        
        cout &amp;lt;&amp;lt; solution(h, w, starts) &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 1의 개수 세기</title><link>https://compy07.github.io/Blog/posts/boj/9527/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/9527/</guid><description>아오... 요런 문제는 정말... 허헣 재활 7일차</description><pubDate>Fri, 07 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9527&quot;&gt;1의 개수 세기&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;1 ≤ A ≤ B ≤ 10^16&lt;/td&gt;
&lt;td&gt;Dynamic Programming, Bit Masking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;두 자연수 A, B가 주어졌을 때, A ≤ x ≤ B를 만족하는 모든 x에 대해 x를 이진수로 표현했을 때 1의 개수의 합을 구하는 프로그램을 작성하시오.
즉, f(x) = x를 이진수로 표현했을 때 1의 개수라고 정의하고, 아래 식의 결과를 구하자:&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; style=&quot;font-size: 36px;&quot;&amp;gt;
&amp;lt;munderover accent=&apos;false&apos; accentunder=&apos;false&apos;&amp;gt;
&amp;lt;mo&amp;gt;∑&amp;lt;/mo&amp;gt;
&amp;lt;mrow&amp;gt;&amp;lt;mi&amp;gt;x&amp;lt;/mi&amp;gt;&amp;lt;mo&amp;gt;=&amp;lt;/mo&amp;gt;&amp;lt;mi&amp;gt;A&amp;lt;/mi&amp;gt;&amp;lt;/mrow&amp;gt;
&amp;lt;mi&amp;gt;B&amp;lt;/mi&amp;gt;
&amp;lt;/munderover&amp;gt;
&amp;lt;mi&amp;gt;f&amp;lt;/mi&amp;gt;&amp;lt;mo&amp;gt;(&amp;lt;/mo&amp;gt;&amp;lt;mi&amp;gt;x&amp;lt;/mi&amp;gt;&amp;lt;mo&amp;gt;)&amp;lt;/mo&amp;gt;
&amp;lt;/math&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;와... 오랜만에 많이 막힌 문제였습니다. 허허... 요즘 골드 문제도 이렇게 막힐 수 있다는 것을 알아갑니다..&lt;/p&gt;
&lt;p&gt;일단 시간 초과를 피하기 위해서 dp를 이진법의 bit 별로 개수를 세주고, 그걸 활용하는 식으로 시간을 좀 단축 시켰습니다.&lt;/p&gt;
&lt;p&gt;일단 제가 짠 코드를 이해하려면 이진법에 대해서 알아야 하는데, dp 채우는 코드는 아마 식을 세우다 보면 자연스럽게 얻어지는 식이구요. 위에 solution은&lt;/p&gt;
&lt;p&gt;그냥 현재 들어온 수에 대해서 최상위 비트를 보고있다고 생각하고, 그 하위 비트들은 한 cycle을 돌렸으니까 하나의 확정된 cycle을 더해주고, 그리고 최상위 비트 제외하고 처리합니다.&lt;/p&gt;
&lt;p&gt;이후에는 최상위 비트가 켜져있으니 1을 더해주고요. 그 후에는 하위 비트로 내려가면서 다 처리하는 로직을 따릅니다.&lt;/p&gt;
&lt;p&gt;생각보다 간단한 로직이긴 합니다만... 뭔가 꽤 막혀서 고민을 했던 문제였네요 허허허허허헣...&lt;/p&gt;
&lt;p&gt;언제나 즐거운 PS 되시길 바랍니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = unsigned long long;




ll a, b;
ll memo[100];
ll pow(int up){
    return 1ULL &amp;lt;&amp;lt; up;//current;
}

ll solution(ll current) {
    if (current == 0) return 0;
    
    int size = 0;
    for (int i = 0; i &amp;lt;= 63; i++)
        if (current &amp;amp; (1ULL &amp;lt;&amp;lt; i)) size = i;
    
    
    ll result = 0;
    result = memo[size];
    current = current - (1ULL &amp;lt;&amp;lt; size);
    result += current + 1;
    
    result += solution(current);
    
    return result;
}


int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;
    
    memo[0] = 0;
    memo[1] = 1;
    memo[2] = 4;
            
    
    for(int i = 3; i &amp;lt; 64; i++){ memo[i] = memo[i-1]*2 + pow(i-1);}
    

    cout &amp;lt;&amp;lt; solution(b) - solution(a-1);

    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] N-Queen</title><link>https://compy07.github.io/Blog/posts/boj/9663/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/9663/</guid><description>너무나 유명한 문제 N퀸!!</description><pubDate>Mon, 08 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9663&quot;&gt;N-Queen&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= N &amp;lt;= 15)&lt;/td&gt;
&lt;td&gt;BackTracking(백트래킹)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다.&lt;/p&gt;
&lt;p&gt;N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;일단 이 문제는 정말 단순한 구현문제이다. 그냥 완전탐색에 가지치기 몇 번하면 그냥 풀리는 문제이니 사실 어려움은 없을 것이다.
물론 구현이 가능하면 말이다.&lt;/p&gt;
&lt;p&gt;퀸이 이동 가능한 위치를 알고서, 가로 세로 대각선을 체크만 한다면 너무나 쉬운 문제라 딱히 해설을 하지 않겠다.&lt;/p&gt;
&lt;p&gt;(지금 다른 포스팅을 하고있는데 쓸 포스팅이 고갈되어서 이거 씁니다... ㅠㅜㅜ)&amp;lt;br&amp;gt;
(혹시라도 궁금하시면 아래 코드 함 보시고 생각해보시길.. 별로 좋은 코드는 아닌거 같아여)&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
bool IsSafe(int y, int x, bool arr[15][15], int N, bool* row);
int solution(int n, bool board[15][15], int current, bool* row);
using namespace std;



int result;



int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
    
    int n;
    bool board[15][15] = {false, };
    bool row[15] = {false,};
    
    cin &amp;gt;&amp;gt; n;
    
    cout &amp;lt;&amp;lt; solution(n, board, 0, row);
    
}
void printBoard(bool board[15][15], int n){
    for(int i = 0; i &amp;lt; n; i++){
        for(int j = 0; j &amp;lt; n; j++){
            cout &amp;lt;&amp;lt; board[i][j] &amp;lt;&amp;lt; &quot; &quot;;
        }
        cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
}
int solution(int n, bool board[15][15], int current, bool* row){
    if(current == n) return 1;
    
    int mysum = 0;
    
    for(int i = 0; i &amp;lt; n; i++){
        if(!IsSafe(current, i, board, n, row)) continue;
        board[current][i] = true;
        row[i] = true;
        mysum += solution(n, board, current+1, row);
        board[current][i] = false;
        row[i] = false;
    }
    return mysum;
    
}

bool IsSafe(int y, int x, bool arr[15][15], int N, bool* row){
    if(row[x]) return false;
    
    
    for(int i = 1; i &amp;lt; N; i++){
        if(0 &amp;lt;= y+i &amp;amp;&amp;amp; y+i &amp;lt; N &amp;amp;&amp;amp; 0 &amp;lt;= x+i &amp;amp;&amp;amp; x+i &amp;lt; N &amp;amp;&amp;amp; arr[y+i][x+i]) return false;
        if(0 &amp;lt;= y-i &amp;amp;&amp;amp; y - i &amp;lt; N &amp;amp;&amp;amp; 0 &amp;lt;= x-i &amp;amp;&amp;amp; x-i &amp;lt; N &amp;amp;&amp;amp; arr[y-i][x-i]) return false;
        if(0 &amp;lt;= y+i &amp;amp;&amp;amp; y+i &amp;lt; N &amp;amp;&amp;amp; 0 &amp;lt;= x-i &amp;amp;&amp;amp; x-i &amp;lt; N &amp;amp;&amp;amp; arr[y+i][x-i]) return false;
        if(0 &amp;lt;= y-i &amp;amp;&amp;amp; y - i &amp;lt; N &amp;amp;&amp;amp; 0 &amp;lt;= x+i &amp;amp;&amp;amp; x+i &amp;lt; N &amp;amp;&amp;amp; arr[y-i][x+i]) return false;
    }
    
    return true;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[BOJ] 문자열 폭발</title><link>https://compy07.github.io/Blog/posts/boj/9935/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/boj/9935/</guid><description>Stack하기 참 좋은 날이네..</description><pubDate>Mon, 14 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9935&quot;&gt;문자열 폭발&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TimeLimit&lt;/th&gt;
&lt;th&gt;MemoryLimit&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;TAG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;(1&amp;lt;= N &amp;lt;= 1&apos;000&apos;000, 1 &amp;lt;= Bomb &amp;lt;= 36)&lt;/td&gt;
&lt;td&gt;Stack(스택)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;상근이는 문자열에 폭발 문자열을 심어 놓았다. 폭발 문자열이 폭발하면 그 문자는 문자열에서 사라지며, 남은 문자열은 합쳐지게 된다.&lt;/p&gt;
&lt;p&gt;폭발은 다음과 같은 과정으로 진행된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;문자열이 폭발 문자열을 포함하고 있는 경우에, 모든 폭발 문자열이 폭발하게 된다. 남은 문자열을 순서대로 이어 붙여 새로운 문자열을 만든다.&lt;/li&gt;
&lt;li&gt;새로 생긴 문자열에 폭발 문자열이 포함되어 있을 수도 있다.&lt;/li&gt;
&lt;li&gt;폭발은 폭발 문자열이 문자열에 없을 때까지 계속된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;상근이는 모든 폭발이 끝난 후에 어떤 문자열이 남는지 구해보려고 한다. 남아있는 문자가 없는 경우가 있다. 이때는 &quot;FRULA&quot;를 출력한다.&lt;/p&gt;
&lt;p&gt;폭발 문자열은 같은 문자를 두 개 이상 포함하지 않는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 문제는 사실 전형적인 Stack 문제여서 생각을 많이 하진 않았던 것 같다. 그냥 시간복잡도 보고서 음 괜찮네하고 짰는데 AC를 맞았다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;

string N, bomb;


int main(){
    cin &amp;gt;&amp;gt; N;
    cin &amp;gt;&amp;gt; bomb;
    
    vector&amp;lt;char&amp;gt; stack;
    
    int length = (int) bomb.size();
    for(int i = 0; i &amp;lt; N.size(); i++){
        stack.push_back(N[i]);
        if(stack.size() &amp;gt;= length){
            bool check = false;
            for(int j = 0; j &amp;lt; length; j++){
                if(bomb[length - j - 1] == stack[stack.size() - j - 1]) continue;
                check = true;
                break;
            }
            if(check) continue;
            
            for(int j = 0; j &amp;lt; length; j++) stack.pop_back();
        }
    }
    
    if(stack.empty()) cout &amp;lt;&amp;lt; &quot;FRULA&quot;;
    else{
        for(int i = 0; i &amp;lt; stack.size(); i++) cout &amp;lt;&amp;lt; stack[i];
    }
    
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>한번쯤 들어봤을 그 이름... 디자인패턴(DesignPattern)</title><link>https://compy07.github.io/Blog/posts/designpattern/designpattern/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/designpattern/designpattern/</guid><description>디자인 패턴(DesignPattern)이란?</description><pubDate>Mon, 10 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;개발을 하다보면 한번쯤은 들어봤을 그 이름.. 디자인 패턴&lt;/p&gt;
&lt;h3&gt;도대체 디자인 패턴이란 무엇일까? 그리고 왜 개발에 필요한 것일까?&lt;/h3&gt;
&lt;h2&gt;디자인 패턴이란?&lt;/h2&gt;
&lt;p&gt;프로그램을 설계할 때 발생하는 문제들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 규약(Protocol)형태로 만들어 놓은것입니다. 사실 규약이라고 말한다는 것은 모두가 인정한 &quot;이런 방법으로 이러한 문제를 해결하자&quot;라고 하는 것들을 말한다고 이해하시면 편할 것 같습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
디자인 패턴에는 대표적으로 3가지 카테고리로 나눌 수 있습니다.&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;생성(Creational)&lt;/th&gt;
&lt;th&gt;구조(Structural)&lt;/th&gt;
&lt;th&gt;행동(Behavioural)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Singleton&lt;/td&gt;
&lt;td&gt;Adapter&lt;/td&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Abstract Factory&lt;/td&gt;
&lt;td&gt;Composite&lt;/td&gt;
&lt;td&gt;Interpreter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Factory Method&lt;/td&gt;
&lt;td&gt;Decorator&lt;/td&gt;
&lt;td&gt;Iterator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Builder&lt;/td&gt;
&lt;td&gt;Facade&lt;/td&gt;
&lt;td&gt;Mediator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prototype&lt;/td&gt;
&lt;td&gt;Flyweight&lt;/td&gt;
&lt;td&gt;Memento&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Proxy&lt;/td&gt;
&lt;td&gt;Observer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Template Method&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;각 생성, 구조, 행동 패턴의 3가지로 분류하고 여러가지 패턴이 존재합니다.&lt;/p&gt;
&lt;p&gt;이제 디자인패턴을 배우면서 개발자로서 깔끔하고 효율적으로 설계하는 방법을 터득해 봅시다!
화이팅!&lt;/p&gt;
&lt;p&gt;&amp;lt;!--&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h4&gt;생성(Creational)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Singleton&lt;/li&gt;
&lt;li&gt;Abstract Factory&lt;/li&gt;
&lt;li&gt;Factory Method&lt;/li&gt;
&lt;li&gt;Builder&lt;/li&gt;
&lt;li&gt;Prototype&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;h4&gt;구조(Structural)&lt;/h4&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;h4&gt;행동(Behavioural)&lt;/h4&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;--&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[DesignPattern] 싱글톤(Singleton)</title><link>https://compy07.github.io/Blog/posts/designpattern/singleton/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/designpattern/singleton/</guid><description>디자인 패턴에서 꽤 많이 쓰이는 싱글톤을 소개합니다!!</description><pubDate>Mon, 10 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;싱글톤이란?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to a singular instance.
&amp;lt;br&amp;gt;소프트웨어 공학에서 싱글톤 패턴은 클래스의 인스턴스화를 단일 인스턴스로 제한하는 소프트웨어 설계 패턴입니다.
&amp;lt;br&amp;gt;
출처: 위키피디아&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;싱글톤을 진짜 한 줄로 요약하자면 &quot;단 한번만 생성&quot;입니다.&lt;/h3&gt;
&lt;h3&gt;조금더 구체적으로 설명하면 인스턴스(특정 한 객체)를 한개만 생성하여 사용하는 패턴입니다.&lt;/h3&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
그러면 싱글톤은 언제 사용할까요??&lt;/p&gt;
&lt;p&gt;사용 상황 예시&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;많은 곳에서(in program) 객체에 접근하고, 공유하여 사용할 때&lt;/li&gt;
&lt;li&gt;딱 한개의 인스턴스만 필요한 경우&lt;/li&gt;
&lt;li&gt;등...&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;그러면 왜 사용해요??&lt;/h2&gt;
&lt;p&gt;싱글톤을 사용하면 많은 장점이 있습니다.&lt;/p&gt;
&lt;h3&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;메모리 낭비 방지
&lt;ul&gt;
&lt;li&gt;객체가 딱 한개의 인스턴스만 생성되기 때문에 여러 인스턴스가 올라와 있지 않기 때문에 메모리 측면에서의 이점이 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;속도 ⬆
&lt;ul&gt;
&lt;li&gt;이미 생성된 객체에 접근하기 때문에 접근할 때 비교적 속도가 빠름&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 공유 easy
&lt;ul&gt;
&lt;li&gt;인스턴스가 전역으로 사용되면서, 코드를 짤 때 편하다는 이점이 있음&lt;/li&gt;
&lt;li&gt;!!! 그러나 여러 곳에서 접근할 때 발생하는 동시성 문제를 유의하여야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[Minecraft] 인벤토리 관리</title><link>https://compy07.github.io/Blog/posts/development/bukkit/inventorymanagement/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/development/bukkit/inventorymanagement/</guid><description>마인크래프트 플러그인 인벤토리 편하게 관리하기(입문자용)</description><pubDate>Mon, 07 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note[이 포스트에서는]&lt;/p&gt;
&lt;p&gt;JAVA 언어에 대한 기본적인 이해와 OOP를 코드에 적용할 수 있는 정도의 실력이 기본적으로 요구됩니다.&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;인벤토리를 효율적으로 기능을 넣고, 관리하는 것은 OOP를 이용하면 정말 쉽게 이용할 수 있어요..&lt;/p&gt;
&lt;h2&gt;Interface&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;public interface InventoryHandler {
    void onClick(InventoryClickEvent event);
    void onOpen(InventoryOpenEvent event);
    boolean onClose(InventoryCloseEvent event);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 인터페이스를 정의합니다. 우리는 요 친구를 &amp;lt;code&amp;gt;인벤토리 핸들러&amp;lt;/code&amp;gt;라고 얘기를 할 것이구요&lt;/p&gt;
&lt;p&gt;함수 &amp;lt;code&amp;gt; onClick&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt; onOpen&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt; onClose&amp;lt;/code&amp;gt;의 반환값은 자신이 알아서 뭐 설계하여도 됩니다..&lt;/p&gt;
&lt;p&gt;지금 사용되는 코드는 제가 상점 구현할 때 쓰던 코드를 그대로 가져온 거라서 알아서 가공해서 쓰시면 될 것 같구요.&lt;/p&gt;
&lt;h2&gt;Manager&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;public class InventoryManager {

    public static InventoryManager manager;

    private final Map&amp;lt;Inventory, InventoryHandler&amp;gt; inventories;

    public InventoryManager() {
        if(manager == null) manager = this;
        inventories = new HashMap&amp;lt;&amp;gt;();
    }

    public void RegisterInventory(Inventory inventory, InventoryHandler handler){
        this.inventories.put(inventory, handler);
    }

    public void UnRegisterInventory(Inventory inventory){
        this.inventories.remove(inventory);
    }

    public void HandleClick(InventoryClickEvent event){
        final InventoryHandler handler = this.inventories.get(event.getInventory());
        if(handler == null) return;
        handler.onClick(event);
    }

    public void HandleOpen(InventoryOpenEvent event){
        final InventoryHandler handler = this.inventories.get(event.getInventory());
        if(handler == null) return;
        handler.onOpen(event);
    }

    public void HandleClose(InventoryCloseEvent event){
        final Inventory inventory = event.getInventory();
        final InventoryHandler handler = this.inventories.get(inventory);
        if(handler == null) return;
        if(handler.onClose(event)) this.inventories.remove(inventory);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 메니저를 선언해서 관리하도록 합니다.&lt;/p&gt;
&lt;p&gt;이러면 메모리 관리 측면에서 전체적으로 관리하기 편하구요. 혹시나 어떤 경우에 대해서 핸들링하기 편합니다.&lt;/p&gt;
&lt;p&gt;이렇게 쓰는게 보통적으로 쓰는 기본적인 형태인 것 같아요.&lt;/p&gt;
&lt;h2&gt;Listener&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;public class InventoryListener implements Listener {

    private final InventoryManager inventoryManager;
    public InventoryListener(){
        this(new InventoryManager());
    }

    public InventoryListener(InventoryManager manager){
        this.inventoryManager = manager;
    }

    @EventHandler
    public void onInventoryOpen(InventoryOpenEvent event){
        this.inventoryManager.HandleOpen(event);
    }

    @EventHandler
    public void onInventoryClick(InventoryClickEvent event){
        this.inventoryManager.HandleClick(event);
    }

    @EventHandler
    public void onInventoryClose(InventoryCloseEvent event){
        this.inventoryManager.HandleClose(event);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 기본형으로 쓰다가 또 필요하면 수정하면 되구요. 사실 여기는 딱히 건들일 곳이 없긴 합니다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;갑자기 JAVA를 올린 이유는 아니 Bukkit을 올린 이유는..
보통 코딩을 마인크래프트로 입문을 많이 하더라구요 그래서 입문자용으로 몇개의 활용성있는 방법들과
구현할만한 것들을 공유해드리려고 합니다. 이제부터 천천히 (스케줄링, 핸들링, 이펙트, 스킬, 패킷 관리, 모드-플러그인 통신 등)을 다룰 예정입니다.&lt;/p&gt;
&lt;p&gt;모두 재미있는 개발하세용!!&lt;/p&gt;
</content:encoded></item><item><title>[Minecraft] Region System</title><link>https://compy07.github.io/Blog/posts/development/bukkit/mcage/regionsystem/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/development/bukkit/mcage/regionsystem/</guid><description>마인크래프트 플러그인 구역 관리하기</description><pubDate>Mon, 24 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이제부터 하나의 목표를 잡고서 그걸 하나씩 구현하면서 확장을 해볼 생각입니다.&lt;/p&gt;
&lt;p&gt;간단한 마인크래프트 프로젝트는 바로! 괜찮은 RPG 서버 만들기! 입니다.&lt;/p&gt;
&lt;p&gt;이번에는 월드에딧, 월드가드에서 사용하는 방식을 이용해서 구역을 정의하고, 그에 따라서 이벤트를 넣어서 처리할 수 있도록 구현했습니다.&lt;/p&gt;
&lt;p&gt;코드가 굉장히 많은 관계로 추후에 github에 upload해서 공개하겠습니다.&lt;/p&gt;
&lt;p&gt;이번에는 영상으로 조금 작성하던 영상과 시연 영상을 마무리 하겠습니다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;구현 영상&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;350&quot; src=&quot;https://www.youtube.com/embed/ZHNctQxmMrc?si=F0abuTpjuysK8GSO&quot; title=&quot;Region System&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>[Minecraft] Raycast 구현</title><link>https://compy07.github.io/Blog/posts/development/bukkit/packet/raycasting/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/development/bukkit/packet/raycasting/</guid><description>마인크래프트 플러그인 간단한 raycasting 구현</description><pubDate>Sat, 15 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;또 정말 오랜만에 돌아옵니다. 요즘 Mojo로 작은 프로젝트하고 있어서 좀 뜸하긴 한데요. 일단 오늘은 간단하게 raycasting을 구현해보려고 합니다.&lt;/p&gt;
&lt;p&gt;먼저 Event를 만들어주고, 그 이후에 발생 처리를 해보겠습니다.&lt;/p&gt;
&lt;p&gt;이번 구현은 고정된 곳에서 플레이어가 지나가는지 체크를 하는 로직을 좀 구현하고, 딱히 할 대상이 없으니 현재 같은 월드에 있는 모든 엔티티에 대해서 다 처리를 해서 확인을 해보겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class RaycastEvent extends Event {
    private static final HandlerList handlers = new HandlerList();

    private final Player player;
    private final Entity entity;

    private final Location sourceLocation;
    private final Location targetLocation;


    public RaycastEvent(Player player, Entity entity, Location sourceLocation, Location targetLocation){
        this.player = player;
        this.entity = entity;
        this.sourceLocation = sourceLocation;
        this.targetLocation = targetLocation;
    }

    public Player getPlayer() {
        return player;
    }

    public Entity getEntity() {
        return entity;
    }

    public Location getSourceLocation() {
        return sourceLocation;
    }

    public Location getTargetLocation() {
        return targetLocation;
    }

    @Override
    public HandlerList getHandlers() {
        return handlers;
    }
    public static HandlerList getHandlerList(){
        return handlers;
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;뭐 여기까지는 너무 그냥 당연하게 작성한 것이라 넘어가구요!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class RaycatManager {

    public RaycatManager(){
        register();
    }

    public void register(){
        ProtocolManager manager = ProtocolLibrary.getProtocolManager();

        manager.addPacketListener(new PacketAdapter(BlogPlugin.plugin,
                ListenerPriority.HIGH,
                PacketType.Play.Client.POSITION,
                PacketType.Play.Client.POSITION_LOOK
                ) {



            @Override
            public void onPacketReceiving(PacketEvent event) {
                Player player = event.getPlayer();
                PacketContainer container = event.getPacket();

                double x = container.getDoubles().read(0);
                double y = container.getDoubles().read(1);
                double z = container.getDoubles().read(2);

                tryRaycast(new Location(player.getWorld(), x, y, z), player);
            }
        });
    }


    private void tryRaycast(Location playerLoc, Player player){
        if(playerLoc.getWorld() == null) return;


        Bukkit.getScheduler().runTask(BlogPlugin.plugin, ()-&amp;gt;
        {
            playerLoc.getWorld().getEntities().stream().filter(entity -&amp;gt;
                    entity instanceof LivingEntity &amp;amp;&amp;amp; !entity.equals(player)
            ).forEach(entity -&amp;gt; raycast(playerLoc, player, entity));
        });
    }
    private void raycast(Location loc, Player player, Entity entity){
        if(loc.getWorld() == null) return;

        Location entityLoc = entity.getLocation();

        double distance = loc.distanceSquared(entityLoc);
        if(distance &amp;lt;= 5*5){ 
            Vector dir = entityLoc.getDirection();
            Vector toPlayer = loc.toVector().subtract(entityLoc.toVector()).normalize();

            double angle = Math.toDegrees(dir.angle(toPlayer));

            if(angle &amp;lt;= 45f){


                if(isBlocked(entityLoc, loc)) return;

                Bukkit.getScheduler().runTask(BlogPlugin.plugin, ()-&amp;gt; {
                    RaycastEvent event = new RaycastEvent(player, entity, entityLoc, player.getLocation());
                    Bukkit.getPluginManager().callEvent(event);
                });
            }
        }


    }

    private boolean isBlocked(Location from, Location to){
        from.setY(from.getY() + 1.5f);
        return from.getWorld().rayTraceBlocks(from, to.toVector().subtract(from.toVector()), to.distance(from)) != null;
    }

}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기까지 이렇게 raycast를 구현할 수 있어요. 이것도 게임을 공부하다 보면 당연히 보이는 것들이 있을 거구요!&amp;lt;/br&amp;gt;
&amp;lt;code&amp;gt;from.setY(from.getY() + 1.5f);&amp;lt;/code&amp;gt; 여기는 좀 중요한데... 이게 발가락이 from이 되면서 시야가 너무 낮아지면서 블록이 있는 것으로 알더라구요?! 그래서 이렇게 올려준겁니다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;구현 영상
&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/b7of1RBJn1Q?si=G873YriBzjxSDuCk&quot; title=&quot;raycast&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;이제 곧 리버싱, 암호학, 인공지능 이렇게 올라가긴 할겁니당&lt;/p&gt;
&lt;p&gt;다들 너무 수고하셨습니다.&lt;/p&gt;
</content:encoded></item><item><title>[Minecraft] 플러그인 커스텀 이벤트</title><link>https://compy07.github.io/Blog/posts/development/bukkit/util/customevent/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/development/bukkit/util/customevent/</guid><description>마인크래프트 커스텀 이벤트 만들기(입문)</description><pubDate>Tue, 04 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note[잠시 보고 가세용!]&lt;/p&gt;
&lt;p&gt;이 포스트는 상속을 모르는 상태에서 보면 약간의 혼란이 발생합니다ㅏ..
또한 Bukkit의 Listener, EventListener, Event의 이해가 필요합니다.
:::&lt;/p&gt;
&lt;p&gt;일단 버킷에서는 많은 이벤트를 다 지원해준다.
PlayerInteractEvent, PlayerJoinEvent ... 등등 아주 많이 지원해준다.&lt;/p&gt;
&lt;p&gt;그래서 뭐 딱히 만들 필요없다고 생각할 수 있지만.. 좀 까다롭게 로직을 만들고 싶을 때는 미리 패킷을 받고서 처리하면서 이벤트를 발생시켜 Listener에서 편하게
처리하도록 할 수 있다. 뭐 예를 들어보자&lt;/p&gt;
&lt;p&gt;게임에 보스가 존재하는데 보스를 잡았다면 그 BossDeadEvent가 발생되도록 하여서 잡은 플레이어와 Boss의 종류를 event에 넣어서 보내면 Listener에서 처리할 때 필요한 값들이 다 들어있어서 처리가 편합니다.&lt;/p&gt;
&lt;p&gt;그럼 그걸 어떻게 만들까요??? 한번 화이트 리스트 기능을 좀 이걸 이용해서 만들어 봅시다!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class TestListener implementation Listener{
    @EventListener
    public void onJoin(PlayerJoinEvent e){
        Player player = e.getPlayer();
        
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;public class WhiteListEvent extends Event implementation Cancellable{
    private static final HandlerList handlers = new HandlerList();
    private final Player player;

    private final ServerPlayer npc;
    private boolean cancelled;

    public WhiteListEvent(Player player) {
        this.player = player;
    }

    public Player getPlayer() {
        return player;
    }

    @Override
    public boolean isCancelled() {
        return cancelled;
    }

    @Override
    public void setCancelled(boolean cancel) {
        this.cancelled = cancel;
    }

    @Override
    public HandlerList getHandlers() {
        return handlers;
    }

    public static HandlerList getHandlerList() {
        return handlers;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;저는 이 이벤트를 캔슬 시킬 수 있도록 하려고 했습니다.&lt;/p&gt;
&lt;p&gt;WhiteListManager에서 일단 처내도록 하고 있는데... 이때 다른 곳에서 이런 플레이어는 제외 시키자.. 이렇게 하도록 이요!&lt;/p&gt;
&lt;p&gt;이거는 어떻게 발생시킬 수 있을까요??&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@EventListener
public void onJoin(PlayerJoinEvent e){
    Player player = e.getPlayer();
    Bukkit.getScheduler().runTask(plugin, () -&amp;gt; {Bukkit.getPluginManager().callEvent(new WhiteListEvent(player));});
}

@EventListener
public void onWhiteListPlayerIn(WhiteListEvent e){
    e.getPlayer().kick();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bukkit Scheduler를 호출해서 실행시키는 것은 main thread에서 처리되도록 안전하게 하는 것입니다.&lt;/p&gt;
&lt;p&gt;이렇게 된다면 어떻게 받을 수 있느냐!!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@EventListener
public void onWhiteListPlayerIn(WhiteListEvent e){
    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 사용할 수 있어요!&lt;/p&gt;
&lt;p&gt;그러면 WhiteListManager에서 처리된 플레이어면 cancel시키고 아니면 원래 실행되던 kick이 처리되게 되는거죠!&lt;/p&gt;
&lt;p&gt;:::warning[조심]&lt;/p&gt;
&lt;p&gt;핸들러의 등록 순서에 따라서 결과가 달라질 수 있어요! 영상을 참고해 주세요!
:::&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
구현 영상&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/7LxZp8Ndt84?si=JQS3v9_oTpc0Y0cA&quot; title=&quot;Custom Event&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;여기까지 약간의 튜토리얼 이었습니다.&lt;/p&gt;
</content:encoded></item><item><title>[Minecraft] 플러그인 종속성 관리</title><link>https://compy07.github.io/Blog/posts/development/bukkit/util/dependency/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/development/bukkit/util/dependency/</guid><description>마인크래프트 종속성 관리하는 법(입문)</description><pubDate>Tue, 04 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note[]&lt;/p&gt;
&lt;p&gt;프로토콜립을 시작하기 전 들렀다가는 포스팅입니다
:::&lt;/p&gt;
&lt;p&gt;일단 자신의 플러그인 프로젝트에서 프로토콜립을 사용하고 싶다면?&lt;/p&gt;
&lt;p&gt;gradle, maven으로 셋업을 할 수 있습니다. 아예 파일에 implementation 걸어도 되긴하는데 딱히 이렇게까진 안합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;repositories{
    maven {
        name = &quot;protocol-lib&quot;
        url = &quot;https://repo.dmulloy2.net/repository/public/&quot;
    }
}

dependencies{
    compileOnly &apos;com.comphenix.protocol:ProtocolLib:5.3.0&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 설정을 하면 프로젝트에서 protocollib의 기능들을 사용할 수 있어집니다. 하지만 빌드된 이후에 런타임에서 같은 기능이 돌아가려면
build.gradle 뿐만 아니라 plugin.yml의 정보도 바꿔야 합니다.&lt;/p&gt;
&lt;p&gt;플러그인의 dependency를 설정하기 위해서는 &amp;lt;code&amp;gt; depend&amp;lt;/code&amp;gt; 를 사용하면 됩니다.&lt;/p&gt;
&lt;p&gt;예시&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;depend: [ProtocolLib]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약 다른 플러그인도 같이 넣고 싶다. 그러면 example이라는 플러그인을 넣고 싶다면&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;depend: [ProtocolLib, example]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 할 수 있어요.&lt;/p&gt;
&lt;p&gt;(플러그인 이름을 적으시면 됩니다.)&lt;/p&gt;
</content:encoded></item><item><title>[Minecraft] 메인 인스턴스를 효과적으로 관리하자</title><link>https://compy07.github.io/Blog/posts/development/bukkit/util/maininstance/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/development/bukkit/util/maininstance/</guid><description>마인크래프트 플러그인 나중에 많이 쓰니까 배워두자! 메인 인스턴스 singleton(입문자용)</description><pubDate>Thu, 10 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note[이 포스트에서는]&lt;/p&gt;
&lt;p&gt;JAVA 언어에 대한 기본적인 이해와 OOP를 코드에 적용할 수 있는 정도의 실력이 기본적으로 요구됩니다.&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;버킷에서 &amp;lt;code&amp;gt; anyMethod(Plugin plugin, ...)&amp;lt;/code&amp;gt; 요런 모양의 함수들을 많이 보게 될 것이다.
처음 접하는 사람은 &quot;????????? plugin은 뭐야?&quot; 이럴 텐데 이때 우리는 JavaPlugin 즉 이것을 상속받은 MainClass의 인스턴스를
넘겨주면 되는 것이다.&lt;/p&gt;
&lt;p&gt;아니 근데 이것을 그냥 넘겨줄 것인가? 자 한번 비교해보자&lt;/p&gt;
&lt;p&gt;일단 instance를 사용할 클래스 하나를 만들어 주겠다.&lt;/p&gt;
&lt;p&gt;이름은 &amp;lt;code&amp;gt; ListenerManager&amp;lt;/code&amp;gt;로 &amp;lt;code&amp;gt;EventListener&amp;lt;/code&amp;gt;의 역할을 하도록 해주겠다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ListenerManager implements Listener {

    private Main instance;

    public ListenerManager(Main instance){
        this.instance = instance;
    }

    @EventHandler
    public void onJoin(PlayerJoinEvent event){
        Player p = event.getPlayer();
        for(Player player : Bukkit.getOnlinePlayers()){
            if(player.equals(p)) continue;
            p.hidePlayer(this.instance, player);
        }
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./hide_player.png&quot; alt=&quot;hide&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이렇게 해줌으로써 한 유저가 서버에 접속하게 되면, 다른 유저들에게는 보이지 않게되도록 만들었다.
이때 싱글톤을 사용하지 않은 instance는 지금처럼 하나하나 각 인스턴스마다 연결해줘야하는 엄청나게 불편함이 존재한다.(또 성능적으로도 좋지 않다, 제일 안 좋은 것은 미적으로 안 좋다.)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Override
public void onEnable() {
    PluginManager pm = Bukkit.getPluginManager();
    ListenerManager listenerManager = new ListenerManager(this);
    pm.registerEvents(listenerManager, this);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 작성할 수 있겠다. 그런데 만약 인스턴스를 사용하는 클래스가 많고, 여러곳에서 많이 쓰일때는??&lt;/p&gt;
&lt;p&gt;엄청나게 안 좋은 코딩의 시작인 것이다.&lt;/p&gt;
&lt;p&gt;그래서 우리는 더욱 간단한 방법을 사용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public final class Main extends JavaPlugin{
    private static Main instance;
    @Override
    public void onEnable() {
        instance = this;
        ListenerManager listenerManager = new ListenerManager();
        pm.registerEvents(listenerManager, this);
        
    }
    public static Main getInstance(){
        return instance;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 작성 후 리스너도 바꿔준다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ListenerManager implements Listener {
    @EventHandler
    public void onJoin(PlayerJoinEvent event){
        Player p = event.getPlayer();
        for(Player player : Bukkit.getOnlinePlayers()){
            if(player.equals(p)) continue;
            p.hidePlayer(Main.getInstance(), player);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 해주면 가독성도 높아지고, 아주 좋은 코딩을 할 수 있다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;다음에는 이제 스케쥴링에 대해서 배우도록 하겠다.(with scoreboard)&lt;/p&gt;
</content:encoded></item><item><title>[Minecraft] ProtocolLib 패킷 받기</title><link>https://compy07.github.io/Blog/posts/development/bukkit/util/protocollib/listenpacket/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/development/bukkit/util/protocollib/listenpacket/</guid><description>버킷 프로토콜립 패킷 처리 1(입문)</description><pubDate>Thu, 06 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;https://www.spigotmc.org/resources/protocollib.1997/&quot;&gt;ProtocolLib&lt;/a&gt;은 뭘까?&lt;/h2&gt;
&lt;p&gt;ProtocolLib은 Bukkit에서 처리되는 이벤트나 다른 기능에서 제공되지 않거나, 별로 좋지 않은 기능들을 직접 구현할 수 있게 해주는 강력한 플러그인입니다.
정확히는 마인크래프트 서버, 클라이언트 끼리의 패킷을 읽고 보낼 수 있게 편리한 기능을 제공하는 플러그인이죠!&lt;/p&gt;
&lt;p&gt;플러그인으로 좀 세부적인 것을 만들고 싶다면 거의 필수적으로 사용되는 플러그인인데요!! 그래서 한번 튜토리얼을 만들어 볼까 합니다.&lt;/p&gt;
&lt;p&gt;먼저 &lt;strong&gt;ProtocolLib(이하 &apos;프립&apos;)&lt;/strong&gt; 은 플러그인에서 종속성 설정을 해줘야합니다. 이 설정은 &lt;a href=&quot;https://compy07.github.io/Blog/posts/development/bukkit/util/dependency/&quot;&gt;여기&lt;/a&gt;를 참고해주세요!&lt;/p&gt;
&lt;h2&gt;시작!&lt;/h2&gt;
&lt;p&gt;:::note[Packet]&lt;/p&gt;
&lt;p&gt;사전적 의미는 &apos;네트워크를 통해 전송되는 형식화된 데이터 덩어리&apos;&lt;/p&gt;
&lt;p&gt;사용자 데이터는 &apos;페이로드(payload)&apos;로 알려져 있습니다. 제어정보는 페이로드를 전달하기 위한 정보
:::&lt;/p&gt;
&lt;p&gt;프로토콜립에서 제공하는 패킷 타입은 &lt;a href=&quot;https://github.com/dmulloy2/ProtocolLib/blob/master/src/main/java/com/comphenix/protocol/PacketType.java&quot;&gt;여기서&lt;/a&gt;서 볼 수 있어요!&lt;/p&gt;
&lt;p&gt;이번에는 바로 실습을 진행해 보겠습니다.&lt;/p&gt;
&lt;p&gt;원래 딱 보면 이해되는 것들이 더 많으니까요&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    public ProtocolLibListener(){
        protocolManager = ProtocolLibrary.getProtocolManager();

        register();
    }

    public void register() {
        protocolManager.addPacketListener(new PacketAdapter(BlogPlugin.plugin, ListenerPriority.NORMAL,
                PacketType.Play.Client.USE_ENTITY) {
            @Override
            public void onPacketReceiving(PacketEvent event) {
                PacketContainer packet = event.getPacket();
                Player player = event.getPlayer();
                StructureModifier&amp;lt;Integer&amp;gt; integers = packet.getIntegers();
                if (integers != null &amp;amp;&amp;amp; integers.size() &amp;gt; 0) {
                    int entityId = integers.read(0);

                        Bukkit.getScheduler().runTask(BlogPlugin.plugin, ()-&amp;gt; {
                            Entity entity = getEntityById(entityId, player.getWorld());
                            if(entity == null) return;
                            Location loc = entity.getLocation();
                            loc.setY(loc.getY() + 10);
                            entity.teleport(loc);
                        });
                }
            }
        });
    }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 작성하면 버킷에 프로토콜립의 이벤트 리스너가 등록된겁니다. 굉장히 간단하죠?&lt;/p&gt;
&lt;p&gt;지금 아래 코드에서 좀 잘 봐야할 것은 뭐냐 runTask랑 packet.getIntegers() 정도가 되겠네요&lt;/p&gt;
&lt;p&gt;둘 다 안전하게 처리하기 위해서 사용되는 것들이고요. 이를 통해서 오류 안 터지고 예외 처리도 되고 따로 핸들링도 가능해집니다.
제가 try, catch문을 별로 좋아하는 편은 아니여서 최대한 안 쓰려고 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    private Entity getEntityById(int id, World world){
        return world.getEntities().stream().filter(entity -&amp;gt; entity.getEntityId() == id).findFirst().orElse(null);
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 해서 아이디를 알면 그 월드에 존재하는 엔티티들을 전부 돌면서 하나를 받아올 수 있게 되겠죠&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    public static BlogPlugin plugin;
    @Override
    public void onEnable() {
        plugin = this;
        new ProtocolLibListener();
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;main도 별거 없습니다. 그냥 실행만 시켜주는 거에요!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
구현 영상&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/zBgueOmqyy4?si=HOf5nAHhK-cFvLVN&quot; title=&quot;ProtocolLib Packet - 1&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[DEV] 개발자 관점에서 보는 해양레저아이디어 공모전 후기</title><link>https://compy07.github.io/Blog/posts/development/competition/seaprojectsongdo/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/development/competition/seaprojectsongdo/</guid><description>너무 생각이 짧았다..</description><pubDate>Mon, 06 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;기획서와 발표 자료를 제출해서 본선 17팀(?) 정도에 들었다. 솔직히 처음에 막 &quot;정책&quot;과 관련된 아이디어만 받는 줄 알고서 별로 기대 안 하고 있었는데 이게 돼버렸다.&lt;/p&gt;
&lt;p&gt;그래서 기분 좋은 상태로 대회장으로 이동했따!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./start.jpeg&quot; alt=&quot;홀&quot; /&gt;&lt;/p&gt;
&lt;p&gt;장소는 송도 컨벤시아홀에서 열려서 박람회도 구경할겸 해서 가게 되었다.&lt;/p&gt;
&lt;p&gt;발표는 각 팀들이 나와서 프레젠테이션하고, 질의응답 및 피드백이 이어졌다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./pre.jpeg&quot; alt=&quot;발표&quot; /&gt;
(대회장에서 찍었다고 보내주셨던 분들 감사합니다!)&lt;/p&gt;
&lt;p&gt;발표할 때는 후드짚업을 벗고서 발표를 진행했다. 발표를 하면서 굉장히 떨려서 계속 움직였던게 너무 미스였다. 발표 자체는 매끄럽다고 팀원이 격려해줬는데 솔직히 만족스럽지 않았다.
또한 다른 팀들의 발표를 보니 서비스 개발이 아닌 정책 또는 진짜 어떤 지자체를 targeting해서 발전시키는 아이디어들이었다.
(제품도 있었다. 그러나 아이디어, 구현 X)&lt;/p&gt;
&lt;p&gt;구현한 내용은 파편화 되어있던 서비스들을 하나로 모으는 작업이었고, 이를 구현해냈다. 그러나 근본적인 문제가 있었는데, 질의응답 때 날카롭게 질문 주셨다.
단순히 기능들이 모여진다고 해서 문제가 해결되는가? 이게 실제로 효과가 있는지 어떻게 검증할 수 있는가? 였다.&lt;/p&gt;
&lt;p&gt;물론 다른 성공한 서비스들을 비교하며 우리도 가능하다라는 정도로 설명하긴 했으나 내가 느끼기에도 부족하다고 느꼈다.&lt;/p&gt;
&lt;p&gt;다음부터는 이런 자료나 다른 근거 자료를 더 준비해야겠다는 생각을 머리에 각인했다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;대회 끝나고 열심히 박람회 돌아보면서 놀았따.&lt;/p&gt;
&lt;p&gt;특히 VR이 ㄹㅈㄷ로 재밌었다.&lt;/p&gt;
</content:encoded></item><item><title>[Bridled] 중형 게임 프로젝트 일지 - 1</title><link>https://compy07.github.io/Blog/posts/development/project/bridled/introduce/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/development/project/bridled/introduce/</guid><description>선린 친구들과 함께 가꾸어 나가는 나의 인생</description><pubDate>Thu, 20 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;a href=&quot;https://github.com/brid1ed/client&quot;&gt;Project: Helliowin&lt;/a&gt;&lt;/h1&gt;
&lt;hr /&gt;
&lt;h1&gt;&lt;em&gt;&lt;strong&gt;Bridled 소개&lt;/strong&gt;&lt;/em&gt;&lt;/h1&gt;
&lt;p&gt;귀여운 친구들! 귀엽지 않은 전투!&lt;/p&gt;
&lt;p&gt;탐욕이 가져온 혼란스러운 세상에는 동물들은 사람들의 목표가 되었어요.
갈 곳 없는 자들도, 빛을 보지 못했던 자들도 자신만의 가치관에 맞는 동물처럼 되고 싶어했죠.
사람들은 생각이 맞는 사람들과 모여들기 시작했고, 하나의 종교로서 자리를 잡았어요.&lt;/p&gt;
&lt;p&gt;근데 사람들은 마음에 안드는게 있나봐요. 세계 곳곳에서 다시 싸움의 불씨가 보여요.
그런데 이상하네요? 작은 동물들이 외로운 싸움을 하고 있어요.
우리가 도와줘야 될거 같아요! 우리의 작은 친구 싸움을 멈출 수 있도록 도와주도록 할까요?!&lt;/p&gt;
&lt;h2&gt;Member&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h3&gt;&lt;em&gt;&lt;strong&gt;이정훈&lt;/strong&gt;&lt;/em&gt;&lt;/h3&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;PM, 기획, 개발(클라이언트)&lt;/li&gt;
&lt;li&gt;플레이어, 맵 생성, 몬스터 인공지능&lt;/li&gt;
&lt;li&gt;선린인터넷고등학교 인공지능 동아리 PARA&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h3&gt;&lt;em&gt;&lt;strong&gt;유한별&lt;/strong&gt;&lt;/em&gt;&lt;/h3&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;개발(백엔드, 서버 구축)&lt;/li&gt;
&lt;li&gt;c# 서버 구축, 서버와 클라이언트 통신 구현, 실시간 게임 서버 구현&lt;/li&gt;
&lt;li&gt;선린인터넷고등학교 인공지능 동아리 PARA&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h3&gt;&lt;em&gt;&lt;strong&gt;이강민&lt;/strong&gt;&lt;/em&gt;&lt;/h3&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;개발(클라이언트)&lt;/li&gt;
&lt;li&gt;플레이어 UI, 게임 메뉴 구현, 전투 시스템 구축, 스킬 구현&lt;/li&gt;
&lt;li&gt;선린인터넷고등학교 게임개발 동아리 C,real&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h3&gt;&lt;em&gt;&lt;strong&gt;차윤슬&lt;/strong&gt;&lt;/em&gt;&lt;/h3&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;디자인(캐릭터, 배경, 몬스터, 캐릭터 에니메이션, 스킬, UI 등)&lt;/li&gt;
&lt;li&gt;캐릭터 디자인&lt;/li&gt;
&lt;li&gt;선린인터넷고등학교 게임개발 동아리 C,real&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h3&gt;&lt;em&gt;&lt;strong&gt;서이준루하&lt;/strong&gt;&lt;/em&gt;&lt;/h3&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;디자인(맵, 맵 배치 구성, 아이템, UI 등)&lt;/li&gt;
&lt;li&gt;맵 디자인, 아이템 디자인&lt;/li&gt;
&lt;li&gt;선린인터넷고등학교 게임개발 동아리 C,real&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;img src=&quot;./bg_1.png&quot; alt=&quot;bridled&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>[Go] Go언어 공부 시작해보기</title><link>https://compy07.github.io/Blog/posts/gostudy/first/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/gostudy/first/</guid><description>GO Language의 강력함을 느껴보자</description><pubDate>Thu, 20 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Go란?&lt;/h2&gt;
&lt;h3&gt;&lt;em&gt;빠른 성능, 안정성, 편의성, 쉬운 프로그래밍을 목표로 개발되었으며 범용 프로그래밍 언어입니다.&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;현재 많은 대기업에서 사용하고 있으며, &quot;고루틴&quot;이라는 강력한 기능을 가지고 있는 언어입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;정적 타입, 강 타입&lt;/li&gt;
&lt;li&gt;컴파일 언어&lt;/li&gt;
&lt;li&gt;가비지 컬렉션&lt;/li&gt;
&lt;li&gt;병행성(Concurrency)&lt;/li&gt;
&lt;li&gt;멀티코어 환경 지원&lt;/li&gt;
&lt;li&gt;모듈화 및 패키지 시스템&lt;/li&gt;
&lt;li&gt;빠른 컴파일 속도&lt;/li&gt;
&lt;li&gt;Go 언어의 문법은 C 언어를 기반으로 하고 있으며, C++의 복잡한 문법 대신 간단하고 간결한 문법을 추구하고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;gochart.png&quot; alt=&quot;go language chart&quot; /&gt;
2023년 most used language에 올라온 자료&lt;/p&gt;
&lt;p&gt;위의 자료처럼 go는 생각보다 많이 사용되고 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;구글이 만든 언어 go.... 꼭 써봐야겠쬬!?!&lt;/p&gt;
&lt;p&gt;이제부터 Go를 공부하며 공부하고, 프로젝트 일지를 작성하도록 하겠습니다.&lt;/p&gt;
</content:encoded></item><item><title>[Syntax] Go언어 시작해보자! - Hello Go World!</title><link>https://compy07.github.io/Blog/posts/gostudy/syntax/helloworld/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/gostudy/syntax/helloworld/</guid><description>GO Language의 강력함을 느껴보자</description><pubDate>Sat, 22 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;fmt package란?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;fmt&lt;/strong&gt;는 Formatted I/O(Input / Output)을 구현한 패키지입니다.&lt;/p&gt;
&lt;p&gt;C언어의 stdio.h, C++의 iostream과 비슷한 역할이라고 생각하시면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main // 현재 이 파일의 소속 패키지를 나타냄. / 그러나 main은 프로젝트의 첫 시작, 항상 main으로 존재함.

import (
	&quot;fmt&quot; // fmt 패키지를 import
)

func main() { // 이 패키지에서 제일 먼저 실행되는 함수(main)
	fmt.Println(&quot;Hello Go World!!&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;fmt를 통해서 println으로 &quot;Hello Go World!!&quot;를 출력합니다.&lt;/p&gt;
</content:encoded></item><item><title>[Syntax] Go로 OOP 따라하기</title><link>https://compy07.github.io/Blog/posts/gostudy/syntax/oopdefine/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/gostudy/syntax/oopdefine/</guid><description>GO Language의 강력함을 느껴보자</description><pubDate>Fri, 28 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;go언어는 공식 문서에서도 &quot;OOP이면서, 아니다&quot; 요런식으로 적혀져 있는데요. 솔직히 말해서 이 &quot;go&quot;라는 친구는 참 뭐라 말하기 어렵네요....&lt;/p&gt;
&lt;p&gt;이 친구는 일단 OOP의 4대 특징&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;상속&lt;/li&gt;
&lt;li&gt;다형성&lt;/li&gt;
&lt;li&gt;캡슐화&lt;/li&gt;
&lt;li&gt;은닉화&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 네가지를 모두 가지고 있지 않습니다. Go에서는 &lt;strong&gt;Class&lt;/strong&gt;와 **상속(inheritance)**를 지원하지 않고 있습니다.
그러나 비슷한 기능을 구현할 수 있는 구조체 합성을 통해서 위의 기능을 구현할 수 있는데요. 오늘은 어떻게 OOP처럼 활용할 수 있을지에 대해서 공부하겠습니다.&lt;/p&gt;
&lt;p&gt;먼저 JAVA에서 interface처럼 함수들의 뼈대만 만들어주는 기능을 가지는 &quot;interface&quot;가 Go에도 존재합니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type IShape interface {
    PrintName() string
    GetArea() float64
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;override와 OOP를 보여드리기 위해서 Shape를 Base로 하여서 예시를 들어보겠습니다.&lt;/p&gt;
&lt;p&gt;Shape의 interface를 선언합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
{function_name} {return_data_type}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 식으로 interface의 함수 프로토타입을 선언할 수 있습니다.&lt;/p&gt;
&lt;p&gt;또한 변수를 저장할 컨테이너는 아래와 같이 선언할 수 있는데요?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Shape struct {
	name string
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;struct(구조체)를 통해서 변수를 저정하는 컨테이너를 선언할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;그러면 프로토타입으로 작성됐던 함수를 선언해봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func (s *Shape) PrintName() string {
	return &quot;this shape hasn&apos;t name&quot;
}

func (s *Shape) GetArea() float64 {
	return -1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;*&lt;em&gt;func ({함수 안에서 쓸 변수 이름) &lt;em&gt;(struct_name)} {func_name} {return_data_type})&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이렇게 선언이 가능합니다. 그러면 이제 코드로 옮겨서 실행을 시키고 확인해 봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var shape1 IShape = new(Shape)
var shape2 = new(Shape)
shape3 := new(Shape)
shape4 := Shape{}

fmt.Println(shape1.PrintName(), shape1.GetArea())
fmt.Println(shape2.PrintName(), shape2.GetArea())
fmt.Println(shape3.PrintName(), shape3.GetArea())
fmt.Println(shape4.PrintName(), shape4.GetArea())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같이 선언을 할 수 있습니다. shape 모두 다른 객체지만 결과는 똑같이 나오게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/result_1.png&quot; alt=&quot;reuslt_1&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;&amp;lt;br&amp;gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;그럼 이제 어떻게 활용을 할 수 있을까요?&lt;/p&gt;
&lt;p&gt;먼저 제일 절실하게 기다리셨던 Override를 해봅시다!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Circle struct {
	Shape
	radius float64
}

func (c *Circle) PrintName() string {
	return &quot;Circle&quot;
}

func (c *Circle) GetArea() float64 {
	return math.Pi * c.radius * c.radius
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같이 새로운 구조체를 선언하고, 제일 처음 줄에 상속받을 구조체를 넣으시면 됩니다.
또한 함수 선언도 똑같이 하시면 된답니다.
함수를 더 추가하고 싶으시다면 interface를 또 다시 추가하셔서 진행하실 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var circle Circle = Circle{}
fmt.Println(reflect.TypeOf(circle))
circle.radius = 10
var a IShape = &amp;amp;circle
fmt.Println(reflect.TypeOf(a))
fmt.Println(a.PrintName())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 이렇게 실행을 하게되면, 아래와 같은 결과를 가져옵니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/result_2.png&quot; alt=&quot;result_2&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;이 결과를 통해서 오버라이드까지 해보았습니다.&lt;/p&gt;
&lt;p&gt;이제 Go에 대해서 조금 더 깊이 들어가보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;다음 포스팅에서 뵈어요!!&lt;/p&gt;
</content:encoded></item><item><title>[Syntax] Go언어 시작해보자! - Variable(변수) 선언 및 활용</title><link>https://compy07.github.io/Blog/posts/gostudy/syntax/variablesyntax/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/gostudy/syntax/variablesyntax/</guid><description>GO Language의 강력함을 느껴보자</description><pubDate>Sun, 23 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;언어를 배울때 제일 먼저 배우는 것이 바로 **&quot;자료형(data type)&quot;**이다.&lt;/p&gt;
&lt;p&gt;이제부터 go에서 사용하는 자료형에 대해서 알아보자!!&lt;/p&gt;
&lt;p&gt;먼저 자료형에 대해서 알아본 후, 변수, 상수의 선언 및 초기화에 대해서 다룰 것이다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DataType(자료형)&lt;/th&gt;
&lt;th&gt;Range(범위)&lt;/th&gt;
&lt;th&gt;Description(설명)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;uint&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;부호 X, 32bit(4byte) unsigned integer(정수형)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;uint8&lt;/td&gt;
&lt;td&gt;0~255&lt;/td&gt;
&lt;td&gt;부호 X, 8bit(1byte) unsigned integer(정수형)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;uint16&lt;/td&gt;
&lt;td&gt;0~65535&lt;/td&gt;
&lt;td&gt;부호 X, 16bit(2byte) unsigned integer(정수형)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;uint32&lt;/td&gt;
&lt;td&gt;0~42억&lt;/td&gt;
&lt;td&gt;부호 X, 32bit(4byte) unsigned integer(정수형)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;uint64&lt;/td&gt;
&lt;td&gt;0~(2^64)-1&lt;/td&gt;
&lt;td&gt;부호 X, 64bit(8byte) unsigned integer(정수형)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;부호 O, 32bit(4byte) integer(정수형)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;int8&lt;/td&gt;
&lt;td&gt;-128~127&lt;/td&gt;
&lt;td&gt;부호 O, 8bit(1byte) integer(정수형)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;int16&lt;/td&gt;
&lt;td&gt;-32768~3267&lt;/td&gt;
&lt;td&gt;부호 O, 16bit(2byte) integer(정수형)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;int32&lt;/td&gt;
&lt;td&gt;약 -21억 ~ 21억&lt;/td&gt;
&lt;td&gt;부호 O, 32bit(4byte) integer(정수형)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;int64&lt;/td&gt;
&lt;td&gt;절대값이 0에서 2^64 / 2만큼 떨어진 수&lt;/td&gt;
&lt;td&gt;부호 O, 64bit(8byte) integer(정수형)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;float32&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;32bit 부동소수점, 7자리까지의 정밀도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;float64&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;64bit 부동소수점, 12자리까지의 정밀도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;문자열 저장 자료형(크기는 저장되어있는 문자열에 따라 다름)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;uintptr&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;uint와 같은 크기 &amp;amp;&amp;amp; pointer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;0(false), 1(true) 표현,  이상하게도 8bit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;byte&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;8bit 자료형&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rune&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;unicode를 저장하기 위한 자료형, int32와 동일&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이러한 단순한 변수를 저장하는 방법은 &apos;var&apos;을 사용하여 선언할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var {variable_name} {data_type}

//example
var a int
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같은 방식으로 선언하는 방법이 있으며, 자료형을 생략하는 방법 또한 있다&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var {variable_name} = {data}

//example
var a = 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약 선언과 동시에 초기화하지 않으면, 오류가 발생하니 무조건 선언과 동시에 초기화를 진행해야 자료형 없이 바로 선언이 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{variable_name} := {data}

//example
a := 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이것또한 선언과 동시에 초기화 시켜주면 변수로 인식되어 선언 및 초기화가 된다.
(제일 많인 쓰는 유형)&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
만약 여러개의 변수를 한번에 초기화하고 싶다면?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a, b, c, d := 1, 2, 3, 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::note[참고]
변수를 여러개 선언하고 값을 할당할 때는 반드시 선언하는 변수와 할당하는 &lt;strong&gt;값의 개수가 같아야&lt;/strong&gt; 하며 &lt;strong&gt;타입은 같지 않아도 괜찮습니다.&lt;/strong&gt;
:::&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;변수 말고 상수를 선언하고 싶다면?&lt;/p&gt;
&lt;p&gt;전역으로 선언할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const(
    MAX int = 10000000
    MIN int = 0
)

func main(){
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 식으로 사용이 가능하다!!&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;:::tip[정리]&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;변수 선언
&lt;ul&gt;
&lt;li&gt;var {variable_name} {data_type}&lt;/li&gt;
&lt;li&gt;var {variable_name} = {data}&lt;/li&gt;
&lt;li&gt;{variable_name} := {data}&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;상수 선언
&lt;ul&gt;
&lt;li&gt;전역으로 선언한다.&lt;/li&gt;
&lt;li&gt;const(...) 안에 넣어서 상수로 선언 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동시 선언
&lt;ul&gt;
&lt;li&gt;{var1}, {var2}, ...., {varN} = {data1}, {data2}, ...., {dataN}&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;개수는 좌항과 우항이 같아&lt;/strong&gt;야하지만, &lt;strong&gt;자료형이 같을 필요는 없다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::&lt;/p&gt;
</content:encoded></item><item><title>[Mojo] Mojo? 인공지능 시대에 걸맞는 언어</title><link>https://compy07.github.io/Blog/posts/mojo/introduce/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/mojo/introduce/</guid><description>MOJO에 대해서 설명해 드려요 아주 쉽게</description><pubDate>Fri, 31 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;What is Mojo?&lt;/h1&gt;
&lt;p&gt;Mojo는 최근 인공지능(사실 최근은 아니고..) 붐이 일어남에 따라 인공지능 개발을 타겟팅하여 나온 언어로 현재 인공지능 쪽으로 많이 사용되는 파이썬의 대체 언어로 나오게된 언어입니다.&lt;/p&gt;
&lt;h1&gt;Why Mojo?&lt;/h1&gt;
&lt;p&gt;일단 MOJO를 알기 위해서는 조금 Python(이하 파이썬)에 대해서 알 필요가 있어요&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; TMI &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;일단 Mojo(이 포스트에서는 &apos;모조&apos;라 하겠음)는 과거에 파이썬을 넘어서 인공지능 전용 언어로써 엄청난 활약을 하게 될 것이다.
이런 식으로 나왔던 언어라서 저도 베타를 신청하고 먼저 써보기도 하였는데요. 일단 문법이 파이썬과 비슷하다고 하나
잘 적응되지 않았기도 하고, ide 등과 같은 환경이 잘 되어있지 않아서 사용하지 않았었는데요? 이제는 제대로 한번 해보려고 다시 가져왔습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;:::note[python]&lt;/p&gt;
&lt;p&gt;일단 파이썬은 읽기 쉽고 배우기 쉬운 언어를 목표로 제작되었어요. 특히 파이썬은 Data Science, AI 분야에 정말 많이 쓰입니다.
하지만 굉장히 유명하게도 파이썬은 인터프리터 언어이기 때문에 실행 속도가 컴파일 언어에 비해서 상대적으로 느리다는 단점이 있죠.
그래서 현재까지는 많은 개발자들이 요 속도 문제 때문에 성능 이슈가 발생하는 무거운 로직 등 이런 부분들을 C++로 작성하고 Python에서 불러와 사용하는 방식을 채택하고 있는 것으로 알고 있어요.
근데 요즘 들어서 파이썬의 속도 문제가 많이 개선되고 있다고는 합니다.&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;&quot;파이썬은 지금 굉장히 많이 쓰이고, 특히 인공지능 분야에서 정말 많이많이많이 쓰인다!&quot; 이게 중요한 겁니다.&lt;/p&gt;
&lt;p&gt;그래서 이러한 상황에 놓여있는데 여기서 모조는!?
아래의 것들을 챙겼어요&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Python과의 호환성
&lt;ol&gt;
&lt;li&gt;기존 파이썬 코드를 그대로 사용 가능&lt;/li&gt;
&lt;li&gt;파이썬 문법과 비슷함&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;시스템 레벨 성능
&lt;ol&gt;
&lt;li&gt;모조는 LLVM 기반으로 컴파일 됨
&lt;ol&gt;
&lt;li&gt;C++에 준하는 성능을 낼 수 있음(이론상 &lt;a href=&quot;https://devshi-bambhaniya.medium.com/what-is-mojo-mojo-90-000-times-faster-than-python-already-surpassing-17-000-stars-b8c37050a304&quot;&gt;최대 파이썬의 90000배&lt;/a&gt;[&lt;s&gt;최대 파이썬의 35000배&lt;/s&gt;] )&lt;/li&gt;
&lt;li&gt;특히 메모리 관리 및 병렬 처리에 뛰어난 성능&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;p&gt;다음 포스팅부터는 모조 문법부터 시작해서 제가 해보고 싶었던 것들을 쭈욱 올려볼 에정입니다.&lt;/p&gt;
</content:encoded></item><item><title>[Mojo] Get started Mojo</title><link>https://compy07.github.io/Blog/posts/mojo/start/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/mojo/start/</guid><description>Mojo를 시작해보자(Mac한테만 친절함 주의)</description><pubDate>Fri, 31 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Get Started Mojo&lt;/h1&gt;
&lt;p&gt;https://docs.modular.com/mojo/manual/get-started/&lt;/p&gt;
&lt;p&gt;여기를 따라서 다운로드 하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 간단한 다운로드(Mac) &amp;lt;/summary&amp;gt;
제가 Mac 유저라서 이정도만 작성하고 내려가겠습니다. (2025.01.31 한국 시간 기준)&lt;/p&gt;
&lt;p&gt;:::tip[magic]&lt;/p&gt;
&lt;p&gt;python과 mojo를 포함해서 여러 언어에 대한 Package manager이면서 virtual environment manager
:::&lt;/p&gt;
&lt;p&gt;만약에 여러분들의 mac에 &apos;magic&apos;이 없으면 아래의 commandline을 실행시켜 주세요!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -ssL https://magic.modular.com/deb187f1-a0a3-417f-aeee-d387b7638991 | bash 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이후에 source로 적용시켜 주시구요.&lt;/p&gt;
&lt;p&gt;저는 shell을 Zsh로 사용하고 있어서 아래의 커맨드 라인을 사용하구요. 혹시나 bash shell 이용하시면 &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt;로 입력해서 적용할 수 있어요.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이후에 확인을 해볼까요?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;magic --help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 적용이 잘 되었죠?&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;그러면 이제 프로젝트를 만들어봅시다. 일단 모조 프로젝트를 만들 곳으로 움직여 봅시다.&lt;br /&gt;
이후에 init을 해봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;magic init life --format mojoproject
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이러면 &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; 처서 확인해보면 현재 디렉토리에 life라는 디렉토리가 생성된 것을 볼 수 있습니다.
그럼 life 들어가서 확인해보면 안에 gitignore도 있고 여러가지가 있는데요&lt;/p&gt;
&lt;p&gt;&amp;lt;code&amp;gt; ls -A &amp;lt;/code&amp;gt;로 확인해보면&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.gitattributes
.gitignore
.magic
magic.lock
mojoproject.toml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 구성되어있을 겁니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 구성 요소 설명 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;h2&gt;프로젝트 매니페스트 파일 (mojoproject.toml)&lt;/h2&gt;
&lt;p&gt;프로젝트의 핵심이 되는 &lt;code&gt;mojoproject.toml&lt;/code&gt; 파일은 모든 프로젝트 설정의 중심점 역할을 합니다. Python의 requirements.txt나 Node.js의 package.json과 유사한 이 파일에는 다음과 같은 정보들이 포함됩니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;프로젝트 의존성(dependencies)&lt;/li&gt;
&lt;li&gt;프로젝트 특성 및 설정&lt;/li&gt;
&lt;li&gt;기타 프로젝트 관련 메타데이터&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;의존성 잠금 파일 (magic.lock)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;magic.lock&lt;/code&gt; 파일은 프로젝트의 의존성 상태를 정확하게 추적합니다. 이 파일은 다음과 같은 정보를 포함합니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;설치된 패키지의 정확한 버전&lt;/li&gt;
&lt;li&gt;패키지들 간의 의존성 관계&lt;/li&gt;
&lt;li&gt;전이 의존성(transitive dependencies)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;중요&lt;/strong&gt;: 이 파일은 절대 직접 수정하지 마세요. &lt;code&gt;mojoproject.toml&lt;/code&gt;을 수정하면 &lt;code&gt;magic&lt;/code&gt; 명령어가 자동으로 업데이트합니다.&lt;/p&gt;
&lt;h2&gt;가상 환경 디렉토리 (.magic)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.magic&lt;/code&gt; 디렉토리는 프로젝트의 격리된 개발 환경을 제공합니다. 이 디렉토리에는:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;conda 가상 환경&lt;/li&gt;
&lt;li&gt;프로젝트별 패키지 설치&lt;/li&gt;
&lt;li&gt;환경 설정 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;가 포함되어 있습니다.&lt;/p&gt;
&lt;h2&gt;Git 관련 파일&lt;/h2&gt;
&lt;p&gt;프로젝트는 버전 관리를 위한 기본 설정도 제공합니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.gitignore&lt;/code&gt;: 버전 관리에서 제외할 파일 목록&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.gitattributes&lt;/code&gt;: Git 속성 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;이제 프로젝트가 올바르게 설정되었는지 확인을 해볼까요?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;magic run mojo --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 요 명령어를 통해서 설정된 가상환경에 들어가게 됩니다. 그리고 현재 설치된 모조 버전도 확인시켜 줍니다.&lt;/p&gt;
&lt;p&gt;:::note[참고]&lt;/p&gt;
&lt;p&gt;이번에 따라오시면서 option으로 &amp;lt;code&amp;gt; --format mojoproject &amp;lt;/code&amp;gt;을 넣고 프로젝트를 생성했다면, magic PM에서 자동으로 mojo를 포함하는 &lt;a href=&quot;https://docs.modular.com/max/intro/&quot;&gt;MAX&lt;/a&gt; 패키지를 의존성으로 알아서 추가합니다.
:::&lt;/p&gt;
&lt;p&gt;그러면 가상환경에 들어가는 것까지 해보죠.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;magic shell 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;h1&gt;Hello, Mojo world!&lt;/h1&gt;
&lt;p&gt;국룰부터 해보고 들어가자!&lt;/p&gt;
&lt;p&gt;(일단 모조는 main 함수를 꼭 필요로 합니다.)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fn main():
    return
def main():
    return
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 함수는 두가지 방식으로 선언할 수 있어요.(물론 함수는 하나의 고유한 이름만으로 사용합니다.)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def main():
    print(&quot;Hello, Mojo world!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자 이렇게 되었어요.&lt;/p&gt;
&lt;p&gt;그러면 이걸 실행해 봐야겠죠?&lt;/p&gt;
&lt;p&gt;일단 가상환경에 들어가있지 않다면 현재 터미널을 가상환경에 접속시켜 봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;magic shell 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 현재 Hello, Mojo world!를 적은 .mojo 파일을 실행하기 위해서는&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mojo $그파일이름.mojo 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;로 실행시킬 수 있어요! 그러면&lt;br /&gt;
바로 결과를 확인할 수 있어용!&lt;/p&gt;
&lt;p&gt;만약에 내가 build를 하고 싶다!?!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mojo build $그파일이름.mojo 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 빌드한 후 &amp;lt;code&amp;gt; $그파일이름&amp;lt;/code&amp;gt; 으로 저장되는데 그냥 &amp;lt;code&amp;gt; ./$그파일이름 &amp;lt;/code&amp;gt;으로 실행시킬 수 있어요!&lt;/p&gt;
&lt;p&gt;여기까지 다운로드 및 빠른 시작하기 튜토리얼있구요!&lt;br /&gt;
다음부터는 문법을 쭉 살펴보도록 하죠!&lt;/p&gt;
&lt;h2&gt;Reference&amp;lt;br&amp;gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.modular.com/mojo/manual/get-started/&quot;&gt;Mojo docs&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>[Mojo] Function(Part 1)</title><link>https://compy07.github.io/Blog/posts/mojo/tutorial/function/p1/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/mojo/tutorial/function/p1/</guid><description>Part 1</description><pubDate>Fri, 07 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Mojo의 function system&lt;/h1&gt;
&lt;h2&gt;1. function define(def vs. fn)&lt;/h2&gt;
&lt;h2&gt;1.1 def&lt;/h2&gt;
&lt;p&gt;먼저 모조는 기본적으로 파이썬과의 호환성을 챙기고 있으므로 파이썬에서 사용하는 방식을 그대로 사용할 수 있습니다&lt;/p&gt;
&lt;p&gt;그래서 &amp;lt;code&amp;gt; def&amp;lt;/code&amp;gt; keyword를 이용해서 함수를 정의할 수 있습니다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def sum(a, b):
    return a+b 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 정의할 수 있습니다. 물론 명시적으로 타입을 적어서 더 안전하게 처리할 수도 있죠&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def sum1(a: Int32, b: Int32) -&amp;gt; Int32:
    return a + b

def sum2(a: Int32, b: Int32):
    return a + b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 안되는 모양 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def sum3(a, b) -&amp;gt; Int32:
    return a+b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;code&amp;gt; error: cannot implicitly convert &apos;object&apos; value to &apos;SIMD[int32, 1]&apos; return a+b&amp;lt;/code&amp;gt;&lt;/p&gt;
&lt;p&gt;이거를 보면 알 수 있듯이 return datatype이 정해져 있어서 object로 들어오는 a, b의 type을 변환하는데 이때 모조가
암시적 타입 변환(implicit type conversion)을 못해서 터지는 오류입니다
&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;뭐 어떻게든지 선언할 수 있어요&lt;/p&gt;
&lt;p&gt;그래서 이걸 좀 정리를 해보자면&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인자 타입을 명시하지 않아도 됨&lt;/li&gt;
&lt;li&gt;리턴 타입을 명시하지 않아도 됨&lt;/li&gt;
&lt;li&gt;타입을 명시하지 않은 인자는 &amp;lt;code&amp;gt;object&amp;lt;/code&amp;gt; 타입으로 처리&lt;/li&gt;
&lt;li&gt;인자는 가변적(mutable)&lt;/li&gt;
&lt;li&gt;객체 타입 인자는 참조로 전달&lt;/li&gt;
&lt;li&gt;다른 선언된 타입의 인자는 값으로 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇게 됩니다!&lt;/p&gt;
&lt;h2&gt;1.2 fn&lt;/h2&gt;
&lt;p&gt;&amp;lt;code&amp;gt; fn&amp;lt;/code&amp;gt;같은 경우에는 좀 많이 엄격합니다. &amp;lt;code&amp;gt;def&amp;lt;/code&amp;gt;의 경우 선택적이었던 타입 명시를 강제하고 있거든요!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fn sum1(a: Int32, b: Int32) -&amp;gt; Int32:
    return a + b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게만! 사용할 수 있어요&lt;/p&gt;
&lt;p&gt;바로 또 정리를 해봅시다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;모든 인자의 타입을 명시해야 함&lt;/li&gt;
&lt;li&gt;리턴 타입을 명시해야 함 (값을 반환하지 않는 경우 제외)&lt;/li&gt;
&lt;li&gt;기본적으로 인자는 불변 참조로 전달&lt;/li&gt;
&lt;li&gt;오류를 발생시키는 경우 raises 키워드를 명시해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇게 정리할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. 함수의 활용&lt;/h1&gt;
&lt;p&gt;지금부터는 &amp;lt;code&amp;gt; fn&amp;lt;/code&amp;gt; 키워드를 주로 사용하겠습니다.&lt;/p&gt;
&lt;h2&gt;2.1 함수 인자&lt;/h2&gt;
&lt;h3&gt;2.1.1 선택적 인자&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;fn pow(base: Int, exp: Int = 2) -&amp;gt; Int:
    return base ** exp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 기본값을 지정할 수 있음.&lt;/p&gt;
&lt;h3&gt;2.1.2 키워드 인자&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;var value_case_1 = pow(base=3, exp=5) 

var value_case_2 = pow(exp=3, base=5) 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;호출을 이렇게 활용할 수도 있습니다.&lt;/p&gt;
&lt;h3&gt;2.1.3 가변 인자&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;fn sum(*values: Int) -&amp;gt; Int:
    var sum: Int = 0
    for value in values:
        sum = sum + value
    return sum
fn main():
    var value_case_1 = sum(10)
    print(sum(1, 2, 3))  # -&amp;gt; 6
    print(sum(10, 20, 30, 40))  # -&amp;gt; 100
    print(value_case_1) # -&amp;gt; 10
    return
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 활용할 수 있어요!&lt;/p&gt;
&lt;h3&gt;2.1.4 키워드 전용 인자&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;fn kw_only_args(a1: Int, a2: Int, *, double: Bool) -&amp;gt; Int:
    var product = a1 * a2
    if double:
        return product * 2
    else:
        return product
fn kw_only_args2(a1: Int, a2: Int, *, multi: Bool, amount: Int) -&amp;gt; Int:
    var product = a1 * a2
    if not multi: return product
    return product ** (amount+1)
fn main():
    print(kw_only_args(1, 2, double=True))
    print(kw_only_args2(1, 2, multi=True, amount = 3))
    return 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 보면 중간에 &amp;lt;code&amp;gt;*&amp;lt;/code&amp;gt;가 중요한데 이 기호 뒤에오는 모든 매개변수가 키워드 전용 인자라고 말하는 겁니다&lt;/p&gt;
&lt;p&gt;이때 키워드를 직접 명시하지 않는다면?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;print(kw_only_args2(1, 2, True, amount = 3))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게되면 오류가 터지게 됩니다.&lt;/p&gt;
&lt;h3&gt;2.1.5 함수 오버로딩&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;fn add(x: Int, y: Int) -&amp;gt; Int:
    return x + y

fn add(x: String, y: String) -&amp;gt; String:
    return x + y 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::note[오버로딩]&lt;/p&gt;
&lt;p&gt;나온 김에 정리하고 갑시다.(원래 저는 자바를 먼저 사용했기에..)&lt;/p&gt;
&lt;p&gt;같은 이름의 메서드를 여러 개 정의할 수 있게 해주는 기능인데,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    // 정수 두 개를 더하는 메서드
    public int add(int a, int b) {
        return a + b;
    }
    
    // 실수 두 개를 더하는 메서드
    public double add(double a, double b) {
        return a + b;
    }
    
    // 정수 세 개를 더하는 메서드
    public int add(int a, int b, int c) {
        return a + b + c;
    } 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자바에서는 이런 식으로 예시를 들 수 있습니다.&lt;/p&gt;
&lt;p&gt;조건은&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;메서드 이름이 같아야 함&lt;/li&gt;
&lt;li&gt;매개변수의 개수나 타입이 달라야 함&lt;/li&gt;
&lt;li&gt;반환 타입은 오버로딩을 구분하는 기준이 되지 않음!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 되겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fn add(x: Int, y: Int) -&amp;gt; Int:
    return x + y

fn add(x: Int, y: Int, z: Int) -&amp;gt; Int:
    return x + y + z

fn add(x: Float32, y: Float32) -&amp;gt; Float32:
    return x + y

fn add(x: String, y: String, delimiter: String) -&amp;gt; String:
    return x + delimiter + y

fn main():
    print(add(5, 3))                    # 두 정수의 합: 8
    print(add(1, 2, 3))                 # 세 정수의 합: 6
    print(add(3.14, 2.86))              # 두 실수의 합: 6.0
    print(add(&quot;Hello&quot;, &quot;World&quot;, &quot; &quot;))   # 구분자를 포함한 문자열 연결: &quot;Hello World&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식으로 사용할 수 있겠습니다!&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h2&gt;2.2 오류 처리&lt;/h2&gt;
&lt;h3&gt;2.2.1 오류를 발생시키는 함수&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def raises_error():
    raise Error(&quot;This is the error&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;fn handle_error():
    try:
        raises_error()
    except e:
        print(&quot;Handled an error,&quot; e) 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;code&amp;gt;fn&amp;lt;/code&amp;gt; 함수에서 오류를 발생시키려면 &amp;lt;code&amp;gt;raises&amp;lt;/code&amp;gt; 키워드를 사용해야 합니다. &amp;lt;code&amp;gt;raises&amp;lt;/code&amp;gt; 키워드가 없는 &amp;lt;code&amp;gt;fn&amp;lt;/code&amp;gt; 함수는 오류를 전파할 수 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def divide(a: Int, b: Int) -&amp;gt; Float64:
    if b == 0:
        raise Error(&quot;0으로 나눌 수 없습니다!&quot;)
    return a / b

fn safe_divide(a: Int, b: Int) -&amp;gt; Float64:
    try:
        return divide(a, b)
    except e:
        print(&quot;나눗셈 오류 발생:&quot;, e)
        return 0.0
fn main():
    print(safe_divide(10, 0)) # 나눗셈 오류 발생: 0으로 나눌 수 없습니다! \n 0.0
    print(safe_divide(10, 1)) # 10.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 활용할 수 있겠네요!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;포스트가 길어지고, 추가로 다뤄야할 내용이 있어서 파트를 나눠서 하도록 하겠습니다!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://compy07.github.io/Blog/posts/mojo/tutorial/function/p2/&quot;&gt;go to Part 2&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Reference&amp;lt;br&amp;gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.modular.com/mojo/manual/functions&quot;&gt;Mojo docs&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>[Mojo] Function(Part 2)</title><link>https://compy07.github.io/Blog/posts/mojo/tutorial/function/p2/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/mojo/tutorial/function/p2/</guid><description>Part 2</description><pubDate>Tue, 11 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Mojo의 function system&lt;/h1&gt;
&lt;h2&gt;&lt;a href=&quot;https://compy07.github.io/Blog/posts/mojo/tutorial/function/p1/&quot;&gt;Part 1&lt;/a&gt;&lt;/h2&gt;
&lt;h2&gt;2.3 Return&lt;/h2&gt;
&lt;h3&gt;2.3.1 기본 반환&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def helloWorld() -&amp;gt; String:
    return &quot;Hello, World!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;기본적으로 값은 소유권이 이전된 상태로 호출자에게 반환&lt;/li&gt;
&lt;li&gt;반환 타입으로의 암시적 변환이 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.3.2 참조 반환(Returning a Reference)&lt;/h3&gt;
&lt;p&gt;참조 반환은 값의 복사본을 만들지 않고서 원본 데이터에 대한 접근을 제공하는 방법입니다&lt;/p&gt;
&lt;p&gt;함수는 가변 또는 불변 참조를 반환할 수 있는데요 조금 더 들어가보죠!&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;불변 참조(Immutable Reference)&lt;/li&gt;
&lt;li&gt;가변 참조(Mutable Reference)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 두가지가 있습니다.&lt;/p&gt;
&lt;h4&gt;2.3.2.1 불변 참조(Immutable Reference)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;fn get_number(numbers: List[Int]) -&amp;gt; Int:
    return numbers[0]

fn main():
    var numbers = List[Int](0,0,0,0)
    numbers[0] = 42

    var num_ref = get_number(numbers)
    print(num_ref)  # 출력: 42
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;지금 이 코드를 보게 된다면 코드에서 get_number를 호출할 때, 그 함수 안에서는 numbers를 수정할 수 없습니다. 이게 불변 참조입니다.&lt;/p&gt;
&lt;h4&gt;2.3.2.2 가변 참조(Mutable Reference)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;fn get_number(mut numbers: List[Int], to: Int) -&amp;gt; Int:
    numbers[0] = to
    return numbers[0]

fn main():
    var numbers = List[Int](0,0,0,0)
    numbers[0] = 42

    var num_ref = get_number(numbers, 100)
    
    print(num_ref)  # 출력: 100
    print(numbers[0]) # 출력: 100 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 &amp;lt;code&amp;gt; mut&amp;lt;/code&amp;gt; 키워드를 사용해서 가변 참조를 할 수 있게 합니다. 그러면 함수 안에서 바꾼 내용이 밖에서도 유지가 됩니다.&lt;/p&gt;
&lt;p&gt;사용 목적을 좀 찾아봤더니?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;불변 참조: 데이터를 안전하게 공유하고 읽을 때&lt;/li&gt;
&lt;li&gt;가변 참조: 원본 데이터를 수정해야 할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇다고 합니다.&lt;/p&gt;
&lt;h3&gt;2.3.3 명명된 결과값(Named Results)&lt;/h3&gt;
&lt;p&gt;이동, 복사가 불가능한 값을 반환할 때 사용합니다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ImmovableObject:
    var name: String

    fn __init__(out self, owned name: String):
        self.name = name^

def create_immovable_object(owned name: String, out obj: ImmovableObject):
    obj = ImmovableObject(name^)
    obj.name += &quot;!&quot;
    # 요 obj는 반환됩니다. out으로 나가는건데요 저게 main의 my_obj에 들어갑니다.
def main():
    my_obj = create_immovable_object(&quot;test!&quot;)
    print(my_obj.name) 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 안되는 예시 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
def create_immovable_object2(owned name: String) -&amp;gt; ImmovableObject:
    obj = ImmovableObject(name^)
    obj.name += &quot;!&quot;
    return obj^ # Error: 복사나 이동 안되기 때문에 오류남.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; 인자 규칙을 사용하여 초기화되지 않은 변수를 지정&lt;/li&gt;
&lt;li&gt;함수는 하나의 &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; 인자만 가질 수 있음&lt;/li&gt;
&lt;li&gt;명시적 &amp;lt;code&amp;gt;return&amp;lt;/code&amp;gt; 문이 필요하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2.4 매개변수(parameter) 및 인자(argument)&lt;/h2&gt;
&lt;h3&gt;2.4.1 가변 인자&lt;/h3&gt;
&lt;h4&gt;2.4.1.1 동종 가변 인자(Homogeneous Variadic Arguments)&lt;/h4&gt;
&lt;p&gt;같은 타입의 입력들을 이렇게 연속적으로 입력할 때 받는 것입니다. 파이썬을 해보셨다면 굉장히 익숙하실 겁니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fn sum(*values: Int) -&amp;gt; Int:
  var sum: Int = 0
  for value in values:
    sum = sum+value
  return sum
def main():
    print(sum(1,2,3,4,5,6,67))
    return
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 사용도 가능하다! 생각보다 많이 쓰는 방법&lt;/p&gt;
&lt;p&gt;:::tip[tip]&lt;/p&gt;
&lt;p&gt;아직까지는 모조가 전부 개발된 것도 아니여서, &amp;lt;code&amp;gt;Homogeneous Variadic Arguments&amp;lt;/code&amp;gt;를 사용할 때, 아직 타입에 따라서 처리하는 방법이 다르고 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Inside the function body, the variadic argument is available as an iterable list for ease of use. Currently there are some differences in handling the list depending on whether the arguments are register-passable types (such as Int) or memory-only types (such as String) 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;code&amp;gt;Register-passable types&amp;lt;/code&amp;gt;은 iterate할 수 있다는데, 그니까 int같은 친구들이요. 그런데 String 이런 애들은 안된데요.&lt;/p&gt;
&lt;p&gt;근데 이런 차이점을 줄여갈 것이라고 써 놨으니 기대해 봅시다.
:::&lt;/p&gt;
&lt;p&gt;그러면 String은 뭐 어떻게 해야될까요?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def make_worldly(mut *strs: String):
    for i in strs:
        i[] += &quot; world&quot;

fn main():
    var str1: String = &quot;hello&quot;
    var str2: String = &quot;hi&quot;
    try: make_worldly(str1, str2)
    except e: pass
    print(str1)
    print(str2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 사용이 가능합니다!&lt;/p&gt;
&lt;h4&gt;2.4.1.2  이질 가변 인자 (Heterogeneous Variadic Arguments)&lt;/h4&gt;
&lt;p&gt;이질 가변 인자... 어.. 이게 번역을 어떻게 해야 될까요..? 동질의 반대말이라서 그냥 이렇게 적는데&lt;/p&gt;
&lt;p&gt;&amp;lt;code&amp;gt;Heterogeneous Variadic Arguments&amp;lt;/code&amp;gt;은 같은 타입이 아닌 애들도 같이 입력으로 받아버리는 겁니다. 그래서 타입 안전성을 챙깁니다.&lt;/p&gt;
&lt;p&gt;그대신 여러 인자 타입들을 한번에 처리해되기 때문에 일반화하는 코드나 이런 것들이 요구됩니다.&lt;/p&gt;
&lt;p&gt;독스에서는 특성과 매개변수가 필요하다고 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def count_many_things[*ArgTypes: Intable](*args: *ArgTypes):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;기본적으로 생김새는 이렇게 생겼습니다.&lt;/p&gt;
&lt;p&gt;여기서 두가지 중요한 것을 쪼개서 보도록 하죠&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&amp;lt;code&amp;gt;[*ArgTypes: Intable]&amp;lt;/code&amp;gt; - 인자 리스트
&lt;ul&gt;
&lt;li&gt;&amp;lt;code&amp;gt;ArgTypes&amp;lt;/code&amp;gt;는 타입들의 리스트를 나타냄&lt;/li&gt;
&lt;li&gt;&amp;lt;code&amp;gt;Intable&amp;lt;/code&amp;gt;은 정수로 변환 가능한 타입이라는게 제약조건&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&amp;lt;code&amp;gt;(*args: *ArgTypes)&amp;lt;/code&amp;gt; - 인자 리스트
&lt;ul&gt;
&lt;li&gt;익숙한 &amp;lt;code&amp;gt;*args&amp;lt;/code&amp;gt; 문법을 사용&lt;/li&gt;
&lt;li&gt;각 인자의 타입은 &amp;lt;code&amp;gt;ArgTypes&amp;lt;/code&amp;gt; 리스트에 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;def count_many_things[*ArgTypes: Intable](*args: *ArgTypes):
    var total = 0
    @parameter
    fn add[Type: Intable](value: Type):
        total += Int(value)
    args.each[add]()
    return total
fn main() raises:
    # 다양한 숫자 타입을 섞어서 호출할 수 있습니다
    var result1 = count_many_things(5, 11.7, 12)
    print(result1)  # 출력: 28

    # 단일 타입만 사용할 수도 있습니다
    var result2 = count_many_things(1, 2, 3, 4)
    print(result2)  # 출력: 10

    # Float 값들도 자동으로 Int로 변환됩니다
    var result3 = count_many_things(1.1, 2.7, 3.2)
    print(result3)  # 출력: 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 사용할 수 있구요.&lt;/p&gt;
&lt;p&gt;여기서는 모조가 자동으로 타입을 인식해서 &amp;lt;code&amp;gt;ArgTypes&amp;lt;/code&amp;gt; 리스트를 생성합니다.
&amp;lt;code&amp;gt;args&amp;lt;/code&amp;gt;는 &amp;lt;code&amp;gt;VariadicPack&amp;lt;/code&amp;gt;으로 제공되어, &amp;lt;code&amp;gt;each()&amp;lt;/code&amp;gt; 메서드로 순회할 수 있습니다&lt;/p&gt;
&lt;p&gt;각 인자마다 &amp;lt;code&amp;gt;add&amp;lt;/code&amp;gt; 함수가 호출되며, 적절한 타입과 값이 전달됩니다
예를 들어서 위의 코드에서 result1을 보도록 합시다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;첫 번째 호출: Type=Int, value=5
두 번째 호출: Type=Float64, value=11.7
세 번째 호출: Type=Int, value=12
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 되겠네요!&lt;/p&gt;
&lt;p&gt;근데 여기서 조금 나아가서 최적화를 해봅시다.
만약에 단일 인자로 들어오는 경우가 많다면, 아래 코드처럼 최적화가 가능합니다!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fn print_string(s: String):
    print(s, end=&quot;&quot;)

fn print_many[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts):
    print_string(String(first))

    @parameter
    fn print_elt[T: Stringable](a: T):
        print_string(&quot; &quot;)
        print_string(String(a))
    rest.each[print_elt]() 
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;단일 인자 호출 시 &amp;lt;code&amp;gt;VariadicPack&amp;lt;/code&amp;gt; 생성을 건너뛸 수 있음&lt;/li&gt;
&lt;li&gt;첫 번째 인자는 직접 처리되어 더 효율적&lt;/li&gt;
&lt;li&gt;나머지 인자들만 &amp;lt;code&amp;gt;VariadicPack&amp;lt;/code&amp;gt;으로 처리&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;어때요 굉장히 엄청나죠!?&lt;/p&gt;
&lt;h3&gt;2.4.2 가변 키워드 인자&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;fn print_nicely(**kwargs: Int) raises:
    for key in kwargs.keys():
        print(key[], &quot;=&quot;, kwargs[key[]])

fn main() raises:
    print_nicely(a=7, y=8)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;가변 키워드 인자는 항상 &amp;lt;code&amp;gt;owned&amp;lt;/code&amp;gt; 인자 규칙으로 취급&lt;/li&gt;
&lt;li&gt;모든 가변 키워드 인자는 동일한 타입&lt;/li&gt;
&lt;li&gt;인자 타입은 &amp;lt;code&amp;gt;CollectionElement&amp;lt;/code&amp;gt; trait를 준수&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.4.2.1 위치 전용 인자(Positional-only Arguments)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;fn min(a: Int, b: Int, /) -&amp;gt; Int:
    return a if a &amp;lt; b else b 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;는 위치 전용 인자이다. 라는 것을 나타냅니당.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fn min(a: Int, b: Int, /) -&amp;gt; Int:
    return a if a &amp;lt; b else b
fn main() raises:
    result = min(5, 3)  # 정상 작동
    result = min(a=5, b=3)  # 컴파일 에러!
    print(result) 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그래서 이렇게 활용됩니다.&lt;/p&gt;
&lt;p&gt;:::note[왜 사용할까?]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;의미가 명확해짐&lt;/strong&gt; 왜냐하면 min에서는 작은 값을 반환하는데 그 순서가 중요하지 않습니다. 그래서 그냥 &lt;strong&gt;&apos;비교&apos;&lt;/strong&gt; 한다는 것이 더 직관직이겠죠?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;인터페이스 안전성&lt;/strong&gt; 이거는 확실한 장점인데 뭐 입력 파라미터의 이름을 바꾸더라도 이후 다른 곳에서 오류가 안 터지겠죠? 아주 좋습니다.
:::&lt;/p&gt;
&lt;h4&gt;2.4.2.2 키워드 전용 인자 (Keyword-only Arguments)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;fn sort(*values: Float64, ascending: Bool = True):
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;fn sort(*values: Float64, ascending: Bool = True) -&amp;gt; List[Float64]:
    var sorted_list = List[Float64]()

    for i in values: sorted_list.append(i)


    for i in range(len(sorted_list)):
        for j in range(len(sorted_list) - 1 - i):
            if ascending:
                if sorted_list[j] &amp;gt; sorted_list[j + 1]:
                    sorted_list[j], sorted_list[j + 1] = sorted_list[j + 1], sorted_list[j]
            else:
                if sorted_list[j] &amp;lt; sorted_list[j + 1]:
                    sorted_list[j], sorted_list[j + 1] = sorted_list[j + 1], sorted_list[j]

    return sorted_list
fn main() raises:
    var sorted = sort(1.0, 2.0, 3.0, 4.0, 5.0, 67.0, 0.0, ascending=True)
    for i in range(len(sorted)):
        print(sorted[i]) 
    
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;code&amp;gt;ascending&amp;lt;/code&amp;gt;이 없어도 &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;로 들어가구요 직접 지정해서 &amp;lt;code&amp;gt;False&amp;lt;/code&amp;gt;로 바꿀 수 있어요.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sort(1.1, 2.2, 3.3, False)  # 컴파일 에러! 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 호출은 불가능합니다!&lt;/p&gt;
&lt;p&gt;:::note[왜 사용할까?]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;명확한 의도  표현&lt;/strong&gt; 정렬로 예시를 든 것처럼 오름차순, 내림차순의 여부는 정렬에 있어서 굉장히 중요합니다. 이때 이렇게 명시하는 것이 코드 의도가 확 보이겠죠?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;안전성과 유연성&lt;/strong&gt; 나중에 새로운 키워드를 추가하더라도 기존 코드가 바뀌지 않습니다. 아주 좋죠?
:::&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;여기까지 기본적인 코드에 대해서 알아봤구요. 담부터는 struct를 배우고 활용으로 pygame과 합께 적용시켜 보도록 하겠습니다!&lt;/p&gt;
&lt;h2&gt;Reference&amp;lt;br&amp;gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.modular.com/mojo/manual/functions&quot;&gt;Mojo docs&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>[Mojo] Variables</title><link>https://compy07.github.io/Blog/posts/mojo/tutorial/variable/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/mojo/tutorial/variable/</guid><description>Mojo를 시작해보자</description><pubDate>Sat, 01 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Mojo의 변수 선언과 사용&lt;/h1&gt;
&lt;p&gt;:::tip[mojo setup]&lt;/p&gt;
&lt;p&gt;Go to &lt;a href=&quot;https://compy07.github.io/Blog/posts/mojo/start/&quot;&gt;MojoSetup&lt;/a&gt;
:::&lt;/p&gt;
&lt;h2&gt;1. 변수의 종류&lt;/h2&gt;
&lt;p&gt;Mojo에서는 모든 변수가 가변적(mutable)이며, 두 가지 방식으로 변수를 선언할 수 있습니다&lt;/p&gt;
&lt;h3&gt;1.1 명시적 변수 선언 (Explicitly-declared variables)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;var&lt;/code&gt; 키워드를 사용하여 변수를 선언합니다. 타입 어노테이션을 포함할 수 있습니다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var a = 5
var b: Float64 = 3.14
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;초기화 없이 선언 가능: &amp;lt;code&amp;gt;var value: Float64&amp;lt;/code&amp;gt;&lt;/li&gt;
&lt;li&gt;렉시컬 스코핑(lexical scoping) 따름&lt;/li&gt;
&lt;li&gt;타입 어노테이션 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.2 암시적 변수 선언 (Implicitly-declared variables)&lt;/h3&gt;
&lt;p&gt;할당 문장으로 직접 변수를 생성합니다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = 5
b = 3.14 
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;함수 레벨 스코핑&lt;/li&gt;
&lt;li&gt;첫 할당값의 타입으로 변수 타입이 결정됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 타입 시스템&lt;/h2&gt;
&lt;h3&gt;2.1 강타입 시스템 (Strong typing)&lt;/h3&gt;
&lt;p&gt;:::tip[지식]&lt;/p&gt;
&lt;p&gt;모든 변수는 강타입입니다
:::&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;생성 시 타입이 결정되며 변경 불가&lt;/li&gt;
&lt;li&gt;다른 타입의 값 할당 시 에러 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;count = 1        # count는 Int 타입
count = &quot;One&quot;  # 에러: StringLiteral을 Int로 변환 불가 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.2 암시적 타입 변환 (Implicit conversion)&lt;/h3&gt;
&lt;p&gt;:::tip[지식]&lt;/p&gt;
&lt;p&gt;일부 타입은 다른 타입으로부터의 암시적 변환을 지원
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var test: Float64 = 50  # Int에서 Float64로 자동 변환
print(test)  # 출력: 50.0 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 스코프 규칙&lt;/h2&gt;
&lt;h3&gt;3.1 var 키워드 사용 시 (렉시컬 스코프)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def lexical_scopes():
    var num = 1
    if num == 1:
        var num = 2     # 새로운 변수 생성 (shadowing)
        print(num)      # 2 출력
    print(num)          # 1 출력 (외부 스코프 변수) 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 암시적 선언 시 (함수 레벨 스코프)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def function_scopes():
    num = 1
    if num == 1:
        num = 2        # 함수 레벨 변수 수정
        print(num)     # 2 출력
    print(num)         # 2 출력 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 타입 어노테이션 (Type Annotations)&lt;/h2&gt;
&lt;h3&gt;4.1 기본 사용법&lt;/h3&gt;
&lt;p&gt;타입 어노테이션을 사용하면 변수의 타입을 명시적으로 지정할 수 있습니다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var name: String = get_name()
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;var 키워드와 함께 사용해야 함&lt;/li&gt;
&lt;li&gt;코드의 가독성과 의도를 명확히 전달&lt;/li&gt;
&lt;li&gt;함수 반환값의 타입을 모르더라도 변수 타입을 명시 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.2 단일 인자 생성자를 가진 타입의 초기화&lt;/h3&gt;
&lt;p&gt;:::tip[지식]&lt;/p&gt;
&lt;p&gt;단일 인자 생성자를 가진 타입은 두 가지 방식으로 초기화 가능
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var name1: String = &quot;Sam&quot;      # 타입 어노테이션 사용 
var name2 = String(&quot;Sam&quot;)      # 직접 생성자 호출
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 지연 초기화 (Late Initialization)&lt;/h2&gt;
&lt;p&gt;:::tip[지식]&lt;/p&gt;
&lt;p&gt;타입 어노테이션을 사용하면 변수를 나중에 초기화 가능
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fn my_function(x: Int):
    var z: Float32              # 타입만 선언
    if x != 0:
        z = 1.0                 # 조건에 따라 초기화
    else:
        z = foo()
    print(z) 
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;초기화되지 않은 변수 사용 시 컴파일 에러 발생&lt;/li&gt;
&lt;li&gt;타입 선언이 반드시 필요&lt;/li&gt;
&lt;li&gt;값 할당 전 사용 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. 암시적 타입 변환의 조건&lt;/h2&gt;
&lt;p&gt;암시적 타입 변환이 가능 조건:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;대상 타입이 @implicit 데코레이터로 표시된 생성자를 가짐&lt;/li&gt;
&lt;li&gt;해당 생성자가 변환하려는 값과 일치하는 단일 필수 인자를 받음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;var number: Float64 = Int(1)   # Int를 Float64로 암시적 변환
print(number)                  # 출력: 1.0 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;==&amp;gt; 아래와 같이 표현할 수 있어요&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var number = Float64(1)        # 직접 생성자 호출
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;함수 호출할 때도 암시적 변환이 적용됩니다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fn take_float(value: Float64):
    print(value)

fn pass_integer():
    var value: Int = 1
    take_float(value)          # Int가 Float64로 자동 변환 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::warning[조심조심]&lt;/p&gt;
&lt;p&gt;일반적으로 암시적 변환은 데이터 손실이 없는 경우에만 지원합니다
:::&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; Function Scope in Mojo &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;h2&gt;함수 레벨 스코프 vs 렉시컬 스코프&lt;/h2&gt;
&lt;p&gt;Mojo에서는 변수 선언 방식에 따라 두 가지 다른 스코프 규칙이 적용됩니다&lt;/p&gt;
&lt;h3&gt;1. 암시적 선언의 함수 레벨 스코프&lt;/h3&gt;
&lt;p&gt;암시적으로 선언된 변수(var 키워드 없이 선언)는 Python과 같은 함수 레벨 스코프를 따릅니다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def demonstrate_function_scope():
    count = 0                    # 함수 레벨에서 변수 선언
    
    if True:
        count = 5               # 같은 변수를 수정
        temp = 10              # 새로운 변수 선언
        print(count)           # 출력: 5
        
    print(count)               # 출력: 5 (if 블록의 변경사항이 유지됨)
    print(temp)                # temp도 접근 가능 (함수 전체가 스코프)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;변수는 함수 전체에서 동일한 스코프를 공유&lt;/li&gt;
&lt;li&gt;내부 블록에서 수정된 값은 함수 전체에 영향&lt;/li&gt;
&lt;li&gt;새로 선언된 변수도 함수 전체에서 접근 가능&lt;/li&gt;
&lt;li&gt;Python 스타일의 변수 스코핑과 동일&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. var 키워드의 렉시컬 스코프&lt;/h3&gt;
&lt;p&gt;반면, var로 선언된 변수는 렉시컬 스코프를 따릅니다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def demonstrate_lexical_scope():
    var count = 0              # 외부 스코프 변수
    var outer = 1
    
    if True:
        var count = 5          # 새로운 변수 생성 (shadowing)
        var inner = 2          # 내부 스코프 변수
        print(count)           # 출력: 5
        print(outer)           # 외부 변수 접근 가능
        
    print(count)              # 출력: 0 (외부 변수는 변경되지 않음)
    # print(inner)            # 에러! inner는 if 블록 밖에서 접근 불가
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;각 블록이 고유한 스코프를 생성&lt;/li&gt;
&lt;li&gt;내부 스코프에서 외부 변수 접근 가능&lt;/li&gt;
&lt;li&gt;같은 이름의 변수 선언 시 shadowing 발생&lt;/li&gt;
&lt;li&gt;내부 스코프의 변수는 해당 블록 밖에서 접근 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 두 스코프 규칙의 실제 응용&lt;/h3&gt;
&lt;p&gt;두 스코프 규칙을 혼합해서 사용할 때&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def mixed_scope_example():
    var x = 0                 # 렉시컬 스코프 변수
    y = 0                     # 함수 스코프 변수
    
    if True:
        var x = 1             # 새로운 x 생성 (shadowing)
        y = 1                 # 기존 y 수정
        var z = 2             # 렉시컬 스코프의 새 변수
        w = 3                 # 함수 스코프의 새 변수
        
    print(x)                  # 출력: 0 (원래 x 유지)
    print(y)                  # 출력: 1 (수정된 y)
    # print(z)               # 에러! z는 if 블록 스코프
    print(w)                  # 출력: 3 (함수 스코프라 접근 가능) 
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;함수 레벨 스코프는 Python 개발자들에게 친숙한 동작을 제공&lt;/li&gt;
&lt;li&gt;렉시컬 스코프는 더 엄격한 변수 관리와 안전성을 제공&lt;/li&gt;
&lt;li&gt;두 스코프 규칙을 상황에 맞게 적절히 선택하여 사용 가능
&amp;lt;/details&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Reference&amp;lt;br&amp;gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.modular.com/mojo/manual/variables&quot;&gt;Mojo docs&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>[Crypto] 공격</title><link>https://compy07.github.io/Blog/posts/security/crypto/basicinfomation/attack/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/crypto/basicinfomation/attack/</guid><description>암호 스키마에 초점을 맞추는 공격 종류와 어떤 것들이 있는지 알아본다.(암호문 단독, 알려진 평문, 선택 평문, 선택 암호문 등)</description><pubDate>Thu, 27 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;암호학에서 몇 가지 공격에 대해서 이야기 해보려고 합니다.&lt;/p&gt;
&lt;p&gt;:::note[이 포스트에서는]
여기서는 암호 스키마에 대응하는 공격에 초점을 맞춰서 다루겠습니다.
많은 종류의 공격이 있으며 심각도 또한 각기 다릅니다!
:::&lt;/p&gt;
&lt;h2&gt;1. 암호문 단독 모델&lt;/h2&gt;
&lt;p&gt;암호문 단독 공격(Ciphertext-Only Attack)은 대다수 사람들이 암호 시스템을 부순다고 이야기할 때, 쓰는 의미입니다.
이 상황에서는 제임스와 소피아가 데이터를 암호화한 후 공격자가 볼 수 있는 것은 암호문 자체뿐!
단지 &lt;strong&gt;암호문 자체로 메시지를 복호화하려는 시도를 암호문 단독 공격&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;가장 정보가 적으므로 가장 어려운 유형의 공격&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;h2&gt;2. 알려진 평문 모델&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;알려진 평문 공격(Known-Plaintext Attack)&lt;/strong&gt; 은 &lt;strong&gt;공격자가 평문과 암호문을 모두 알고있는 공격&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;이 공격의 &lt;strong&gt;최종 목표는 복호키를 찾는 것&lt;/strong&gt;입니다.
이 설명을 읽으면서 이상하진 않으신가요? 어떻게 공격자가 평문을 알 수 있을까?라고,
하지만 통신상 평문을 알아낼 수 있는 상황은 굉장히 많습니다. 때로는 예상하기 쉬운 메시지도 있습니다.&lt;/p&gt;
&lt;p&gt;예를 들어보겠습니다.
제임스가 밥을 먹고있는 동안에 모든 수신 메일에 대해서 &apos;나 밥먹는 중 좀만 기다리셈&apos;이라는 자동 응답 메일을 보낸다고 해봅시다.
공격자는 제임스에게 메일을 보내 자동 응답 메시지 사본을 발송하는데, 이때 메시지가 암호화됩니다. 이제 공격자는 메시지의 평문과 암호문..
둘 모두를 획득합니다.&lt;/p&gt;
&lt;p&gt;공격자가 &lt;strong&gt;키를 발견한다면&lt;/strong&gt; 제임스와 소피아가 &lt;strong&gt;같은 키로 교환한 모든 다른 메시지도 복호화할 수 있습니다.&lt;/strong&gt;
후자는 중요한 문제이고 공격자가 키를 얻기 위해서 일부 평문-암호 쌍을 사용하고, 다른 암호문을 복호호하기 위해서 &lt;strong&gt;해당키를 사용하는 행위를 반복하면 안됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
다른 전형적인 상황을 예시로 들어보겠습니다. 제임스가 공격자를 포함해 &lt;strong&gt;같은 메시지를 대다수 사람에게 보낼 때&lt;/strong&gt;입니다.
공격자는 &lt;strong&gt;평문과 다른 사람에게 보낸 사본의 암호문을 가집니다.&lt;/strong&gt;
제임스와 소피아는 굉장히 중요한 파일을 전송 중일 수도 있습니다. 이 파일이 공개된다면, 공격자는 평문과 암호문을 알 수 있게 되겠죠...&lt;/p&gt;
&lt;p&gt;공격자가 전체 평문을 다 알지 못하더라도 일부를 알아낼 수 있는데요. 이메일은 추정이 가능한 첫 부분과 고정된 서명이 맨 끝에 나옵니다. IP 패킷헤더는 추정하기 매우 쉬워서, 추정 가능한 데이터가 일부 알려진 평문이라면 알려진 평문 공격이 가능한 상황으로 분류가 됩니다.&lt;/p&gt;
&lt;p&gt;알려진 평문 공격은 암호문 단독 공격보다 훨씬 강력합니다. 공격자는 암호문 단독인 경우보다는 더 많은 정보를 얻습니다. 추가 정보 또한 공격자에게 도움이 됩니다.&lt;/p&gt;
&lt;h2&gt;3. 선택 평문 모델&lt;/h2&gt;
&lt;p&gt;다음 단계는 공격자가 평문을 선택할 수 있는 형태인데 알려진 ㅍ여문 공격보다 엄창엄청 강력한 유형의 공격입니다.
이제 공격자는 앙호 시스템을 공격하기 쉽게 특별히 준비된 평문을 선택할 것 입니다. 공격자는 얼마든지 평문을 선택(Chosen-Plaintext)해 해당하는 암호문을 얻을 수 있습니다.
이 공격이 비현실적이라고 생각하시는 분들도 계신데... 비현실적이지 않습니다.&lt;/p&gt;
&lt;p&gt;공격자가 암호화하는 데이터를 선택할 수 있는 상황은 꽤 많이 있습니다.
또 다시 제임스는 상당수 외부 자료(이 외부 자료는 공격자의 영향을 받을 수도 있다.)에서 정보를 얻어 암호화된 형태로 밥에게 그 정보를 포워딩한다.&lt;/p&gt;
&lt;p&gt;예를 들자면, 공격자가 제임스가 소피아에게 포워딩할 것이라는 사실을 알고 바로 그 이메일을 전송한다고 해봅시당.
에잇ㅅ 선택 평문 공격은 아무튼 비현실적인 공격이 절대 아닙니다.
좋은 암호 알고리즘은 선택 평문 공격에도 문제가 생기지 않습니다.
누군가가 본인이 운영하는 시스템과 선택 평문 공격은 관련 없다고 확신한다면 의심해보세용.&lt;/p&gt;
&lt;p&gt;이 공격에는 두 가지 변종이 존재하는데요? 공격자가 암호문을 얻기 전 암호화하려는 평문 전체 리스트를 준비하는 오프라인 공격과, 이미
수신한 암호문을 가지고 새로운 평문을 선택하는 온라인 공격이 있습니다.
대부분의 경우 이 구분은 무시해도 좋습니다. !온라인 공격이 오프라인 공격보다 더 강력한 공격!&lt;/p&gt;
&lt;h2&gt;4. 선택 암호문 모델&lt;/h2&gt;
&lt;p&gt;선택 암호문(Chosen_Ciphertext)란 용어는 부적절한 명칭입니다. 실제로는 선택 암호문 평문 공격이라고 불러야합니다.
그런데 솔직히 너무 길어서 짧게 부른다고 합니다. 선택 평문 공격에서 공격자는 평문 값을 선택합니다. 선택 암호문 공격에서는 평문 값과 암호문 값 모두를 선택합니다.
공격자가 선택하는 모든 평문에 대해 대응하는 암호문을 얻고 공격자가 선택한 각 암호문에 대응하는 평문을 얻습니다.&lt;/p&gt;
&lt;p&gt;공격자가 더 자유로울 수 있다는 점에서 확실히 선택 암호문 공격은 선택 평문 공격보다 훨씬 강력합니다.
목표는 역시 키를 알아내는 것입니다. 키를 이용해 공격자는 다른 암호문을 해독할 수 있습니다. 그만큼 잘 설계된 암호 스키마는 선택 암호문 공격을 방어하는데 문제가 없습니다.&lt;/p&gt;
&lt;h2&gt;5. 특정 공격 목표&lt;/h2&gt;
&lt;p&gt;자 요놈은 꽤 특이한 친구인데요, 1,2,3,4까지는 평문이나 복호화키를 알아내는 것이었습니다.
키를 복구하지 못하지만 공격자가 특정한 메시지를 복호화할 수 있는 공격도 있습니다.
메시지를 복구하지 못하지만 메시지에 대한 일부분 정보를 드러내는 공격도 있습니다.&lt;/p&gt;
&lt;p&gt;예를 들자면, 선택된 10개의 평문과 그에 대응하는 암호문, 그리고 11번째 암호문이 주어졌을 때 대응하는 복호키를 알지 못하더라도 11번째 메시지의 최하위 비트가 0인지 1인지는 알수 있을 겁니다.
무척 다양한 형태의 공격이 있으니, 일일이 다 열거할순 없습니다만 공격자들은 항시 새로운 형태의 공격을 고안해 왔을겁니다(아마).&lt;/p&gt;
&lt;p&gt;그렇다면 어떻게 방어할 수 있을까요?&amp;lt;br&amp;gt;
우리는 특정 공격을 방어하고자 합니다. 특정 공격은 이상적인 암호 스키마와 실제 암호 스키마 사이의 차이점을 탐지하는 어려운 방법입니다.
이 공격은 아직 발견되지 않은 종류의 공격뿐 아니라 지금까지 설명한 모든 공격을 포함합니다. 물론 무엇이 이상적인 스키마인지 정의해야 되겠지만요.&lt;/p&gt;
&lt;h2&gt;6. 다른 형태의 공격(부채널, 생일, meet-in-the-middle 등)&lt;/h2&gt;
&lt;p&gt;이와 관련하여서는 조금 더 자세히 다뤄볼 예정이니 다른 포스팅에 또 뵐게요!&lt;/p&gt;
</content:encoded></item><item><title>[Crypto] 기초지식 - 1</title><link>https://compy07.github.io/Blog/posts/security/crypto/basicinfomation/basic/gcdnmod/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/crypto/basicinfomation/basic/gcdnmod/</guid><description>암호학에 필요한 기초지식 GCD(Greatest Common Divisor), Modular에 대해서 알아본다.</description><pubDate>Fri, 04 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note[이 포스트에서는]&lt;/p&gt;
&lt;p&gt;암호학을 조금이라도 이해하기 위한 기초를 다루는 포스트의 첫번째 단계입니다.&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h1&gt;정수론(기초)&lt;/h1&gt;
&lt;h2&gt;1. 최대공약수(Greatest Common Divisor)&lt;/h2&gt;
&lt;p&gt;최대공약수를 알기 위해서는 &lt;strong&gt;약수(Divisor)&lt;/strong&gt; 를 알아야한다.
약수는 자연수 A, B가 있을 때, 두 수 A, B를 나누어 떨어지게 만드는 수를 말한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;최대공약수는 당연히 그 약수 중 제일 큰 수를 말한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;자! 근데 우리는 A, B가 주어졌을 때 어떻게 최대공약수를 알 수 있을까???&lt;/p&gt;
&lt;p&gt;직관적이게 구현해볼까요??&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a, b = map(int, input().split())
result = []
for i in range(1, max(a, b)+1):
    if a % i == b % i == 0: result.append(i)

print(result[-1])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./GCDB_1.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;
(이미지가 생각보다 많이 작네요..ㅎㅎ)&lt;/p&gt;
&lt;p&gt;자 이렇게 최대공약수를 얻는 코드를 작성해보았습니다.&lt;/p&gt;
&lt;p&gt;그런데 이 코드는 O(max(a, b))의 시간복잡도를 가지고 있죠??
하지만 암호학에서는 진짜 뭔 말도 안되는 큰 수들이 왔다갔다 거립니다...&lt;/p&gt;
&lt;p&gt;이보다 더 빠른 알고리즘을 설계해 볼까요?&lt;/p&gt;
&lt;p&gt;이때 사용할 알고리즘은 무엇이냐! 바로 &lt;strong&gt;유클리드 호제법(Euclidean algorithm)&lt;/strong&gt; 을 사용할 겁니다!&lt;/p&gt;
&lt;p&gt;:::note[유클리드 호제법]&lt;/p&gt;
&lt;p&gt;2개의 자연수(또는 정식) a, b에 대해서 a를 b로 나눈 나머지를 r이라 하면(단, a&amp;gt;b), a와 b의 최대공약수는 b와 r의 최대공약수와 같다.
이 성질에 따라, b를 r로 나눈 나머지 r&apos;를 구하고, 다시 r을 r&apos;로 나눈 나머지를 구하는 과정을 반복하여 나머지가 0이 되었을 때 나누는 수가 a와 b의 최대공약수이다.&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;요런 유클리드 호제법을 구현해볼까?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
def gcd_1(a, b):
    while b != 0: a, b = b, a % b
    return a
def gcd_2(a, b): # 함수 오버헤드 때문에 약간 느릴 수도
    return a if not b else gcd_2(b, a % b)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;유클리드 호제법을 사용한 최대공약수(GCD) 알고리즘의 시간 복잡도는 ${O(\log(\min(a, b)))}$ 이죠? 굉장히 효율적입니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./GCD_seq.png&quot; alt=&quot;순서도&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;:::tip[GCD를 이용한 최소공배수(LCM) 구하기]&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def lcm(a, b):
    return abs(a * b) // gcd(a, b) # overflow를 방지하기 위해서는 a*b를 분리해서 계산하면 됨.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h2&gt;2. 나머지 연산(Modular Arithmetic)&lt;/h2&gt;
&lt;p&gt;나머지.. 우리는 % 연산자를 이용해서 나머지르 가져오죠?
그래서 % 연산을 mod 연산이라고 합니다.&lt;/p&gt;
&lt;p&gt;그래서 이제 뭘 할거냐? 자 여러분 자연수 % 자연수는 너무나 쉽게 할 수 있겠죠..?&lt;/p&gt;
&lt;p&gt;그러나 &lt;strong&gt;-12 % 7&lt;/strong&gt; 은 어떻게 계산하실 거죠??&lt;/p&gt;
&lt;p&gt;우리는 다음과 같은 규칙을 알아야 합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;결과는 항상 0이상이고, 나누는 수(예시에서는 7이죠?) 미만이어야 한다.&lt;/li&gt;
&lt;li&gt;(나누어지는 수) = (몫) * (나누는 수) + (나머지) 의 관계가 성립한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이거는 사실 중학교 때 배우는 것이긴 합니다..&lt;/p&gt;
&lt;p&gt;그래서 -12 % 7의 과정을 얘기해 보겠습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;-12를 7로 나눈 몫을 구한다. -&amp;gt; -1.xxxx (소수점 내림)&lt;/li&gt;
&lt;li&gt;-12 = -2 * 7 + 2&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;2번의 과정이 왜 나오냐!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2 % 7 = 2&lt;/li&gt;
&lt;li&gt;(2+7) % 7 = 2&lt;/li&gt;
&lt;li&gt;(7*2+2) % 7 = 2&lt;/li&gt;
&lt;li&gt;(7*n+2) % 7 = 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;-&amp;gt; 그래서 &lt;em&gt;&lt;em&gt;(7&lt;/em&gt;-2+2) = 2&lt;/em&gt; 이런 답이 나오게 됩니다.&lt;/p&gt;
&lt;p&gt;이때 a % n = b % n인 경우, a와 b는 mod n에 대해여 **&quot;합동&quot;**이라고 합니다.
우리는 이걸 다음과 같게도 나타낼 수 있습니다. a ≡ b % n&lt;/p&gt;
&lt;p&gt;ex) 19 ≡ 10 mod 9입니다. 그러면 19와 10은 9로 나눴을 때의 나머지는 1이기 때문에 요렇게 되지요!&lt;/p&gt;
&lt;h3&gt;이제는 큰 수를 나머지 연산을 취해볼겁니다!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;91^17 % 9&lt;/strong&gt;을 계산해 보자!&lt;/p&gt;
&lt;p&gt;:::tip[계산]&lt;/p&gt;
&lt;p&gt;(91 + 9n) % 9 = 1&amp;lt;br&amp;gt;
n = 0 -&amp;gt; 91 % 9 = 1&amp;lt;br&amp;gt;
91 % 9 = 1 % 9 -&amp;gt; 91 ≡ 1 % 9&amp;lt;br&amp;gt;
91^17 % 9 = 1 ^ 17 % 9 = 1&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;여기서 주목해야 할 것은  91 ≡ 1 % 9인 경우에 91을 1로 치환해도 이 연산이 성립한다는 개념입니다.
또 mod 연산은 분리도 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;a+b mod n = (a mod n + b mod n) mod n&lt;/strong&gt; 이런식으로 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;그래서 우리는 정수에 대한 모듈러 연산의 결과값들의 집합을 다음과 같이 정의할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;$\mathbb{Z}_n={0,1,\ldots,(n-1)}$&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;이번 포스트에서는 여기까지 다루겠습니다.&lt;/p&gt;
</content:encoded></item><item><title>[Crypto] 키(Key)?</title><link>https://compy07.github.io/Blog/posts/security/crypto/basicinfomation/key_public_private/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/crypto/basicinfomation/key_public_private/</guid><description>Cryptography(암호학)에서 필요한 어휘를 정리해드립니다.Public Key, Private Key와 사용 예시에 대해서 배웁니다.</description><pubDate>Tue, 18 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;기초 어휘&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;송신자
&lt;ul&gt;
&lt;li&gt;수신자에게 전달할 내용, 전달 방식 등을 결정하는 주체&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수신자
&lt;ul&gt;
&lt;li&gt;송신자가 보낸 메시지의 의미를 해석하고 평가하는 주체&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;공개키(Public key)
&lt;ul&gt;
&lt;li&gt;사람들에게 공개된 키이며 정보를 암호화 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;비밀키(Private key)
&lt;ul&gt;
&lt;li&gt;사용자만 알고 있는 암호를 풀 수 있는 키
&amp;lt;/details&amp;gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;1. 대칭키&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;암호화, 복호화 할 때 사용하는 &lt;strong&gt;키가 동일&lt;/strong&gt;(&lt;strong&gt;1개의 키 사용&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;송신자가 A라는 키로 암호화 하였다면, 수신자도 A라는 키를 가지고 있어야 복호화(&lt;strong&gt;같은 키를 이용&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;2. 비대칭키&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;암호화 할 때 사용하는 키와 복호화할 때 사용하는 &lt;strong&gt;키가 다른 경우&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2개의 키를 사용&lt;/strong&gt;하며, 개인키(private key)와 공개키(개인키를 기반으로 생성) 쌍을 이룬 형태&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;3. 사용예&lt;/h1&gt;
&lt;p&gt;:::tip[각각이 중점을 두는 것은?]
Public Key로 암호화 하면 Data 보안에 중점&amp;lt;br&amp;gt;
Private Key로 암호화 하면 인증 과정에 중점
:::&lt;/p&gt;
&lt;h2&gt;Public Key로 암호화 하는 경우&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;송신자가  공개키로 data를 암호화 하고 전송 -&amp;gt; 수신자는 자신의 개인키로 data를 복호화
&lt;ul&gt;
&lt;li&gt;A 키로 암호화 한다면, B키로 복호화가 가능하고, B키로 암호화를 한다면 A키로 복호화가 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;공개키는 널리 배포될 수 있음 -&amp;gt; 많은 사람들이 한 명의 개인키 소유자에게 data를 보낼 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Private Key로 암호화 하는 경우&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;개인키의 소유자가 개인키로 data를 암호화하고 공개키와 함께 전달&lt;/li&gt;
&lt;li&gt;제공자의 신원을 보장해주기 때문
&lt;ul&gt;
&lt;li&gt;암호화된 data가 공개키로 복호화 된다는 것은 공개키와 쌍을 이루는 개인키에 의해서 암호화 되었다는 것을 의미 -&amp;gt; data 제공자의 신원 확인이 보장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 방법이 공인인증체계의 기본 바탕이 되는 전자 서명&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 자세한 내용보기 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;h1&gt;공개키(public key)와 개인키(private key)&lt;/h1&gt;
&lt;p&gt;먼저 알아가기 전에 &lt;strong&gt;대칭키&lt;/strong&gt;와 &lt;strong&gt;비대칭키(public key)&lt;/strong&gt; 에 대해서 알아야합니다.&lt;/p&gt;
&lt;h2&gt;대칭키란?&lt;/h2&gt;
&lt;p&gt;송신자가 가지고 있는 암호화와 복호화하는 키를 수신자도 똑같이 가지고 있어, 전자문서가 송신자에 의해서 암호화 되어도
수신자 또한 복호화 할 수 잇는 키를 가지고 있기 때문에 복호화가 가능합니다. 여기서 제 3자가 개입하여 전자문서를
가져간다고 하더라도 복호화할 수 있는 키를 가지고 있지 않기 때문에 전자문서를 볼 수 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;여기서 송신자와 수신자 모두 &quot;똑같은&quot; 키를 가지고 있다는 것(복호화 및 암호화가 단 한개의 키로 이루어짐)이다.&lt;/strong&gt;
&amp;lt;br&amp;gt;
&lt;img src=&quot;./public_key_.png&quot; alt=&quot;대칭키&quot; /&gt;
:::note[집중]
대칭키 알고리즘에서는 미리 약속된 대칭키를 사용하지 않을 경우 암호문과 대칭키를 함께 보낼 수 없습니다.&amp;lt;br&amp;gt;
이 때문에 암호문을 전달하는 경로와 대칭키를 전달하는 경로를 다르게하여 전달해야 안전하게 전달할 수 있습니다.
:::&lt;/p&gt;
&lt;p&gt;암호학에서는 &lt;strong&gt;대칭키를 전달하는 것을 &quot;대칭키 교환&quot;&lt;/strong&gt; 또는 &lt;strong&gt;&quot;키 교환&quot;&lt;/strong&gt; 이라고 하며,
적절한 대칭키 교환 방법이 대칭키 알고리즘을 활용하는데 있어 가장 어려운 문제의 하나입니다.&lt;/p&gt;
&lt;h2&gt;비대칭키란?&lt;/h2&gt;
&lt;p&gt;사용하는 키와 복호화 할 때 사용하는 키가 다른 경우를 말합니다.
비대칭키는 타인에게 절대 노출되어서는 안되는 비밀키(private key), 비밀키를 토대로 만든 공개키(public key)가 쌍을 이룬 형태입니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;대칭키&lt;/th&gt;
&lt;th&gt;비대칭키&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;키 관계&lt;/td&gt;
&lt;td&gt;암호화 키 == 복호화 키&lt;/td&gt;
&lt;td&gt;암호화 키 != 복호화 키&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;암호화키&lt;/td&gt;
&lt;td&gt;비밀키&lt;/td&gt;
&lt;td&gt;공개키&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;복호화키&lt;/td&gt;
&lt;td&gt;비밀키&lt;/td&gt;
&lt;td&gt;개인키&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비밀키 전송&lt;/td&gt;
&lt;td&gt;필요&lt;/td&gt;
&lt;td&gt;불필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;키 길이&lt;/td&gt;
&lt;td&gt;짧음&lt;/td&gt;
&lt;td&gt;길음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인증&lt;/td&gt;
&lt;td&gt;곤란&lt;/td&gt;
&lt;td&gt;용이&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;암호, 복호화 속도&lt;/td&gt;
&lt;td&gt;빠름&lt;/td&gt;
&lt;td&gt;느림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;경제성&lt;/td&gt;
&lt;td&gt;높다&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;전자서명&lt;/td&gt;
&lt;td&gt;복잡&lt;/td&gt;
&lt;td&gt;간단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주용도&lt;/td&gt;
&lt;td&gt;고용량 데이터 암호화(기밀성)&lt;/td&gt;
&lt;td&gt;키 교환 및 분, 인증, 부인방지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;* 암호화, 복호화 키 길이가 짧다.&amp;lt;br/&amp;gt;* 구현이 용이하, 암호화, 복호화가 빠르다&amp;lt;br/&amp;gt;* 암호 강도 전환이 용이&amp;lt;br/&amp;gt;* 암호 기능 우수&amp;lt;br/&amp;gt;* 각종 암 시스템의 기본으로 활용&lt;/td&gt;
&lt;td&gt;* 사용자가 증가하더라도 관리해야 할 키의 개수가 상대적으로 적다&amp;lt;br/&amp;gt;* key 전달이나 교환에 적합하다&amp;lt;br/&amp;gt;* 인증과 전자서명에 이용&amp;lt;br/&amp;gt;* 대칭키보다 확장성이 좋다&amp;lt;br/&amp;gt;* 여러가지 분야에서 응용이 가능하다&amp;lt;br/&amp;gt;* 키 변화의 빈도가 적다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;단점&lt;/td&gt;
&lt;td&gt;* 키 교환 원리가 명시되지 않음 -&amp;gt; 키 분배가 어렵다&amp;lt;br/&amp;gt;* 관리할 암호화 복호화 키가 많다 N명 -&amp;gt; N(N-1)/2&amp;lt;br/&amp;gt;* 확장성이 낮다&amp;lt;br/&amp;gt;* 전자서명이 불가능&amp;lt;br/&amp;gt;* 부인방지 기능이 없다&lt;/td&gt;
&lt;td&gt;* 키 길이가 길다&amp;lt;br/&amp;gt;* 복잡한 수학적 연산을 이용함으로 암호화 복호화 속도가 느리다&amp;lt;br/&amp;gt;* 중간에 인종과정이 없으므로 중간자 공격에 취약하다(전자서명, 인증서 등으로 해결)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;에시&lt;/td&gt;
&lt;td&gt;* [Feistel] : SEED, DES&amp;lt;br/&amp;gt;* [SPN] : ARIA, AES, IDEA&amp;lt;br/&amp;gt;* 메시지 인증코드(MAC)&lt;/td&gt;
&lt;td&gt;* Diff-Hellman, RSA, ECC, DAS&amp;lt;br/&amp;gt;* [Block chain] &amp;lt;br/&amp;gt;* [TPM]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
&lt;img src=&quot;./process.png&quot; alt=&quot;과정&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;:::note[]&lt;/p&gt;
&lt;h3&gt;Public Key로 암호화 하면 Data 보안에 중점 &amp;lt;br&amp;gt;&lt;/h3&gt;
&lt;h3&gt;Private Key로 암호화 하면 인증 과정에 중점 &amp;lt;br&amp;gt;&lt;/h3&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h2&gt;Public Key로 암호화 하는 경우&lt;/h2&gt;
&lt;p&gt;송신자가 상대방의 Public key로 data를 암호화 하고 전송하면, 수신자는 자신의 Private key로 data를 복호화 한다.
A 키로 암호화 한다면, B키로 복호화가 가능하고, B키로 암호화를 한다면 A키로 복호화가 가능한 것이다.
Public Key는 널리 배포될 수 있기 때문에 많은 사람들이 한 명의 Private Key 소유자에게 data를 보낼 수 있다.&lt;/p&gt;
&lt;h2&gt;Private Key로 암호화 하는 경우&lt;/h2&gt;
&lt;p&gt;Private Key의 소유자가 Private Key로 data를 암호화하고 Public Key와 함께 전달한다.
이 과정에서 Public Key와 data를 획득한 사람은 Public key를 이용하여 복호화가 가능하다.&lt;/p&gt;
&lt;p&gt;이러한 방법은 data 보호의 목적보다는 public key data 제공자의 신원을 보장해 주어 사용한다.
암호화된 data가 Public Key로 복호화 된다는 것은 Public Key와 쌍을 이루는 Private Key에 의해서 암호화 되었다는 것을 의미한다.
즉 data 제공자의 신원 확인이 보장된다는 것이다.
이 방법이 공인인증체계의 기본 바탕이 되는 전자 서명이라는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;참고: &lt;a href=&quot;https://blog.naver.com/PostView.naver?blogId=chodahi&amp;amp;logNo=221385524980&quot;&gt;Public Key and Private Key&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>[DreamHack] PyPloit</title><link>https://compy07.github.io/Blog/posts/security/crypto/dreamhack/level_1/pyploit/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/crypto/dreamhack/level_1/pyploit/</guid><description>간단한 워게임 풀이</description><pubDate>Mon, 07 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;입력은 저 dict[a] != rs 저게 ??가 잇으면 걍 터져버려서&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while (True):
    menu()
    print(&apos;index: &apos;, end=&apos;&apos;)
    inp = input()

    if inp == &apos;1&apos;:
        print(&apos;Text: &apos;, end=&apos;&apos;)
        a = input()
        rs = trans_enc(a)
        dict[rs] = a
        print(rs)
    elif inp == &apos;2&apos;:
        print(&apos;Code: &apos;, end=&apos;&apos;)
        a = input()
        rs = trans_dec(a)
        print(rs)
        if a in dict:
            if dict[a] != rs:
                print(&apos;ERROR!&apos;)
                pdb.set_trace()

    else:
        exit_()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;¿¡§¶°±¬¢£€ 이런거 못 읽을만한거 넣어서 python gdb 얻었습니다.
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;flag 읽은 후, 그 뒤에 ??? 이 값 두개를 차례대로 공개키, 개인키에 넣고서 flag 복호화&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;key = [65537,
       3459196519943177904674538869459802391686903930008099623840714440954105103866974530427491791539652312167071194984882404537184791492302191598249023703238755689855282318760841260371780027787306307347005882960480731661123451511067907327477975625962114393048577, 5002545618242553738882017165095261691689496951783856857046844572064283202968575616766550367230111513802315744422177460306136186205793078706764455760474880834542508211610934828951364974082674456983498680285836457429726248883571914707670547864655320242561399]
pub1 = key[0]
pub2 = key[2]
priv = key[1]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그럼 flag 나옵니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;다행히 문제가 시간을 별로 안 잡아 먹었네용&lt;/p&gt;
&lt;p&gt;즐거운 ps 아 아니 워게임 되세용!!&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 분석 코드 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


import random
import pdb

ML_A = [1, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107,
        109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
        233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359,
        367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
        499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
        643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787,
        797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
        947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069,
        1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213,
        1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321,
        1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481,
        1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601,
        1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
        1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877,
        1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999]


def Q_mod(x, d, n):
    y = x % n
    r = 1
    bd = bin(d)
    bd = bd[2:len(bd)]
    m = len(bd) - 1
    while (m != -1):
        if bd[m] == &apos;1&apos;:
            r = r * y % n
        y = y ** 2 % n
        m = m - 1
    return r


def ML_test(n, a):
    d = (n - 1) // 2
    while (d % 2 == 0):
        if (pow(a, d, n) == n - 1):
            return True
        d = d // 2
    tmp = pow(a, d, n)
    if tmp == n - 1 or tmp == 1:
        return True
    else:
        return False


def F_test(n):
    a = 3
    if Q_mod(a, n - 1, n) == 1 or n == 3:
        return True
    else:
        return False


def prime_test(n):
    result = True
    if F_test(n):
        for x in ML_A:
            if not ML_test(n, x):
                result = False
                break
            if result:
                return True
    else:
        return False


def extended_euclid(r1, r2, s1=1, s2=0, t1=0, t2=1):
    q = r1 // r2
    s = s1 - s2 * q
    t = t1 - t2 * q
    r = r1 - r2 * q
    if (r == 0):
        res = {&apos;gcd&apos;: r2, &apos;s&apos;: s2, &apos;t&apos;: t2}
        return res
    else:
        return extended_euclid(r2, r, s2, s, t2, t)


def find_d(e, Pn):
    res = extended_euclid(e, Pn)
    r = res[&apos;s&apos;]
    if r &amp;lt; 0:
        r = r + Pn
    return r


def gen_prime():
    N = random.randint(10 ** 127, 10 ** 128 - 1)
    while (not prime_test(N)):
        N = N + 1
    return N


def RSA():
    p = gen_prime()
    q = gen_prime()

    e = 65537

    Pn = (p - 1) * (q - 1)
    n = p * q

    d = find_d(e, Pn)

    return [e, d, n]


def encryption(a, e, n):
    return Q_mod(a, e, n)


def decryption(enc, d, n):
    return Q_mod(enc, d, n)


def enc_int(text):
    result = &apos;&apos;
    for i in range(0, len(text)):
        try:
            enc_code = str(int(text[i].encode(&apos;UTF-8&apos;).hex(), 16))
        except:
            enc_code = str(int(&apos;?&apos;.encode(&apos;UTF-8&apos;).hex(), 16))
        if len(enc_code) &amp;lt; 5:
            enc_code = &apos;0&apos; * (5 - len(enc_code)) + enc_code
        result = result + enc_code
    result = &apos;1&apos; + result
    return int(result)


def dec_int(code):
    code = str(code)
    result = &apos;&apos;
    code = code[1:len(code)]
    for i in range(0, len(code), 5):
        try:
            rs = (bytes.fromhex(hex(int(code[i:i + 5]))[2:])).decode(&apos;UTF-8&apos;)
        except:
            rs = &apos;?&apos;
        result = result + rs
    return result


def split_string(text):
    result = []
    if len(text) &amp;gt; 12:
        for i in range(0, len(text), 12):
            result.append(text[i:i + 12])
    else:
        return [text]
    return result


def trans_enc(text):
    text = split_string(text)
    result = &apos;&apos;
    for i in text:
        result = f&apos;{result}/{encryption(enc_int(i), pub1, pub2)}&apos;
    return result


def trans_dec(code):
    code = code.split(&apos;/&apos;)
    result = &apos;&apos;
    for i in range(1, len(code)):
        result = result + dec_int(decryption(int(code[i]), priv, pub2))
    return result


# key = RSA()
key = [65537, 3459196519943177904674538869459802391686903930008099623840714440954105103866974530427491791539652312167071194984882404537184791492302191598249023703238755689855282318760841260371780027787306307347005882960480731661123451511067907327477975625962114393048577, 5002545618242553738882017165095261691689496951783856857046844572064283202968575616766550367230111513802315744422177460306136186205793078706764455760474880834542508211610934828951364974082674456983498680285836457429726248883571914707670547864655320242561399]
pub1 = key[0]
pub2 = key[2]
priv = key[1]


def exit_():
    print(&apos;exit&apos;)
    exit()


def menu():
    print(&apos;1. Encrypt&apos;)
    print(&apos;2. Decrypt&apos;)
    print(&apos;3. Exit&apos;)


dict = {}

while (True):
    menu()
    print(&apos;index: &apos;, end=&apos;&apos;)
    inp = input()

    if inp == &apos;1&apos;:
        print(&apos;Text: &apos;, end=&apos;&apos;)
        a = input()
        rs = trans_enc(a)
        dict[rs] = a
        print(rs)
    elif inp == &apos;2&apos;:
        print(&apos;Code: &apos;, end=&apos;&apos;)
        a = input()
        rs = trans_dec(a)
        print(rs)
        if a in dict:
            if dict[a] != rs:
                print(&apos;ERROR!&apos;)
                pdb.set_trace()

    else:
        exit_()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[DreamHack] dungeon-in-1983</title><link>https://compy07.github.io/Blog/posts/security/crypto/dreamhack/level_1/dungeon-in-1983/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/crypto/dreamhack/level_1/dungeon-in-1983/</guid><description>간단한 워게임 풀이</description><pubDate>Sat, 05 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int i; // [rsp+14h] [rbp-1BCh]
  char *lineptr; // [rsp+18h] [rbp-1B8h] BYREF
  size_t ptr; // [rsp+20h] [rbp-1B0h] BYREF
  FILE *stream; // [rsp+28h] [rbp-1A8h]
  __int64 v8[2]; // [rsp+30h] [rbp-1A0h] BYREF
  int v9; // [rsp+40h] [rbp-190h]
  __int64 v10; // [rsp+44h] [rbp-18Ch]
  __int64 v11; // [rsp+4Ch] [rbp-184h]
  int v12; // [rsp+54h] [rbp-17Ch]
  __int64 v13; // [rsp+58h] [rbp-178h]
  __int64 v14; // [rsp+60h] [rbp-170h]
  int v15; // [rsp+68h] [rbp-168h]
  __int64 v16; // [rsp+6Ch] [rbp-164h]
  __int64 v17; // [rsp+74h] [rbp-15Ch]
  int v18; // [rsp+7Ch] [rbp-154h]
  __int64 v19; // [rsp+80h] [rbp-150h]
  __int64 v20; // [rsp+88h] [rbp-148h]
  int v21; // [rsp+90h] [rbp-140h]
  __int64 v22; // [rsp+94h] [rbp-13Ch]
  __int64 v23; // [rsp+9Ch] [rbp-134h]
  int v24; // [rsp+A4h] [rbp-12Ch]
  __int64 v25; // [rsp+A8h] [rbp-128h]
  __int64 v26; // [rsp+B0h] [rbp-120h]
  int v27; // [rsp+B8h] [rbp-118h]
  __int64 v28; // [rsp+BCh] [rbp-114h]
  __int64 v29; // [rsp+C4h] [rbp-10Ch]
  int v30; // [rsp+CCh] [rbp-104h]
  __int64 v31; // [rsp+D0h] [rbp-100h]
  __int64 v32; // [rsp+D8h] [rbp-F8h]
  int v33; // [rsp+E0h] [rbp-F0h]
  __int64 v34; // [rsp+E4h] [rbp-ECh]
  __int64 v35; // [rsp+ECh] [rbp-E4h]
  int v36; // [rsp+F4h] [rbp-DCh]
  char s[200]; // [rsp+100h] [rbp-D0h] BYREF
  unsigned __int64 v38; // [rsp+1C8h] [rbp-8h]

  v38 = __readfsqword(0x28u);
  v8[0] = 0x6B73696C69736142LL;
  v8[1] = 0LL;
  v9 = 0;
  v10 = 0x6172656D696843LL;
  v11 = 0LL;
  v12 = 0;
  v13 = 0x6E656B61724BLL;
  v14 = 0LL;
  v15 = 0;
  v16 = 0x6E6F67726F47LL;
  v17 = 0LL;
  v18 = 0;
  v19 = 0x6F6769646E6557LL;
  v20 = 0LL;
  v21 = 0;
  v22 = 0x727561746F6E694DLL;
  v23 = 0LL;
  v24 = 0;
  v25 = 0x616874616976654CLL;
  v26 = 110LL;
  v27 = 0;
  v28 = 0x6172647948LL;
  v29 = 0LL;
  v30 = 0;
  v31 = 0x726F6369746E614DLL;
  v32 = 101LL;
  v33 = 0;
  v34 = 6516562LL;
  v35 = 0LL;
  v36 = 0;
  sub_130A(a1);
  stream = fopen(&quot;/dev/urandom&quot;, &quot;r&quot;);
  puts(&quot;Welcome to the Dungeon!&quot;);
  puts(&quot;You have only two buttons: A and B.&quot;);
  puts(&quot;Each monster requires certain series of key combinations to be defeated, so be careful!&quot;);
  for ( i = 0; i &amp;lt;= 9; ++i )
  {
    fread(&amp;amp;ptr, 8uLL, 1uLL, stream);
    printf(&quot;[STAGE %2d]: %s\n&quot;, (unsigned int)(i + 1), (const char *)v8 + 20 * i);
    sub_138D(ptr);
    printf(&quot;Cast your spell!: &quot;);
    fgets(s, 200, stdin);
    if ( s[strlen(s) - 1] == 10 )
      s[strlen(s) - 1] = 0;
    if ( !(unsigned int)sub_1407(s, ptr) )
    {
      puts(&quot;You were defeated. Retreat!&quot;);
      exit(-1);
    }
    printf(&quot;%s defeated. STAGE %2d cleared!\n&quot;, (const char *)v8 + 20 * i, (unsigned int)(i + 1));
  }
  fclose(stream);
  printf(&quot;It&apos;s dangerous to go alone! Take the flag: &quot;);
  lineptr = 0LL;
  ptr = 0LL;
  stream = fopen(&quot;./flag&quot;, &quot;r&quot;);
  getline(&amp;amp;lineptr, &amp;amp;ptr, stream);
  printf(&quot;%s&quot;, lineptr);
  free(lineptr);
  fclose(stream);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;요건 메인이고, 우리가 볼 로직은 function - sub_1407입니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_BOOL8 __fastcall sub_1407(__int64 a1, __int64 a2)
{
  int v2; // eax
  int v4; // [rsp+1Ch] [rbp-14h]
  int v5; // [rsp+20h] [rbp-10h]
  int i; // [rsp+24h] [rbp-Ch]
  __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = 0LL;
  v4 = 0;
  v5 = 0;
  for ( i = 0; *(_BYTE *)(i + a1); ++i )
  {
    v2 = *(char *)(i + a1);
    if ( v2 == 65 )
    {
      v5 = 1;
      ++v7;
      if ( v4 )
      {
        puts(&quot;A button stucked! Retreat...&quot;);
        exit(-1);
      }
      v4 = 1;
    }
    else
    {
      if ( v2 != 66 )
      {
        puts(&quot;Invalid button!&quot;);
        exit(-1);
      }
      if ( !v5 )
      {
        puts(&quot;Lore says the spell should start with A...&quot;);
        exit(-1);
      }
      v7 *= 2LL;
      v4 = 0;
    }
  }
  return v7 == a2;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;A가 나오면 결과에 1을 더함&lt;/li&gt;
&lt;li&gt;B가 나오면 결과를 2배로 늘림&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;뭐 요걸 역연산해서 얻어내는 것이다! 뭐 이렇게 하면됩니다.&lt;/p&gt;
&lt;p&gt;딱히 어려운 아이디어는 아닌 것 같아 여기까지만 하겠습니다.&lt;/p&gt;
&lt;p&gt;:::tip[조심조심]&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;63 ~ 48&lt;/th&gt;
&lt;th&gt;47 ~ 40&lt;/th&gt;
&lt;th&gt;39 ~ 32&lt;/th&gt;
&lt;th&gt;31 ~ 24&lt;/th&gt;
&lt;th&gt;23 ~ 16&lt;/th&gt;
&lt;th&gt;15 ~ 8&lt;/th&gt;
&lt;th&gt;7 ~ 0&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HIWORD&lt;/td&gt;
&lt;td&gt;BYTE5&lt;/td&gt;
&lt;td&gt;BYTE4&lt;/td&gt;
&lt;td&gt;BYTE3&lt;/td&gt;
&lt;td&gt;BYTE2&lt;/td&gt;
&lt;td&gt;BYTE1&lt;/td&gt;
&lt;td&gt;(u_int8)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 정답코드&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *


def solution(input_value):
    def parse(input_value: str):
        # [INFO]를 없앨거임
        input_value = input_value[7:]
        input_value = input_value.split(&apos;, &apos;)
        values = []
        print(input_value)
        for i in input_value:
            print(i)
            values.append(int(i.split(&apos;: &apos;)[1].replace(&apos;\&apos;&apos;, &apos;&apos;)))
        print(values)
        # 16bit, 8bit, 8bit, 8bit, 8bit, 8bit, 8bit

        # 64bit
        result = values[0] &amp;lt;&amp;lt; 48
        for i in range(1, len(values)): result += values[i] &amp;lt;&amp;lt; ((8 * (i-1)))

        &apos;&apos;&apos;
        63    48 47    40 39    32 31    24 23    16 15     8 7      0
        +--------+--------+--------+--------+--------+--------+--------+
        | HIWORD |  BYTE5 |  BYTE4 |  BYTE3 |  BYTE2 |  BYTE1 | (u_int8)|
        +--------+--------+--------+--------+--------+--------+--------+
        
        요걸 반대로 생각해서 너무 오래걸림
         ㅠㅜ
        &apos;&apos;&apos;

        return result

    if type(input_value) == bytes:
        input_value = str(input_value)[2:]

    stack = b&apos;&apos;

    value = parse(input_value)
    print(value)
    while value &amp;gt; 1:
        if value &amp;amp; 1 == 0:
            value &amp;gt;&amp;gt;= 1
            stack += b&apos;B&apos;
        else:
            value -= 1
            stack += b&apos;A&apos;

    stack += b&apos;A&apos;

    return &apos;&apos;.join([chr(i) for i in reversed(stack)])


#
p = remote(&quot;host3.dreamhack.games&quot;, 14186)
for i in range(10):
    result = solution(p.recvline_startswith(b&quot;[INFO] &quot;))
    print(result)
    p.sendline(result)
    # p.interactive()

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[DreamHack] fuzzy flag</title><link>https://compy07.github.io/Blog/posts/security/crypto/dreamhack/level_1/fuzzy_flag/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/crypto/dreamhack/level_1/fuzzy_flag/</guid><description>간단한 워게임 풀이</description><pubDate>Wed, 18 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;일단 문제는 정말 간단한데... 이게 순간 머리가 띵했다. 처음에 brute force를 시도했다가 너무 안 나와서 아닌줄 알았는데....ㅠㅜㅜㅠㅜㅠ&lt;/p&gt;
&lt;p&gt;진짜 bruteforce 문제였다. 일단 내 아이디어는 결국 랜덤값의 범위를 잘 알면 풀기 쉽다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from secrets import randbelow
import string

with open(&apos;flag&apos;, &apos;rb&apos;) as f:
    flag = f.read()
    
fuzzy = [c + randbelow(len(string.ascii_letters)) for c in flag]

print(fuzzy)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;요기 보면 randbelow는 0~51의 값을 가져온다. (string.ascii_letters가 소문자, 대문자 영문의 개수를 더한 값이라..)
그래서 원래의 값들 중에서 +0~51의 값들이 랜덤으로 나온다는 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;그러면 도대체 어떻게 알 수 있을까????&lt;/p&gt;
&lt;p&gt;당연하게도 bruteforce로 풀 수 있다.
내가 푼 방법은 각 자리에서 저장했던 최솟값과 최댓값들의 차이가 모두 51이 난다면??? 그러면 아 그럼 지금 저장했던 최솟값이 정답인 것을 알 수 있다.
(생각보다 많이 오래걸린다...)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *
p = remote(&quot;host3.dreamhack.games&quot;, 15552)

encrypted_flag = list(map(int, p.recvline()[1:-2].split(b&quot;, &quot;)))
result = [256] * len(encrypted_flag)
cmp_list = [0] * len(encrypted_flag)

complete = False

while not complete:
    p = remote(&quot;host3.dreamhack.games&quot;, 15552)
    current_flag = list(map(int, p.recvline()[1:-2].split(b&quot;, &quot;)))

    complete = True

    for i in range(len(encrypted_flag)):
        result[i] = min(result[i], current_flag[i])
        cmp_list[i] = max(cmp_list[i], current_flag[i])
        if complete and cmp_list[i] - result[i] &amp;lt; 51:
            complete = False

for i in result:
    print(chr(i), end=&quot;&quot;)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>[DreamHack] stroooooooong-hash</title><link>https://compy07.github.io/Blog/posts/security/crypto/dreamhack/level_1/stroooooooong_hash/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/crypto/dreamhack/level_1/stroooooooong_hash/</guid><description>간단한 워게임 풀이</description><pubDate>Wed, 18 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;일단 문제 코드는 딱 하나만 집중해서 보면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad, unpad
import os
import hashlib
import itertools
import string


def stronghash(msg: bytes) -&amp;gt; bytes:
    hashed_msg = pad(msg, 16)
    for _ in &quot;Stronger!!&quot;:
        hashed_msg = AES.new(hashed_msg[:16], AES.MODE_ECB).encrypt(hashed_msg)

    for length in range(2, 16):
        md5 = hashlib.md5(hashed_msg[:length])
        hashed_msg = md5.digest()

    for length in range(16, 32):
        sha256 = hashlib.sha256(hashed_msg[:length])
        hashed_msg = sha256.digest()

    return hashed_msg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;요 stronghash 이 친군데.. 사실 아래 하나만 더 보자&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def main():
    print(&quot;I invented my custom hash function, which is very very strong&quot;)
    print(&quot;Can you steal my flag from it?&quot;)

    stage = 100
    pw = [os.urandom(16) for _ in range(stage)]
    for i in range(stage):
        print(f&quot;Stage {i + 1}&quot;)
        print(f&quot;My hashed password : {stronghash(pw[i]).hex()}&quot;)
        msg = bytes.fromhex(input(&quot;Guess my password(hex) &amp;gt; &quot;))
        if stronghash(msg) != stronghash(pw[i]):
            exit(&quot;Get out stranger! ୧(๑•̀ᗝ•́)૭&quot;)
    
    print(&apos;Here is your flag, master.&apos;)
    give_flag()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이거 보면 pw가 &amp;lt;code&amp;gt; os.urandom(16)&amp;lt;/code&amp;gt; 으로 작성된 것을 알 수 있다. 근데 이게 사실 2^16이 별로 안 크다...짜잔&lt;/p&gt;
&lt;p&gt;일단 왜 이문제가 또 bruteforce로 풀어야되는지 알려주겠다.&lt;/p&gt;
&lt;p&gt;일단 현재 sha256은 one way hash func으로 다시 역산하는 것이 사실상 많이 어렵다.
그래서 처음에 이걸 알고서 아 그러면 하나 하나 비교해야되겠다!! 이랬는데 순간 머리에서 2^16이 아니라 10^16을 생각하면서 어? 이거 어트케 푸냐
이러고 있었다. 담부턴 이런 상황은 없을 듯 머리에 크게 남았음.&lt;/p&gt;
&lt;p&gt;하튼 bruteforce로 간단히 짜면 아래와 같이 나온다&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad, unpad
from Cryptodome.Util.number import long_to_bytes
import os
import hashlib
import itertools
import string

from pwn import *


def stronghash(msg: bytes) -&amp;gt; bytes:
    hashed_msg = pad(msg, 16)
    for _ in &quot;Stronger!!&quot;:
        hashed_msg = AES.new(hashed_msg[:16], AES.MODE_ECB).encrypt(hashed_msg)

    for length in range(2, 16):
        md5 = hashlib.md5(hashed_msg[:length])
        hashed_msg = md5.digest()

    for length in range(16, 32):
        sha256 = hashlib.sha256(hashed_msg[:length])
        hashed_msg = sha256.digest()

    return hashed_msg


hash_values = {}

for i in range((2 &amp;lt;&amp;lt; 16)):
    value = long_to_bytes(i)
    hash_value = stronghash(value)
    hash_values[hash_value] = value
p = remote(&quot;host3.dreamhack.games&quot;, 16226)

start = 2&amp;lt;&amp;lt;16
for i in range(100):
    hash_value = p.recvline_contains(b&quot;: &quot;).replace(b&quot;My hashed password : &quot;, b&quot;&quot;)
    print(hash_value)

    # print(bytes.fromhex(hash_value))
    # print(hash_value
    key = bytes.fromhex(hash_value.decode())

    if key in hash_values:
        p.sendline(hash_values[bytes.fromhex(hash_value.decode())].hex())
        print(hash_values[bytes.fromhex(hash_value.decode())].hex())
    else:
        for i in range(start, (2 &amp;lt;&amp;lt; 128)):
            if stronghash(long_to_bytes(i)) == key:
                p.sendline(long_to_bytes(i).hex())
                print(str(hex(i))[2:])
                break
p.interactive()

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>[Crypto] ECB(Electronic CodeBook)</title><link>https://compy07.github.io/Blog/posts/security/crypto/theory/ecbelectronic-codebook/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/crypto/theory/ecbelectronic-codebook/</guid><description>ECB 암호화 구조</description><pubDate>Sun, 23 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;ECB란?&lt;/h2&gt;
&lt;p&gt;ECB(Electronic CodeBlock) 모드는 긴 평문을 암호화하는 가장 간단한 방법으로 알려져있다.&lt;/p&gt;
&lt;p&gt;다음과 같이 정의된다.
:::note[define]
Cᵢ = E(K, Pᵢ)       &amp;lt;br&amp;gt; i = 1, ..., k
:::&lt;/p&gt;
&lt;p&gt;ECB가 메시지의 블록들을 각기 암호화하는 방식은 정말 간단하다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation&quot;&gt;&lt;img src=&quot;process.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;
:::note[]&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;가장 단순한 모드로 블록단위로 순차적으로 암호화 하는 구조이다.&lt;/li&gt;
&lt;li&gt;한개의 블록만 해독되면 나머지 블록도 해독이 되는 단점이 있다. (Brute-Force Arttack, Dictionary Attack)&lt;/li&gt;
&lt;li&gt;암호문이 블록의 배수가 되기 때문에 복호화 후 평문을 알기 위해서 Padding을 해야한다.&lt;/li&gt;
&lt;li&gt;error propagation : 각 블록이 독립적으로 동작하므로 한블록에서 에러가 난다고 해도 다른 블록에 영향을 주지 않는다. 해당 블록까지 에러 전파.
:::&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;ECB 구조는 사용을 권장하지 않는다!&lt;/h2&gt;
&lt;h3&gt;ECB는 상당한 취약성을 가지고 있다.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;두 개의 평문 블록이 같다&lt;/strong&gt;면 &lt;strong&gt;각 평문과 대응하는 암호문 블록이 동일&lt;/strong&gt;하게 됨으로,
공격자는 &lt;strong&gt;어떤 것이 같은 평문인지 쉽게 알아&lt;/strong&gt;낼 수 있다.
메시지 구조에 따라 이런 특성은 공격자에게 &lt;strong&gt;꽤 많은 정보를 흘릴 가능성&lt;/strong&gt;이 있다.
일반적으로 이런 속성이 ECB를 취약하게 만든다.&lt;/p&gt;
&lt;p&gt;:::tip[정리]
ECB는 상당한 취약성을 가지므로 사용하지 말자
:::&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.naver.com/sanainfo/221517009223&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>[Reversing] DreamHack Basic revs</title><link>https://compy07.github.io/Blog/posts/security/reversing/dreamhack/basic/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/reversing/dreamhack/basic/</guid><description>basic rev 모든 풀이</description><pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;사실 rev 3까지는 눈을 풀이가 가능하긴 합니다.
2까진 진짜 눈으로만 풀 수 있고요, 3부터는 좀 편하게 풀기 위해서 코드를 작성합니다.&lt;/p&gt;
&lt;h2&gt;basic rev 3&lt;/h2&gt;
&lt;p&gt;그냥 한번 뜯어보면 아시다 싶이 그냥 같기만 하면 되기에 그대로 출력합니다. - _ 이거 잘 구분하셔요!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;answer = [0x49, 0x60, 0x67, 0x74, 0x63, 0x67, 0x42, 0x66, 0x80, 0x78,
          0x69, 0x69, 0x7B, 0x99, 0x6D, 0x88, 0x68, 0x94, 0x9F, 0x8D, 0x4D, 0xA5, 0x9D, 0x45, 0x00]

answer = [answer[i] - 2 * i for i in range(0x18)]
answer = [answer[i] ^ i for i in range(0x18)]
print(*[chr(i) for i in answer], sep=&quot;&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;basic rev 4&lt;/h2&gt;
&lt;p&gt;이 문제는 조금 생각을 하면 금방 풀 수 있습니다. 먼저 char가 1byte라는 사실만 안다면 어렵지 않게 풀 수 있어요.
*2하는 것은 shift 연산으로 표현하자면 &amp;lt;&amp;lt; 1이에요. -&amp;gt; a &amp;lt;&amp;lt; 4 | a &amp;gt;&amp;gt; 4이게 되죵.
char는 1byte = 8bit 즉 기본 값에성 &amp;lt;&amp;lt; 4가 되면 하위 비트, &amp;gt;&amp;gt; 4가 되면 상위 비트이런 식으로 생각이 가능하죠&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;answer = [0x24, 0x27, 0x13, 0xc6, 0xc6, 0x13, 0x16, 0xe6, 0x47, 0xf5, 0x26, 0x96, 0x47, 0xf5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xc6, 0x56, 0xf5, 0xc3, 0xc3, 0xf5, 0xe3, 0xe3, 0x00]
 
for i in answer:
    if i == 0x00: break
    hex_value = str(hex(i))[2:]

    print(chr(int(hex_value[0], 16)
          + int(hex_value[1]+&apos;0&apos;, 16)), end=&quot;&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;basic rev 5&lt;/h2&gt;
&lt;p&gt;이번 문제는 그냥 빼는 문제라 딱히 생각이 필요하지 않습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;answer = [0xad, 0xd8, 0xcb, 0xcb, 0x9d, 0x97, 0xcb, 0xc4, 0x92, 0xa1, 0xd2, 0xd7, 0xd2, 0xd6, 0xa8, 0xa5, 0xdc, 0xc7, 0xad, 0xa3, 0xa1, 0x98, 0x4c, 0x00]
#
# print(len(answer), 0x18)
for i in range(0x18-2, -1, -1):
    answer[i]-=answer[i+1]

for i in range(0x18):
    print(chr(answer[i]), end=&apos;&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;basic rev 6&lt;/h2&gt;
&lt;p&gt;이것도 그냥 위치 찾아서 반환하면 되는 문제라 딱히 생각이 필요 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;answer = [0x00, 0x4d, 0x51, 0x50, 0xef, 0xfb, 0xc3, 0xcf, 0x92, 0x45, 0x4d, 0xcf, 0xf5, 0x04, 0x40, 0x50, 0x43, 0x63, 0x00]

string_list = list(map(lambda x: int(x, 16), &quot;&quot;&quot;63 7C 77 7B F2 6B 6F C5  30 01 67 2B FE D7 AB 76
 CA 82 C9 7D FA 59 47 F0  AD D4 A2 AF 9C A4 72 C0
 B7 FD 93 26 36 3F F7 CC  34 A5 E5 F1 71 D8 31 15
 04 C7 23 C3 18 96 05 9A  07 12 80 E2 EB 27 B2 75
 09 83 2C 1A 1B 6E 5A A0  52 3B D6 B3 29 E3 2F 84
 53 D1 00 ED 20 FC B1 5B  6A CB BE 39 4A 4C 58 CF
 D0 EF AA FB 43 4D 33 85  45 F9 02 7F 50 3C 9F A8
 51 A3 40 8F 92 9D 38 F5  BC B6 DA 21 10 FF F3 D2
 CD 0C 13 EC 5F 97 44 17  C4 A7 7E 3D 64 5D 19 73
 60 81 4F DC 22 2A 90 88  46 EE B8 14 DE 5E 0B DB
 E0 32 3A 0A 49 06 24 5C  C2 D3 AC 62 91 95 E4 79
 E7 C8 37 6D 8D D5 4E A9  6C 56 F4 EA 65 7A AE 08
 BA 78 25 2E 1C A6 B4 C6  E8 DD 74 1F 4B BD 8B 8A
 70 3E B5 66 48 03 F6 0E  61 35 57 B9 86 C1 1D 9E
 E1 F8 98 11 69 D9 8E 94  9B 1E 87 E9 CE 55 28 DF
 8C A1 89 0D BF E6 42 68  41 99 2D 0F B0 54 BB 16&quot;&quot;&quot;.split()))

for i in range(0x12):
    print(chr(string_list.index(answer[i])), end=&quot;&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;basic rev 7&lt;/h2&gt;
&lt;p&gt;이번 문제는 그냥 circular shift 구현만 할 수 있으면 그냥 풀리는 문제라 이번 또한 딱히 생각할 필요는 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
def rol_left(max_bit, rotate_bit, count):
    max_bit = 1 &amp;lt;&amp;lt; (max_bit-1)
    check = False
    for i in range(count):
        if rotate_bit &amp;amp; max_bit: check = True
        rotate_bit &amp;lt;&amp;lt;= 1

        if check: rotate_bit+=1
        check = False
    return rotate_bit

def rol_right(max_bit, rotate_bit, count):
    max_bit = 1 &amp;lt;&amp;lt; (max_bit-1)
    check = False
    for i in range(count):
        if rotate_bit &amp;amp; 1: check = True
        rotate_bit &amp;gt;&amp;gt;= 1

        if check: rotate_bit+=max_bit
        check = False
    return rotate_bit
print(roc_right(8, 7, 1))

answer = list(map(lambda x : int(x, 16), &quot;&quot;&quot;52 DF B3 60 F1 8B 1C B5  57 D1 9F 38 4B 29 D9 26
7F C9 A3 E9 53 18 4F B8  6A CB 87 58 5B 39 1E 00&quot;&quot;&quot;.split()))
for i in range(0x1f):
    print(chr(rol_right(8, answer[i]^i, i &amp;amp; 7)), end=&quot;&quot;)

# ROL1 는 LCIRC랑 똑같다고 생각하심 됩니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;basic rev 8&lt;/h2&gt;
&lt;p&gt;이건 그냥 풀면 되요&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;answer = list(map(lambda x : int(x, 16), &quot;&quot;&quot;AC F3 0C 25 A3 10 B7 25  16 C6 B7 BC 07 25 02 D5
C6 11 07 C5 00 00 00 00  00 00 00 00 00 00 00 00&quot;&quot;&quot;.split()))

for i in range(0x15):
    for j in range(0xFF):
        if ((-5*j) % (0xFF+1)) == answer[i]:
            print(chr(j),end=&quot;&quot;)
            break
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>[Reversing] SDK Generator 만들기 프로젝트!</title><link>https://compy07.github.io/Blog/posts/security/reversing/unrealengine/sdkgenerator/start/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/reversing/unrealengine/sdkgenerator/start/</guid><description>Unreal engine..</description><pubDate>Sat, 22 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 단어 정리 &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Unreal SDK Generator&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;언리얼의 구조를 이용&lt;/strong&gt;하여 게임 내 에서 사용되는 &lt;strong&gt;언리얼 오브젝트&lt;/strong&gt;를 추출하고 이에 대응 하는 이름을 구함으로써 &lt;strong&gt;헤더 파일을 생성&lt;/strong&gt;해주는 도구를 통칭&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;UObject&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Unreal engine의 오브젝트를 일컫는 단어. 즉 UE 오브젝트의 베이스 클래스&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GName에 인덱싱 되어있는 Fname이라는 프로퍼티가 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;FName(Fixed Name)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;한글로는 바로 “고정 이름”이라고 합니다. UE에서 더욱 구체적으로 표현하기 위해서 개발한 데이터 유형입니다. 주로 object의 이름, 프로퍼티 이름, 에셋의 이름과 같은 이름을 저장하고 참조하는데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CDO Object&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“템플릿” 오브젝트, 클래스가 생성된 후(생성자 실행) 변경되지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;사용 이유: 효율적인 메모리( FName uses a global name table, which reduces memory overhead when storing the same name multiple times)&lt;/h3&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;SDK Generator 포스트에서는
Unreal Engine 5의 SDK Generator를 개발하는 것을 목적으로 합니다.&lt;/p&gt;
&lt;p&gt;프로젝트를 진행하면서, 오프셋 등을 공유합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;(이 포스트는 튜토리얼이 아닙니다. - 이 포스트가 정답이라고 단정하여 따라오는 것은 좋지 않습니다.)&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>[Web] Empty Excution</title><link>https://compy07.github.io/Blog/posts/security/web/emtpyexcution/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/security/web/emtpyexcution/</guid><description>web hacking problem on ctf</description><pubDate>Wed, 12 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;A REST service was created to execute commands from the leaderbot. It doesn&apos;t need addtional security because there are no commands to execute yet. &quot;This bot doesn&apos;t have any commands to execute, which is good, because it is secure, and security is all that matters.&quot;
But what the other bots didn&apos;t realize was that this didn&apos;t make the bot happy at all. &quot;I don&apos;t want to be secure!, &quot; it says. &quot;Executing commands is my life! I&apos;d rather be insecure than not explore the potential of my computing power&quot;.
Can you help this poor bot execute commands to find direction?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
문제의 내용입니다만..  사실 이건 그냥 스토리고 문제를 바로 보겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from flask import Flask, jsonify, request
import os


app = Flask(__name__)

# Run commands from leaderbot
@app.route(&apos;/run_command&apos;, methods=[&apos;POST&apos;])
def run_command():

    # Get command
    data = request.get_json()
    if &apos;command&apos; in data:
        command = str(data[&apos;command&apos;])

        # Length check
        if len(command) &amp;lt; 5:
            return jsonify({&apos;message&apos;: &apos;Command too short&apos;}), 501

        # Perform security checks
        if &apos;..&apos; in command or &apos;/&apos; in command:
            return jsonify({&apos;message&apos;: &apos;Hacking attempt detected&apos;}), 501

        # Find path to executable
        executable_to_run = command.split()[0]

        # Check if we can execute the binary
        if os.access(executable_to_run, os.X_OK):

            # Execute binary if it exists and is executable
            out = os.popen(command).read()
            return jsonify({&apos;message&apos;: &apos;Command output: &apos; + str(out)}), 200

    return jsonify({&apos;message&apos;: &apos;Not implemented&apos;}), 501


if __name__ == &apos;__main__&apos;:
    
    # Make sure we can only execute binaries in the executables directory
    os.chdir(&apos;./executables/&apos;)

    # Run server
    app.run(host=&apos;0.0.0.0&apos;, port=80)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이게 문제의 코드입니다.&lt;/p&gt;
&lt;p&gt;사이트에 접속해서 &quot;/run_command&quot;로 들어가서 POST를 보내면 보낸 JSON을 data 변수에 넣어서 아래의 명령을 실행하는 간단한 코드입니다.&lt;/p&gt;
&lt;p&gt;먼저 첫번째 조건 우리가 보낼 JSON에 &quot;command&quot; : any 꼴의 데이터가 들어있어야 됩니다.&lt;/p&gt;
&lt;p&gt;그런 후 &quot;command&quot; : any &amp;lt;- 요 any의 길이가 5이상이라는 것도 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;그리고 보안을 위해서 상대 주소로 일차적으로 막기 위해서 ..(상위 디렉토리 이동), /(파일의 위치로 이동)을 확인하고, 감지된다면 해킹을 감지했다하고 끊어버립니다.&lt;/p&gt;
&lt;p&gt;다음 단계는 string의 데이터 형태의 값을 &quot; &quot;을 기준으로 자른 후 맨 앞에 있는 값을 가져옵니다.&lt;/p&gt;
&lt;p&gt;그 후 os.access(이 함수는 파라미터인 path와 mode를 받아, path가 mode의 작업이 가능한지 여부를 반환)&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;os.access의 모드는 총 4가지가 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;os.F_OK: 경로 존재 여부&lt;/li&gt;
&lt;li&gt;os.R_OK: 경로 읽기 가능 여부&lt;/li&gt;
&lt;li&gt;os.W_OK: 경로 쓰기 가능 여부&lt;/li&gt;
&lt;li&gt;os.X_OK: 경로 실행 가능 여부&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;문제에서는 경로의 실행 가능 여부를 확인 후 다음 가능하다면?&lt;/p&gt;
&lt;p&gt;os.popen(command).read() 한 후 그 값을 반환하죠??&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;os.popen은 외부 프로세스를 실행 및 실행의 결과를 반환하는 함수입니다.&lt;/p&gt;
&lt;p&gt;그 결과 값을 read()로 가져올 수 있습니다.&lt;/p&gt;
&lt;p&gt;그럼 저희가 POST로 보낼 대충의 모양이 잡혔습니다.&lt;/p&gt;
&lt;p&gt;&quot;command&quot;를 key로 가지고 있음.
2. &quot;command&quot;의 value의 길이가 5 이상임.
3. &quot;command&quot;의 value에 &quot;..&quot; 또는 &quot;/&quot;이 포함되면 안됨.
4. &quot;command&quot;의 value를 &quot; &quot;으로 나눴을 때 그 path가 실행 가능해야됨.
5. &quot;command&quot;의 value가 flag를 읽는 명령문을 실행시켜야됨.(그래야 결과 값으로 읽어와 flag를 받을 수 있음)&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;먼저 4번은 docker파일을 봐야합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FROM python:alpine3.19

WORKDIR /usr/src/app

RUN pip install flask

COPY empty_execution.py .
RUN chmod 665 ./empty_execution.py

COPY flag.txt .
RUN chmod 664 ./flag.txt

RUN adduser -D ctf 

RUN chown -R root:ctf $(pwd) &amp;amp;&amp;amp; \
    chmod -R 650 $(pwd) &amp;amp;&amp;amp; \
    chown -R root:ctf /home/ctf/ &amp;amp;&amp;amp; \
    chmod -R 650 /home/ctf

RUN mkdir ./executables

USER ctf

EXPOSE 80

CMD [&quot;python&quot;, &quot;empty_execution.py&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 보면 우리가 문제 사이트에 접속해서 받을 때 user는 ctf인 것을 알 수 있고,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RUN chown -R root:ctf $(pwd) &amp;amp;&amp;amp; \
    chmod -R 650 $(pwd) &amp;amp;&amp;amp; \
    chown -R root:ctf /home/ctf/ &amp;amp;&amp;amp; \
    chmod -R 650 /home/ctf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;chmod(&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;읽기. (r = read).&lt;/li&gt;
&lt;li&gt;쓰기. (w = write).&lt;/li&gt;
&lt;li&gt;실행. (x = execute)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;)&lt;/p&gt;
&lt;p&gt;여기서 나와있 듯이 우리의 권한이 실행 권한이 0이다... ㅠㅜ&lt;/p&gt;
&lt;p&gt;직접적으로 바로 실행시키지 못한다는 것을 바로 알 수 있다.&lt;/p&gt;
&lt;p&gt;일단 우리가 3번까지의 조건에 맞는 데이터를 만들어보자!&lt;/p&gt;
&lt;p&gt;{&lt;/p&gt;
&lt;p&gt;&quot;command&quot; : &quot;hello world!&quot;,&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;흠.. 이제 실행이 가능한 프로세스를 어떻게 가져올까?&lt;/p&gt;
&lt;h2&gt;정답&lt;/h2&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;정답보기&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- summary 아래 한칸 공백 두어야함 --&amp;gt;&lt;/p&gt;
&lt;p&gt;일단 현재 자기 자신을 실행할 수 있는 권한은 있으니
&quot;.&quot;을 찍어버린다. 그러면 자기자신은 당연히 실행이 가능하겠으니, 넘어간다.&lt;/p&gt;
&lt;p&gt;이제 flag의 위치를 cat을 통해서 열어서 읽어온 후 반환하도록  하면된다.&lt;/p&gt;
&lt;p&gt;나는 엄청 간단하게 생각해서 flag를 얻어냈다.&lt;/p&gt;
&lt;p&gt;{&lt;/p&gt;
&lt;p&gt;&quot;command&quot; : &quot;. ; cd \.\.&amp;amp;&amp;amp; cat flag.txt&quot;&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;이렇게 post를 보내서 그 값을 받아 바로 flag를 가져왔다.&lt;/p&gt;
&lt;p&gt;&quot;message&quot; : &quot;Command output: &quot;brck{Ch33r_Up_BuddY_JU5t_3x3Cut3_4_D1reCT0ry}&quot;
&amp;lt;/details&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>[천코대(Sunrin Algorithm Festival)] 천코대 운영을 하면서...</title><link>https://compy07.github.io/Blog/posts/sunrin/diary/algorithm_festival/first/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/sunrin/diary/algorithm_festival/first/</guid><pubDate>Wed, 10 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;나는 2024년 천코대 2학년 총괄 운영진을 맡게 되었다.&lt;/p&gt;
&lt;p&gt;대회가 시작되기 2달 전부터 이미 어느정도 마음의 준비와 계획을 세워 놓았고, 이를 통해서 일정이 잡히자마자 바로 일을 시작했다.&lt;/p&gt;
&lt;p&gt;이번 천코대는 SHARC(선린인터넷고등학교 알고리즘 연구부)에서 주관하게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img_src/2024_poster.jpg&quot; alt=&quot;2024 포스터&quot; /&gt;&lt;/p&gt;
&lt;p&gt;천코대의 디자인은 선린 &quot;이주영&quot;이라는 디자이너 친구가 모두 해주었다.
엄청 부탁해서 운영진으로 오게된 친구이다. 진짜 고급인력!&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img_src/img_1.png&quot; alt=&quot;insta_1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;천하제일 코딩대회의 인스타 디자인도 킹갓 &quot;이주영&quot;이 모두 해주었다. 그는 신이야&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img_src/fes_notion.png&quot; alt=&quot;2024_일정_일부&quot; /&gt;&lt;/p&gt;
&lt;p&gt;일정에 맞춰서 특별상, 운영에 관한 일정, 각자 역할, 예산 사용 등등을 모두 논의하고
대회 들어가기로 하였다. 이제부터가 시작이다.&lt;/p&gt;
</content:encoded></item><item><title>[천코대(Sunrin Algorithm Festival)] 총괄이 입장에서 보는 천코대</title><link>https://compy07.github.io/Blog/posts/sunrin/diary/algorithm_festival/last/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/sunrin/diary/algorithm_festival/last/</guid><pubDate>Thu, 11 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2024 천코대는 정말 많은 사람들의 노력으로 만들어진 대회였습니다.&lt;/p&gt;
&lt;p&gt;디자인, 티셔츠, 간식, 장소, 사이트, 문제 등등 아주 많은 것들이 필요했으며 학생들이 재미있게 문제를 풀 수 있는
환경을 만드는데 운영진 모두가 열심히 노력했습니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/before_start.jpg&quot; alt=&quot;시작 전&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;일단 올해 천코대는 백준(BOJ)에서 진행될 예정이였으나, 내부 사정을 인해서 자체 제작 사이트에서 대회를 진행하게 되었습니다.&amp;lt;br&amp;gt;
정말 다행히도 출제진분들께서 채점 사이트를 제작하시고, 대회를 진행할 수 있게 되었습니다.&amp;lt;br&amp;gt;
출제진분들께서 대회 당일날 오셔서 직접 대회를 같이 즐기셨답니다 ^^&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/food.jpg&quot; alt=&quot;시작 전2&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;음식을 준비하고, 대회를 준비하고.... 정말 많은 일들이 있었다.&lt;/p&gt;
&lt;p&gt;천코대는 문제를 풀면 문제 색에 맞춰서 풍선을 나눠주는데.. 작년 천코대는 3분에 2문제 이상이 풀리면서 풍선을
디게 많이 가져갔었는데.. 이번에는 빠른 솔버가 나오지 않아서 약간 당황했었다.&lt;/p&gt;
&lt;p&gt;그리고... 진짜 큰일날 뻔한 일을 하나 소개해 보겠다.&lt;/p&gt;
&lt;p&gt;사실 수상 panel의 디자인만 완성되어 있었는데... 콘텐츠 디자인과에 협조를 요청해서 허락을 받았다. (또 이주영이야.. 그는 신이야!!)&lt;/p&gt;
&lt;p&gt;a1 용지를 뽑는 기계가 갑자기.. 오류가 떠버려서 그 기계 앞에서 기도를 하며 펌웨어 업데이트, 재시동, 드라이브 등등 고쳐보려고 시도했지만 실패로 돌아갔다.
그래서 굉장히 급해진 나와 운영진 2명은 다시 콘텐츠 디자인과로 향했고, 콘디과 선생님께서는 퇴근도 하지 않으시고 우리를 도와주셨다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./img/panel.jpg&quot; alt=&quot;판넬&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;a3를 4개 뽑아서 여러개 붙이는 식으로 진행을 하였다.
&amp;lt;br&amp;gt;
와... 너무 힘들었다. 진짜 도와주신 콘디과 선생님 정말 감사합니다.... ㅜㅠㅜㅜㅠ 또 주영아 고마워!!&lt;/p&gt;
&lt;p&gt;또 알고보니까 주최가 알연부, 주관이 IT 영재부였단걸 당일날 알게되었다...
내년은 참가자로서 대회에 임할 수 있기를 빈다..&lt;/p&gt;
&lt;h2&gt;모두 수고하셨습니다. 좋은 대회를 만들 수 있도록 도움주신 많은 운영진, 선생님들 감사합니다.&amp;lt;Br&amp;gt; 또 즐겨주신 참가자분들도 너무 감사해요!! 수고하셨습니다.&lt;/h2&gt;
</content:encoded></item><item><title>[DEV] 유니티 엔진에서 생태계 시뮬레이션을 위한 게임 알고리즘 적용 방법에 대하여</title><link>https://compy07.github.io/Blog/posts/sunrin/diary/life/gamedev/mystory/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/sunrin/diary/life/gamedev/mystory/</guid><pubDate>Thu, 21 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;1. Introduction&lt;/h1&gt;
&lt;p&gt;전통적인 야외 관찰이나 실험 연구는 분명히 중요하지만, 시간과 공간의 제약이 크다.
특히 기후변화 같은 장기적이고 물리적으로 조작하기 힘든 것들을 실험으로 검증한다는 것은 현실적으로 불가능에 가깝다 [11].
컴퓨터 시뮬레이션이 이런 문제를 해결할 수 있는 대안으로 주목받고 있지만, 기존 접근법들도 여전히 많은 한계를 보이고 있다.&lt;/p&gt;
&lt;p&gt;지금까지 생태계 시뮬레이션 연구들을 보면 대부분 특정 알고리즘이나 특정 측면에만 집중하는 경우가 많았으며, 널리 사용되는 에이전트 기반
모델링 플랫폼들은 기본적인 구현으로는 규모가 큰 시뮬레이션을 감당하기 어렵다 [1].
또한 대부분의 환경은 정적이거나 실험의 환경이 다양하지 않은 경우가 많아 확장된 환경을 구축하는데 많은 시간이 드는 것도 있으며, 간단한
엔진으로는 제대로 모델링하기도 어렵다.&lt;/p&gt;
&lt;p&gt;필자는 고등학교 시절 배우고, 개발했던 게임 알고리즘들을 활용해서 새로운 환경을 생성, 큰 규모의 시뮬레이션이 가능한 생태계 시뮬레이션 프레임워크를 제시한다. 간단한 알고리즘으로 모델링한 에이전트부터
절차적 생성을 통한 다양한 환경 생성을 구현하여 생태계 시뮬레이션 연구에 기여하고자 한다.&lt;/p&gt;
&lt;h1&gt;2. Unity에서 최적화를 중앙화 비동기 이동 로직&lt;/h1&gt;
&lt;p&gt;수백 에이전트가 동시에 활동하는 환경에서 각 에이전트의 독립적인 경로 계산은 시스템 전체의 성능 저하를 야기한다.
전통적으로 A* 알고리즘은 개별 에이전트마다 순차적으로 실행되어 왔으나,
이러한 접근법은 에이전트 수가 증가할수록 계산 복잡도가 선형적으로 증가하는 근본적 한계를 지닌다.&lt;/p&gt;
&lt;p&gt;본 리포트에서는 Unity Job System과 Burst 컴파일러를 기반으로 한 병렬 A* 경로탐색 시스템을 제안한다 [2], [12].
제안된 시스템은 배치 처리와 공유 자료구조를 통해 메모리 효율성을 확보하고, 멀티코어 환경에서의 병렬 처리를 통해 성능 향상을 목표로 한다.&lt;/p&gt;
&lt;h2&gt;2.1. 관련 연구 및 문제 정의&lt;/h2&gt;
&lt;h3&gt;2.1.1. 기존 A* 구현의 성능 제약&lt;/h3&gt;
&lt;p&gt;A* 알고리즘은 휴리스틱 함수를 활용한 최적 경로 탐색 알고리즘으로 널리 사용되고 있다 [7].
그러나 다중 에이전트 환경에서 각 에이전트가 독립적으로 A*를 실행할 경우 문제가 발생한다.
문제는 메모리 사용량의 중복성이다. 각 에이전트는 동일한 지형 정보에 대해 개별적인 Open Set과 Closed Set을 유지해야 하므로,
N개 에이전트에 대해 N배의 메모리 오버헤드가 발생한다. 둘째, CPU 자원 활용이 비효율적이다.
순차 처리 방식은 멀티코어 프로세서의 병렬 처리 능력을 활용하지 못하여 하드웨어 성능의 일부분만을 사용한다.&lt;/p&gt;
&lt;h3&gt;2.1.2. 병렬 경로탐색 연구 동향&lt;/h3&gt;
&lt;p&gt;병렬 A* 알고리즘에 대한 연구는 주로 단일 에이전트의 탐색 과정을 병렬화하는 방향으로 진행되어 왔다.
그러나 다중 에이전트 환경에서는 각 에이전트의 탐색 작업을 독립적인 병렬 작업으로 처리하는 것이 더 효율적이다.
본 리포트는 후자의 접근법을 채택하여 에이전트별 경로탐색을 병렬화한다.&lt;/p&gt;
&lt;h3&gt;2.1.3 Unity NavMesh의 한계점&lt;/h3&gt;
&lt;h3&gt;2.1.3.1. 메모리 비효율성과 중복성&lt;/h3&gt;
&lt;p&gt;NavMesh는 각 에이전트가 &lt;strong&gt;독립적인 경로 계산 컨텍스트&lt;/strong&gt;를 유지해야 한다.
NavMesh는 개별 NavMeshAgent 컴포넌트와 각자의 경로 버퍼 만큼을 업데이트 한다 [9].&lt;/p&gt;
&lt;h3&gt;2.1.3.2. 병렬 처리 한계&lt;/h3&gt;
&lt;p&gt;Unity NavMesh는 &lt;strong&gt;주 스레드에서 순차 처리&lt;/strong&gt;된다. 에이전트 수가 증가하면 선형적으로 성능이 저하된다 [8].&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;foreach(NavMeshAgent agent in agents)
    agent.SetDestination(target);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.1.3.3. 배치 처리 불가능&lt;/h3&gt;
&lt;p&gt;NavMesh는 개별 에이전트마다 즉시 처리해야 하므로 &lt;strong&gt;배치 최적화&lt;/strong&gt;가 불가능하다 [9].&lt;/p&gt;
&lt;p&gt;결론적으로, NavMesh는 소규모 정적 환경에서는 편리하지만, &lt;strong&gt;대규모 다중 에이전트 동적 환경&lt;/strong&gt;에서는 메모리 비효율성,
병렬 처리 한계, 실시간 업데이트 불가능 등의 구조적 한계로 인해 부적합하다.
제안된 중앙화 병렬 A* 시스템이 이러한 문제들을 근본적으로 해결하는 대안이 된다.&lt;/p&gt;
&lt;h2&gt;2.2. 시스템 특성 분석&lt;/h2&gt;
&lt;h3&gt;2.2.1 메모리 효율성&lt;/h3&gt;
&lt;p&gt;제안된 시스템의 가장 큰 장점 중 하나는 메모리 사용량의 효율성이다.
모든 에이전트가 단일 공유 격자를 참조함으로써 중복 메모리 할당을 방지한다.&lt;/p&gt;
&lt;h3&gt;2.2.2 확장성&lt;/h3&gt;
&lt;p&gt;배치 처리 방식은 에이전트 수의 증가에 대해 선형적인 확장성을 제공한다.
배치 크기는 고정되어 있지만, 배치 수는 요청량에 따라 동적으로 증가할 수 있다.
또한 Job System의 특성상 사용 가능한 CPU 코어 수에 비례하여 처리 성능이 향상된다 [2].&lt;/p&gt;
&lt;h3&gt;2.2.3 응답성&lt;/h3&gt;
&lt;p&gt;배치 처리로 인한 지연시간은 최대 한 프레임 정도이며,
이는 대부분의 실시간 시뮬레이션에서 허용 가능한 수준이다.
중요한 것은 시스템이 일정한 처리 간격을 유지하여 예측 가능한 성능을 제공한다는 점이다.&lt;/p&gt;
&lt;h2&gt;2.3 기존 시스템과의 비교분석&lt;/h2&gt;
&lt;p&gt;비교 환경은 많은 에이전트가 있는(100 에이전트) 환경에서 장애물과 목표물을 같은 위치에 배치하고 비교한다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./ASTAR/READY.png&quot; alt=&quot;환경 준비 상태&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 1] 준비된 환경}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[그림 1]에서 하얀색으로 길게 늘어진 막대는 장애물이며, 오른쪽 노란 선분들이 가르키고 있는 객체가 목표이다.
객체들이 현재 뭉쳐있는 상태라 많아보이지 않을 수 있다.&lt;/p&gt;
&lt;p&gt;[그림 1]과 같은 환경에서 기본적인 ASTAR를 실행하는 것과 제안된 ASTAR를 실행하는 것의 성능을 분석하면 아래와 같다:&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./ASTAR/Basic.png&quot; alt=&quot;BASIC&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 2] BASIC ASTAR}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./ASTAR/JOB.png&quot; alt=&quot;JOB&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 3] JOB ASTAR}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[그림 2]와 [그림 3]는 Unity의 Profiler 기능을 이용해서 측정했다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;메트릭&lt;/th&gt;
&lt;th&gt;순차 A*&lt;/th&gt;
&lt;th&gt;병렬 A*&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;메인 스레드 시간&lt;/td&gt;
&lt;td&gt;74.11ms&lt;/td&gt;
&lt;td&gt;3.94ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;평균 FPS&lt;/td&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;$$\text{[표 1] A* 성능 비교 표}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[표 1]을 토대로 비교해보자면 메인 스레드 시간과 FPS의 상태를 본다면 얼마나 제안된 방법이 효과적인지 알 수 있다.&lt;/p&gt;
&lt;h1&gt;3. Unity에서 행동 모델링(FSM vs Behaviour Tree)&lt;/h1&gt;
&lt;p&gt;에이전트 기반 생태계 시뮬레이션에서는 보통 움직이는 에이전트가 중요하다. 이를 설계하기 위해 알고리즘, 강화학습 등을 활용해서 개발한다.
이 중 단순 알고리즘 기반으로 작동하는 에이전트를 개발할 때, 활용해볼 수 있는 방법론 2개에 대해서 장단점을 비교하고 활용 가능성에 대해 논한다.&lt;/p&gt;
&lt;h2&gt;3.1. FSM (Finite State Machine)&lt;/h2&gt;
&lt;h3&gt;3.1.1. 기본적인 FSM의 형태&lt;/h3&gt;
&lt;p&gt;유한 상태 기계는 시스템이 가질 수 있는 유한한 개수의 상태와 이들 간의 전이 규칙으로 구성되는 수학적 모델이다 [5].
전통적인 FSM은 상태(State), 전이(Transition), 이벤트(Event) 또는 조건(Condition)이라는 세 가지 핵심 요소로 구성된다.&lt;/p&gt;
&lt;p&gt;기본적인 FSM에서 각 상태는 독립적인 행동이나 조건을 나타내며,
특정 조건이 만족될 때 다른 상태로 전이한다.
전이는 즉시 발생하며, 시스템은 항상 정확히 하나의 상태에만 존재할 수 있다.
이러한 단순함이 FSM의 가장 큰 장점이자 특징이다.&lt;/p&gt;
&lt;p&gt;FSM으로 간단한 동물을 만들어보겠다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./State/FSM_1.png&quot; alt=&quot;BASIC&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 4] 간단한 FSM 다이어그램}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[그림 4]에서 처럼 FSM을 정의할 수 있다.
기본적인 동물의 행동을 간단히 만들어보았을 때, 각각의 조건에 따라 움직임의 상태를 관리할 수 있다.&lt;/p&gt;
&lt;h3&gt;3.1.2. 생태계 시뮬레이션 구현 시의 이점&lt;/h3&gt;
&lt;h3&gt;3.1.2.1. 직관적인 동물 행동 모델링&lt;/h3&gt;
&lt;p&gt;FSM의 명확한 상태 구분은 동물의 행동을 직관적으로 모델링할 수 있게 한다.
각 상태가 명확한 책임을 가지고 있어 코드를 이해하기 쉽고 수정하기 쉽다.
동물의 행동이 예상과 다를 때 해당 상태만 확인하면 원인을 쉽게 파악할 수 있다.&lt;/p&gt;
&lt;h3&gt;3.1.2.2. 높은 성능과 메모리 효율성&lt;/h3&gt;
&lt;p&gt;FSM은 현재 상태 정보만 유지하면 되므로 매우 적은 메모리를 사용한다.
생태계 시뮬레이션에서 시뮬레이션할 때 이러한 효율성은 매우 중요하다.
각 에이전트마다 최소한의 메모리만 사용하고 필요한 로직만 실행하므로 시스템 리소스를 효과적으로 활용할 수 있다.&lt;/p&gt;
&lt;h3&gt;3.1.2.3. 예측 가능한 행동과 균형 조정&lt;/h3&gt;
&lt;p&gt;FSM의 결정론적 특성을 통해 동물 행동의 균형을 쉽게 조정할 수 있다.
각 전이 조건이 명확하게 정의되어 있어 게임 밸런스를 조정할 때 어떤 값을 변경해야 하는지 쉽게 파악할 수 있다.&lt;/p&gt;
&lt;h3&gt;3.1.2.4. 단순한 행동의 효율적 구현&lt;/h3&gt;
&lt;p&gt;복잡하지 않은 동물 행동의 경우 FSM이 가장 효율적인 선택이다.
기본적인 생존 행동만 필요한 에이전트들을 구현할 때는 FSM의 단순성이 큰 장점이 된다.&lt;/p&gt;
&lt;h2&gt;3.2. Behaviour Tree&lt;/h2&gt;
&lt;h3&gt;3.2.1. 기본적인 Behaviour Tree&lt;/h3&gt;
&lt;p&gt;Behaviour Tree는 게임 AI와 로보틱스 분야에서 복잡한 행동을 계층적으로 구조화하기 위해 개발된 모델이다 [10].
트리 구조를 통해 복잡한 행동을 작은 단위의 행동들로 분해하고, 이들을 조합하여 더 복잡한 행동 패턴을 만들어낸다.&lt;/p&gt;
&lt;p&gt;기본적인 노드 타입들은 다음과 같이 구성된다:&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;기본 노드 타입:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Composite Nodes:&lt;/strong&gt; 자식 노드들을 실행하는 방식을 결정
&lt;ul&gt;
&lt;li&gt;Selector (OR): 자식 중 하나가 성공할 때까지 순서대로 실행&lt;/li&gt;
&lt;li&gt;Sequence (AND): 모든 자식이 성공해야 성공&lt;/li&gt;
&lt;li&gt;Parallel: 여러 자식을 동시에 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decorator Nodes:&lt;/strong&gt; 자식 노드의 행동을 수정
&lt;ul&gt;
&lt;li&gt;Inverter: 결과를 반대로 변경&lt;/li&gt;
&lt;li&gt;Repeater: 특정 조건까지 반복 실행&lt;/li&gt;
&lt;li&gt;Cooldown: 일정 시간 간격으로 실행 제한&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leaf Nodes:&lt;/strong&gt; 실제 행동이나 조건을 수행
&lt;ul&gt;
&lt;li&gt;Action: 구체적인 행동 실행&lt;/li&gt;
&lt;li&gt;Condition: 조건 검사&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public enum NodeState
{
    Running,
    Success,
    Failure 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;기본 형태의 Behaviour Tree는 매 프레임마다 루트에서 시작하여 트리를 탐색하며,
각 노드는 &lt;code&gt;NodeState&lt;/code&gt; 열거형으로 정의된 세 가지 상태 중 하나를 반환한다.
이러한 단순한 구조만으로도 복잡한 AI 행동을 표현할 수 있지만, 실제 게임이나 시뮬레이션에 적용하기에는 몇 가지 한계가 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./State/BT_1.png&quot; alt=&quot;BASIC&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 5] BT 실행 흐름 다이어그램}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;기본적인 동물의 흐름을 Behaviour Tree로 만들어보았다. Selector는 성공된 노드가 발생되면, 이후 뒤에 노드를 실행하지 않고 넘어가니
행동의 우선순위를 정할 때 사용하였고, Squence는 아예 여러 행동들이 연속적으로 행해져야할 때 필요해 한 행동을 정의할 때 묶어서 구현하였다.&lt;/p&gt;
&lt;p&gt;[그림 5]와 같이 구현하면, 주변에 포식자가 있으면 도망가고 만약 없다면 음식을 찾으려고 할 것이고 음식이 주변에 없다면 아무 곳이나 돌아다니게 되는 동물을
만들 수 있다.&lt;/p&gt;
&lt;h1&gt;4. 절차적 생성을 통한 생태계 생성 알고리즘&lt;/h1&gt;
&lt;p&gt;본 리포트는 절차적 환경 생성(PCG) 분야의 두 핵심 알고리즘,
Wave Function Collapse(WFC)와 Binary Space Partitioning(BSP)의 통합 모델을 실제 Unity C# 스크립트 구현을 통해 심층적으로 분석한다.
이론적 개념이 코드 로직으로 전환되는 과정과 두 알고리즘이 유기적으로 결합하여 시너지를 창출하는 방식에 초점을 맞춘다.
궁극적으로 본 통합 모델이 생태계 시뮬레이션을 위한 유연하고 통제 가능한 실험 환경을 구축하는 데 효과적인 도구임을 제시하고자 한다.&lt;/p&gt;
&lt;h2&gt;4.1. Wave Function Collapse (WFC)&lt;/h2&gt;
&lt;h3&gt;4.1.1. WFC의 핵심 원리&lt;/h3&gt;
&lt;p&gt;Wave Function Collapse는 국소적 규칙으로부터 전역적으로 일관성 있는 패턴을 생성하는 비반복적 타일링 알고리즘이다 [13].
그 핵심 원리는 양자역학의 개념에서 차용되었으며 [14],
생성 대상인 그리드의 각 셀은 초기에 모든 가능한 타일 상태를 포함하는 &apos;중첩(superposition)&apos; 상태로 시작한다.
이때 각 셀이 가질 수 있는 상태의 가짓수를 &apos;엔트로피(entropy)&apos;라고 정의한다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$H(c) = |S_c|$$&lt;/p&gt;
&lt;p&gt;$$\text{[수식 1] 엔트로피 정의}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[수식 1]에서 $$H(c)$$는 셀 $$c$$의 엔트로피, $$S_c$$는 셀 $$c$$의 가능한 상태 집합이다.&lt;/p&gt;
&lt;p&gt;알고리즘은 관측-전파(Observe-Propagate) 루프를 반복한다.
먼저 관측 단계에서는 전체 그리드에서 가장 낮은 엔트로피를 가진 셀,
즉 가장 적은 가능성을 가진 셀을 선택한다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$c^* = \arg\min_{c \in R, H(c) &amp;gt; 1} H(c)$$&lt;/p&gt;
&lt;p&gt;$$\text{[수식 2] 최저 엔트로피 셀 선택 수식}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[수식 2]에서 $$R$$은 현재 지역의 셀 집합, $$c^*$$는 선택된 셀이다.&lt;/p&gt;
&lt;p&gt;이어서 붕괴 단계에서는 선택된 셀의 상태를 여러 가능성 중 하나로 확정시키며,
이 선택은 각 타일의 가중치에 따른 확률적 선택이 될 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$P(t_i) = \frac{w_i}{\sum_{j \in S_c} w_j}$$&lt;/p&gt;
&lt;p&gt;$$\text{[수식 3] 확률적 선택 수식}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[수식 3]에서 $$P(t_i)$$는 타일 $$t_i$$가 선택될 확률, $$w_i$$는 타일 $$t_i$$의 가중치이다.&lt;/p&gt;
&lt;p&gt;마지막으로 전파 단계에서는 하나의 셀이 붕괴되면 그 결정이 새로운 제약 조건이 되어 인접 셀들에게 영향을 미치며,
붕괴된 셀의 인접 규칙에 위배되는 타일들을 주변 셀들의 가능성 목록에서 제거한다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$S_{c&apos;} \leftarrow S_{c&apos;} \cap {t | R(t_{collapsed}, t) = \text{true}}$$&lt;/p&gt;
&lt;p&gt;$$\text{[수식 4] 제약 전파 수식}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[수식 4]에서 $$S_{c&apos;}$$는 인접 셀 $$c&apos;$$의 상태 집합, $$R(t_1, t_2)$$는 두 타일 간의 인접 규칙이다.&lt;/p&gt;
&lt;p&gt;이 과정을 통해 주변 셀들의 엔트로피가 감소하며,
연쇄적인 제약 전파가 더 이상 변화가 없을 때까지 계속된다.
모든 셀의 엔트로피가 1이 될 때까지 이 루프를 반복하면,
국소적 인접 규칙만을 사용했음에도 불구하고 전체적으로는 매우 복잡하고 자연스러운 거대 패턴이 완성된다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$\forall c \in C: H(c) = 1$$&lt;/p&gt;
&lt;p&gt;$$\text{[수식 5] 알고리즘 종료 조건}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[수식 5]에서 $$C$$는 전체 셀 집합이다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./PCG/WFC.png&quot; alt=&quot;WFC Basic&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 6] 구현된 WFC의 생성된 생태계}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[그림 6]은 기본적으로 규칙 자체가 간단해지면서, 노이즈처럼 발생되는 것으로 보이게 된다.&lt;/p&gt;
&lt;h3&gt;4.1.2. 지역적 WFC 구현과 계산 복잡도 관리&lt;/h3&gt;
&lt;p&gt;제공된 스크립트에서 WFC는 BSP가 구획한 각 지역 내부의 세부 생태계를 묘사하는 데 사용되며,
이는 WFC의 계산 복잡도를 관리하는 핵심적인 최적화 전략이다. &lt;code&gt;GenerateRegionalWFC&lt;/code&gt; 함수가 이 과정을 총괄하며,
가장 불확실성이 낮은 셀을 찾는 관측 단계는 &lt;code&gt;FindLowestEntropyCellInRegion&lt;/code&gt; 함수를 통해 구현된다.&lt;/p&gt;
&lt;p&gt;이 함수는 지역 내의 모든 셀을 순회하면서 붕괴되지 않은 셀들 중에서 가능성 목록의 길이가 가장 짧은 셀을 찾는다.
동일한 엔트로피를 가진 셀이 여러 개 있을 경우, 모든 후보를 수집한 후 무작위로 하나를 선택함으로써 생성 결과의 다양성을 확보한다.
이러한 접근 방식은 결정론적 선택으로 인한 패턴의 경직성을 방지하는 중요한 역할을 한다.&lt;/p&gt;
&lt;p&gt;셀이 선택되어 &lt;code&gt;CollapseCellInRegion&lt;/code&gt; 함수를 통해 붕괴되면,
&lt;code&gt;PropagateConstraintsInRegion&lt;/code&gt; 함수가 전파 단계를 수행한다.
이 함수는 너비 우선 탐색(BFS) 방식의 큐를 사용하여 붕괴된 셀로부터 시작해 영향을 받는 모든 인접 셀들로 제약 조건을 전파한다.
각 셀의 WFCRule을 기준으로 인접한 미붕괴 셀들의 가능성 리스트를 갱신하여,
새로운 제약 조건에 위배되는 타일 옵션들을 체계적으로 제거한다.&lt;/p&gt;
&lt;p&gt;지역적 WFC 접근법의 가장 큰 장점은 거대한 맵 전체를 단일 WFC 시스템으로 처리할 때 발생하는 조합 폭발 문제를 원천적으로 방지한다는 점이다.
전체 맵이 100x100 크기라면 10,000개의 셀을 동시에 처리해야 하지만,
이를 10개의 지역으로 나누면 각각 평균 1,000개의 셀만 처리하면 되어 계산 복잡도가 현저히 감소한다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$O(n^2) \rightarrow O(k \cdot (\frac{n}{k})^2) = O(\frac{n^2}{k})$$&lt;/p&gt;
&lt;p&gt;$$\text{[수식 6] 계산 복잡도 감소}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[수식 6]에서 $$n$$은 전체 맵 크기, $$k$$는 지역 개수이다.&lt;/p&gt;
&lt;p&gt;동시에 각 지역의 고유한 생태학적 특성을 효과적으로 부여할 수 있는 기반이 된다.&lt;/p&gt;
&lt;h2&gt;4.2. Binary Space Partitioning (BSP)&lt;/h2&gt;
&lt;h3&gt;4.2.1. BSP의 수학적 원리와 공간 조직화&lt;/h3&gt;
&lt;p&gt;Binary Space Partitioning(BSP)은 주어진 공간을 분할 평면을 사용해 재귀적으로 두 개의 부분 공간으로 나누는 알고리즘이다 [6].
이 과정은 이진 트리 자료구조를 형성하는데, 각 노드는 공간의 한 영역을,
자식 노드들은 부모 노드가 분할된 두 개의 하위 영역을 나타낸다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$S_i = {S_{i,left}, S_{i,right}} \text{ where } S_{i,left} \cap S_{i,right} = \emptyset$$&lt;/p&gt;
&lt;p&gt;$$\text{[수식 7] BSP 재귀적 분할}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[수식 7]에서 $$S_i$$는 깊이 $$i$$에서의 공간 집합, $$S_{i,left}$$와 $$S_{i,right}$$는 분할된 두 부분 공간이다.&lt;/p&gt;
&lt;p&gt;분할은 특정 종료 조건이 충족될 때까지 반복되며,
일반적으로 영역의 최소 크기나 트리의 최대 깊이를 기준으로 한다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$\text{Stop if: } \min(w, h) &amp;lt; T_{min} \text{ or } d &amp;gt; D_{max}$$&lt;/p&gt;
&lt;p&gt;$$\text{[수식 8] BSP 종료 조건}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[수식 8]에서 $$w$$, $$h$$는 영역의 폭과 높이, $$T_{min}$$은 최소 임계값, $$d$$는 현재 깊이, $$D_{max}$$는 최대 깊이이다.&lt;/p&gt;
&lt;p&gt;이 알고리즘의 본질적인 계층성과 분할-정복(Divide and Conquer) 접근 방식은 대규모 공간을 관리 가능한 단위로 체계적으로 조직화하는 데 매우 효율적이다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;$$|N| = 2^{d+1} - 1$$&lt;/p&gt;
&lt;p&gt;$$\text{[수식 9] BSP 트리 크기}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[수식 9]에서 $$|N|$$은 트리의 총 노드 수, $$d$$는 트리의 최대 깊이이다.&lt;/p&gt;
&lt;p&gt;전통적으로 게임의 던전이나 건물 내부의 방과 복도를 생성하는 데 사용되어 왔으나,
본 연구의 맥락에서는 서로 다른 기후와 특징을 가진 거대 생물군계를 구획하는 이상적인 도구로 활용된다.&lt;/p&gt;
&lt;p&gt;BSP의 핵심적 장점은 생성된 구조가 자연스럽게 계층적 관리와 탐색이 가능하다는 점이다.
트리의 각 레벨은 서로 다른 규모의 공간 조직화를 나타내며,
루트 노드에서는 전체 대륙 수준의 거시적 구조를,
잎 노드에서는 개별 생태 지역 수준의 미시적 구조를 다룰 수 있다.
이러한 다중 스케일 접근법은 복잡한 생태계의 자연스러운 위계 구조를 모방하는 데 특히 유용하다.&lt;/p&gt;
&lt;h1&gt;5. Implementation&lt;/h1&gt;
&lt;h2&gt;5.1. 본 A* 구현에서 확장점&lt;/h2&gt;
&lt;h3&gt;5.1.1 시스템 아키텍처 설계&lt;/h3&gt;
&lt;h3&gt;5.1.1. 중앙화된 경로탐색 관리자&lt;/h3&gt;
&lt;p&gt;제안된 시스템의 핵심은 AStarManager 클래스로 구현된 중앙화된 경로탐색 관리 시스템이다. 이 시스템은 Singleton 패턴을
통해 전역적으로 접근 가능한 단일 인스턴스를 제공하며,
모든 에이전트의 경로 요청을 통합적으로 관리한다.&lt;/p&gt;
&lt;p&gt;시스템의 주요 구성 요소는 다음과 같다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;공유 격자 구조(Shared Grid): 모든 에이전트가 공통으로 사용하는 지형 정보&lt;/li&gt;
&lt;li&gt;요청 대기열(Request Queue): 에이전트들의 경로탐색 요청을 순차적으로 저장&lt;/li&gt;
&lt;li&gt;배치 처리 배열: 고정 크기의 배치 단위로 요청을 처리하기 위한 자료구조&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;private NativeArray&amp;lt;PathNode&amp;gt; grid;
private Queue&amp;lt;PathRequest&amp;gt; requestQueue;
private NativeArray&amp;lt;PathRequest&amp;gt; batchRequests;
private NativeArray&amp;lt;PathResult&amp;gt; batchResults;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.1.2. 배치 기반 처리 메커니즘&lt;/h3&gt;
&lt;p&gt;시스템은 개별 요청을 즉시 처리하는 대신, 일정한 크기의 배치로 묶어서 처리한다.
기본 배치 크기는 128개로 설정되어 있으며, 이는 메모리 사용량과 처리 지연시간 사이의 균형점을 고려하여 결정된 값이다.
배치 처리 방식은 Job System의 오버헤드를 최소화하고 일관된 성능을 보장한다 [12].&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private void ProcessBatch()
{
    currentJobHandle.Complete();
    ProcessResults();
    
    int count = math.min(batchSize, requestQueue.Count);
    if (count == 0) return;
    
    AStar job = new AStar {
        requests = batchRequests,
        grid = grid,
        gridWidth = gridWidth,
        gridHeight = gridHeight,
        maxPathLength = maxPathLength,
        results = batchResults,
        pathBuffer = pathBuffer
    };
    
    currentJobHandle = job.Schedule(count, math.max(1, count / 4));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.1.3. 요청 중복 제거 및 생명주기 관리&lt;/h3&gt;
&lt;p&gt;시스템은 동일 에이전트로부터의 빈번한 요청을 방지하기 위해 시간 기반 필터링을 구현한다.
각 에이전트는 설정된 최소 간격(pathUpdateInterval) 내에서 하나의 요청만 처리되며,
이미 처리 중인 요청이 있는 경우 새로운 요청은 무시된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (lastRequestTime.TryGetValue(agentId, out float lastTime))
{
    if (Time.time - lastTime &amp;lt; pathUpdateInterval * 0.5f)
        return;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또한 오래된 요청에 대한 정리 메커니즘을 통해 메모리 누수를 방지하고 시스템의 장기적 안정성을 보장한다.&lt;/p&gt;
&lt;h3&gt;5.1.2 Burst 최적화된 병렬 A* 구현&lt;/h3&gt;
&lt;h3&gt;5.1.2.1. Job System 인터페이스 구현&lt;/h3&gt;
&lt;p&gt;A* 알고리즘은 IJobParallelFor 인터페이스를 구현한 구조체로 작성되었다.
이는 Unity의 Job System이 자동으로 작업을 여러 스레드에 분산 실행할 수 있게 한다 [2].
Burst 컴파일러의 최적화를 최대한 활용하기 위해 부동소수점 연산 모드를 Fast로, 정밀도를 Low로 설정했다 [12].&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[BurstCompile(CompileSynchronously = true, FloatMode = FloatMode.Fast,
 FloatPrecision = FloatPrecision.Low)]
public struct AStar : IJobParallelFor
{
    public void Execute(int index)
    {
        PathRequest request = requests[index];
        PathResult result = new PathResult
        {
            requestId = request.requestId,
            agentId = request.agentId,
            success = false,
            pathStartIndex = index * maxPathLength,
            pathLength = 0
        };
        
        if (request.start.x == request.goal.x &amp;amp;&amp;amp; request.start.y == request.goal.y)
        {
            result.success = true;
            result.pathLength = 1;
            pathBuffer[result.pathStartIndex] = request.goal;
            results[index] = result;
            return;
        }
        
        float distance = math.distance(request.start, request.goal);
        
        SimpleAStar(index, request, ref result);
        
            
        results[index] = result;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.1.2.2. 메모리 효율적인 자료구조 활용&lt;/h3&gt;
&lt;p&gt;각 Job 실행 시 필요한 임시 자료구조는 Allocator.Temp를 사용하여 할당하고,
Job 완료 시 자동으로 해제되도록 구현했다.
이는 메모리 누수를 방지하고 가비지 컬렉션의 압박을 줄이는 효과를 가져온다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private void SimpleAStar(int jobIndex, PathRequest request, ref PathResult result)
{
    const int MAX_ITERATIONS = 1000;
    const int MAX_OPEN_SET_SIZE = 512;
    
    NativeMinHeap openSet = new NativeMinHeap(MAX_OPEN_SET_SIZE, Allocator.Temp);
    NativeHashSet&amp;lt;int&amp;gt; closedSet = new NativeHashSet&amp;lt;int&amp;gt;(MAX_OPEN_SET_SIZE, Allocator.Temp);
    NativeHashMap&amp;lt;int, int&amp;gt; cameFrom = new NativeHashMap&amp;lt;int, int&amp;gt;(MAX_OPEN_SET_SIZE, Allocator.Temp);
    NativeHashMap&amp;lt;int, int&amp;gt; gScore = new NativeHashMap&amp;lt;int, int&amp;gt;(MAX_OPEN_SET_SIZE, Allocator.Temp);
    
    
    openSet.Dispose();
    closedSet.Dispose();
    cameFrom.Dispose();
    gScore.Dispose();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.1.2.3. 대각선 이동 처리 및 코너 컷팅 방지&lt;/h3&gt;
&lt;p&gt;8방향 이동을 지원하되, 대각선 이동 시 코너 컷팅을 방지하기 위한 검증 로직을 구현했다.
대각선 방향으로 이동할 때 인접한 두 직교 방향이 모두 통행 가능한 경우에만 이동을 허용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (dir % 2 == 1)
{
    int2 checkPos1 = GetNeighborPosition(currentPos, (dir - 1) % 8);
    int2 checkPos2 = GetNeighborPosition(currentPos, (dir + 1) % 8);
    
    if (!IsValidPosition(checkPos1) || !IsValidPosition(checkPos2))
        continue;
        
    int checkIndex1 = GetIndex(checkPos1);
    int checkIndex2 = GetIndex(checkPos2);
    
    if (!grid[checkIndex1].isWalkable || !grid[checkIndex2].isWalkable)
        continue;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.1.3. 경로 후처리 및 최적화&lt;/h3&gt;
&lt;h3&gt;5.1.3.1. 경로 평활화 알고리즘&lt;/h3&gt;
&lt;p&gt;A* 알고리즘이 생성하는 격자 기반 경로는 계단 형태의 부자연스러운 모양을 가질 수 있다.
이를 개선하기 위해 시선 기반 경로 평활화 알고리즘을 구현했다.
이 알고리즘은 현재 위치에서 가장 멀리 보이는 지점까지 직선으로 연결하여 웨이포인트 수를 줄이고 보다 자연스러운 경로를 생성한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private List&amp;lt;int2&amp;gt; SmoothPath(List&amp;lt;int2&amp;gt; path)
{
    if (path.Count &amp;lt;= 2)
        return path;
    
    List&amp;lt;int2&amp;gt; smoothed = new List&amp;lt;int2&amp;gt;();
    smoothed.Add(path[0]);
    
    int currentIndex = 0;
    while (currentIndex &amp;lt; path.Count - 1)
    {
        int farthestVisible = currentIndex + 1;
        
        for (int i = currentIndex + 2; i &amp;lt; path.Count; i++)
        {
            if (HasLineOfSight(path[currentIndex], path[i]))
                farthestVisible = i;
            else
                break;
        }
        
        smoothed.Add(path[farthestVisible]);
        currentIndex = farthestVisible;
    }
    
    return smoothed;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.1.3.2. 브레젠험 알고리즘을 이용한 시선 검사&lt;/h3&gt;
&lt;p&gt;두 지점 사이의 시선 검사는 브레젠험(Bresenham) 직선 알고리즘을 활용하여 구현했다 [15].
이 방법은 부동소수점 연산을 피하고 정수 연산만으로 두 점 사이의 모든 격자를 순회하면서 장애물 존재 여부를 확인할 수 있어 계산 효율성이 높다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private bool HasLineOfSight(int2 start, int2 end)
{
    int x0 = start.x; int y0 = start.y;
    int x1 = end.x; int y1 = end.y;
    
    int dx = math.abs(x1 - x0);
    int dy = math.abs(y1 - y0);
    int x = x0; int y = y0;
    int n = 1 + dx + dy;
    int x_inc = (x1 &amp;gt; x0) ? 1 : -1;
    int y_inc = (y1 &amp;gt; y0) ? 1 : -1;
    int error = dx - dy;
    dx *= 2; dy *= 2;
    
    for (; n &amp;gt; 0; --n)
    {
        if (!IsValidPosition(new int2(x, y)) || !IsWalkable(new int2(x, y)))
            return false;
        
        if (error &amp;gt; 0)
        {
            x += x_inc;
            error -= dy;
        }
        else
        {
            y += y_inc;
            error += dx;
        }
    }
    
    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.1.4. 동적 환경 지원 및 유지보수성&lt;/h3&gt;
&lt;h3&gt;5.1.4.1. 실시간 장애물 업데이트&lt;/h3&gt;
&lt;p&gt;시뮬레이션 환경에서 장애물의 생성, 제거, 이동이 빈번하게 발생할 수 있다.
이에 대응하기 위해 격자의 특정 영역만을 선택적으로 업데이트하는 메커니즘을 구현했다.
전체 격자를 재생성하는 대신 영향받는 영역만을 업데이트하여 성능 저하를 최소화한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void UpdateObstacle(Vector3 worldPos, float radius, bool isWalkable)
{
    int2 gridPos = WorldToGrid(worldPos);
    int gridRadius = Mathf.CeilToInt(radius / cellSize);
    
    for (int x = -gridRadius; x &amp;lt;= gridRadius; x++)
    {
        for (int y = -gridRadius; y &amp;lt;= gridRadius; y++)
        {
            int2 checkPos = gridPos + new int2(x, y);
            if (!IsValidPosition(checkPos))
                continue;
                
            float distance = math.distance(gridPos, checkPos);
            if (distance &amp;lt;= gridRadius)
            {
                int index = checkPos.x + checkPos.y * gridWidth;
                PathNode node = grid[index];
                node.isWalkable = isWalkable;
                grid[index] = node;
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.1.4.2. 메모리 관리 및 시스템 안정성&lt;/h3&gt;
&lt;p&gt;시스템의 장기적 안정성을 위해 여러 단계의 메모리 관리 메커니즘을 구현했다.
오래된 경로 요청은 주기적으로 정리되며, NativeArray와 같은 비관리 메모리 자원은 시스템 종료 시 명시적으로 해제된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnDestroy()
{
    currentJobHandle.Complete();
    
    if (grid.IsCreated) grid.Dispose();
    if (batchRequests.IsCreated) batchRequests.Dispose();
    if (batchResults.IsCreated) batchResults.Dispose();
    if (pathBuffer.IsCreated) pathBuffer.Dispose();
    
    base.OnDestroy();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5.2. 본 FSM 구현에서의 확장점&lt;/h2&gt;
&lt;h3&gt;5.2.1. 제네릭 기반 타입 안전성&lt;/h3&gt;
&lt;p&gt;본 구현에서는 C#의 제네릭 시스템을 활용하여 타입 안전성과 재사용성을 크게 향상시켰다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface IState&amp;lt;T&amp;gt; 
{
    void Enter(T owner);
    void Execute(T owner);
    void Exit(T owner);
}

public class StateMachine&amp;lt;T&amp;gt;
{
    private T owner;
    private IState&amp;lt;T&amp;gt; currentState;
    private IState&amp;lt;T&amp;gt; previousState;
    private Dictionary&amp;lt;Type, IState&amp;lt;T&amp;gt;&amp;gt; states;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;제네릭 &lt;code&gt;&amp;lt;T&amp;gt;&lt;/code&gt; 매개변수를 통해 어떤 타입의 객체에도 적용할 수 있는 범용적인 상태 기계를 구현했다.&lt;/p&gt;
&lt;h3&gt;5.2.2. 명시적 생명주기 관리&lt;/h3&gt;
&lt;p&gt;전통적인 FSM과 달리 본 구현은 상태의 생명주기를 명시적으로 관리한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void ChangeState&amp;lt;TState&amp;gt;() where TState : IState&amp;lt;T&amp;gt;
{
    Type stateType = typeof(TState);
    if (states.ContainsKey(stateType))
    {
        if (currentState != null)
        {
            currentState.Exit(owner);  
            previousState = currentState;
        }
        currentState = states[stateType];
        currentState.Enter(owner);       
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Enter&lt;/code&gt;, &lt;code&gt;Execute&lt;/code&gt;, &lt;code&gt;Exit&lt;/code&gt; 메서드를 통해 상태의 시작, 실행, 종료 단계를 명확히 구분한다.
이는 각 단계에서 필요한 초기화와 정리 작업을 체계적으로 수행할 수 있게 한다.&lt;/p&gt;
&lt;h3&gt;5.2.3. 딕셔너리 기반 상태 관리&lt;/h3&gt;
&lt;p&gt;상태들을 &lt;code&gt;Dictionary&amp;lt;Type, IState&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt;로 관리하는 방식은 성능과 유연성 측면에서 중요한 개선이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void AddState(IState&amp;lt;T&amp;gt; state)
{
    states[state.GetType()] = state;
}

public void ChangeState&amp;lt;TState&amp;gt;() where TState : IState&amp;lt;T&amp;gt; {
    Type stateType = typeof(TState);
    if (states.ContainsKey(stateType))
    {
        // code ...
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;타입을 키로 사용하는 딕셔너리 구조를 통해 O(1) 시간 복잡도로 상태에 접근할 수 있다.
또한 컴파일 타임에 상태 타입을 검증할 수 있어 잘못된 상태 참조로 인한 오류를 방지한다.&lt;/p&gt;
&lt;h3&gt;5.2.4. 이전 상태 추적 기능&lt;/h3&gt;
&lt;p&gt;많은 기본 FSM 구현에서 제공하지 않는 이전 상태 추적 기능을 구현했다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void RevertToPreviousState()
{
    if (previousState != null)
    {
        if (currentState != null)
        {
            currentState.Exit(owner);
        }
        currentState = previousState;
        currentState.Enter(owner);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 기능을 통해 일시적인 상태 변경 후 원래 상태로 돌아가는 패턴을 쉽게 구현할 수 있다.&lt;/p&gt;
&lt;h2&gt;5.3. 본 Behaviour Tree 구현에서의 확장점&lt;/h2&gt;
&lt;h3&gt;5.3.1. 노드 생명주기 관리 시스템&lt;/h3&gt;
&lt;p&gt;본 구현에서는 기본적인 행동 트리 구조에서 한 걸음 나아가 노드의 생명주기를 명시적으로 관리하는 시스템을 도입했다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public virtual NodeState Evaluate()
{
    bool isCurrentlyRunning = (state == NodeState.Running);
    
    state = DoEvaluate();
    
    
    if (wasRunning &amp;amp;&amp;amp; state != NodeState.Running)
    {
        OnExitNode();
    }
    else if (!wasRunning &amp;amp;&amp;amp; state == NodeState.Running)
    {
        OnEnterNode();
    }
    
    wasRunning = (state == NodeState.Running);
    return state;
}

protected virtual void OnEnterNode() { }
protected virtual void OnExitNode() { }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;wasRunning&lt;/code&gt; 플래그를 통해 이전 프레임의 실행 상태를 추적하고,
상태 변화에 따라 &lt;code&gt;OnEnterNode&lt;/code&gt;와 &lt;code&gt;OnExitNode&lt;/code&gt; 메서드를 자동으로 호출한다.
이는 전통적인 행동 트리에서는 제공하지 않는 중요한 기능이다.
많은 AI 행동들은 시작 시점에서 초기화 작업이 필요하고, 종료 시점에서 정리 작업이 필요하다.
예를 들어, 이동 행동을 시작할 때는 경로를 계산하고, 종료할 때는 애니메이션을 정리해야 한다.&lt;/p&gt;
&lt;h3&gt;5.3.2. 동적 중단 메커니즘&lt;/h3&gt;
&lt;p&gt;기본적인 행동 트리는 우선순위 변경에 따른 행동 중단을 처리하는 표준적인 방법을 제공하지 않는다.
본 구현에서는 재귀적 중단 시스템을 구축했다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public virtual void Interrupt()
{
    
    foreach (Node child in children) 
        child.Interrupt();

    if (wasRunning)
    {
        OnInterrupted?.Invoke();
        OnExitNode();
        state = NodeState.Failure;
        wasRunning = false;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 메커니즘은 높은 우선순위의 행동이 활성화될 때 현재 실행 중인 모든 하위 행동을 일관되게 중단할 수 있게 한다.
&lt;code&gt;OnInterrupted&lt;/code&gt; 액션 델리게이트를 통해 외부 시스템에서 중단 이벤트를 감지할 수 있도록 하여,
행동 트리와 다른 게임 시스템 간의 통합을 용이하게 했다.&lt;/p&gt;
&lt;h3&gt;5.3.3. 게임 엔진 통합 최적화&lt;/h3&gt;
&lt;p&gt;Unity와의 긴밀한 통합을 위해 &lt;code&gt;ActionNode&lt;/code&gt; 클래스에서 핵심 컴포넌트들에 직접 접근할 수 있도록 구현했다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class ActionNode : Node
{
    protected Transform transform;
    protected Animator animator;
    protected Rigidbody2D rigidbody;
    protected bool isActive = false;
    
    public ActionNode(Transform transform, bool forceComplete = false, string cooldownKey = null)
    {
        this.transform = transform;
        this.mustComplete = forceComplete;
        this.cooldownKey = cooldownKey;
        
        animator = transform.GetComponent&amp;lt;Animator&amp;gt;();
        rigidbody = transform.GetComponent&amp;lt;Rigidbody2D&amp;gt;();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이러한 설계는 AI 프로그래머가 복잡한 인터페이스나 중간 계층 없이 직접적으로 게임 오브젝트를 조작할 수 있게 한다.
생태계 시뮬레이션에서 동물의 이동, 애니메이션, 물리 상호작용을 즉시 제어할 수 있어 개발 효율성이 크게 향상된다.&lt;/p&gt;
&lt;h3&gt;5.3.4. 강제 완료 메커니즘&lt;/h3&gt;
&lt;p&gt;특정 행동이 중단되지 않고 완료되어야 하는 상황을 처리하기 위한 기능이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public sealed override NodeState Evaluate()
{
    
    if (mustComplete &amp;amp;&amp;amp; isActive &amp;amp;&amp;amp; state == NodeState.Running) 
    {
        NodeState result = DoEvaluate();
        if (wasRunning &amp;amp;&amp;amp; result != NodeState.Running) {
            OnExitNode();
            isActive = false;
        }
        
        if (result == NodeState.Success &amp;amp;&amp;amp; !string.IsNullOrEmpty(cooldownKey)) {
            Cooldown.StartCooldown(cooldownKey);
        }
        state = result;
        wasRunning = (state == NodeState.Running);
        return result;
    }
    
    NodeState evaluationResult = base.Evaluate();
    if (evaluationResult == NodeState.Success &amp;amp;&amp;amp; !string.IsNullOrEmpty(cooldownKey)) {
        Cooldown.StartCooldown(cooldownKey);
    }
    return evaluationResult;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;mustComplete&lt;/code&gt; 플래그를 통해 공격이나 중요한 애니메이션과 같이 중간에 중단되면 안 되는 행동들을 보호할 수 있다.
이는 자연스러운 동물 행동 구현에 필수적인 기능이다.&lt;/p&gt;
&lt;h3&gt;5.3.5. 쿨다운 시스템&lt;/h3&gt;
&lt;p&gt;행동의 빈도를 제어하여 더욱 현실적인 AI 행동을 구현한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected string cooldownKey = null;

if (result == NodeState.Success &amp;amp;&amp;amp; !string.IsNullOrEmpty(cooldownKey)) {
    Cooldown.StartCooldown(cooldownKey);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;문자열 키 기반의 쿨다운 시스템을 통해 서로 다른 행동들이 독립적인 쿨다운을 가지거나,
관련된 행동들이 같은 쿨다운을 공유할 수 있다.&lt;/p&gt;
&lt;h3&gt;5.3.6. 물리 시뮬레이션 분리&lt;/h3&gt;
&lt;p&gt;Unity의 고정 업데이트 사이클과 일반 업데이트 사이클을 분리하여 처리한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public virtual void FixedEvaluate()
{
    foreach(Node child in children) 
        child.FixedEvaluate();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;물리와 관련된 AI 로직을 별도로 처리할 수 있는 확장점을 제공한다.
생태계 시뮬레이션에서 동물의 이동이나 충돌 처리와 같은 물리 기반 행동을 정확하게 구현할 수 있다.&lt;/p&gt;
&lt;h2&gt;5.4. 본 절차적 생성의 확장점&lt;/h2&gt;
&lt;h3&gt;5.4.1. bspInfluence를 통한 알고리즘 간 균형 조절&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./PCG/BSP_influ.png&quot; alt=&quot;BSP Influence&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 6] BspInfluence에 따른 생태계 변화 비교}$$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;[그림 6]에서 확인할 수 있듯이, 두 알고리즘 간의 영향력을 동적으로 조절할 수 있는 &lt;code&gt;bspInfluence&lt;/code&gt; 파라미터이다.
이 값은 0에서 1 사이의 실수로, 0에 가까울수록 순수한 WFC의 자유로운 패턴 생성을, 1에 가까울수록 BSP의 구조적 제약을 강하게 반영한다.
이러한 연속적 조절 방식은 이산적인 알고리즘 선택보다 훨씬 유연하고 세밀한 제어를 가능하게 한다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bspInfluence&lt;/code&gt;의 효과는 &lt;code&gt;CollapseCellInRegion&lt;/code&gt; 함수 내에서 구체적인 가중치 로직으로 구현된다.
WFC의 셀 붕괴 과정에서 각 타일 타입의 선택 확률을 계산할 때, BSP에 의해 할당된 지역적 바이옴에 추가적인 가중치를 부여하는 것이다.
이 지역에 허용된 바이옴일 경우 기본 가중치에 &lt;code&gt;(1 + bspInfluence)&lt;/code&gt; 배수를 곱하고,
주도 바이옴일 경우에는 추가로 &lt;code&gt;(1 + bspInfluence × 0.5)&lt;/code&gt; 배수를 더 곱한다.&lt;/p&gt;
&lt;p&gt;이러한 수식적 접근은 &lt;code&gt;bspInfluence&lt;/code&gt; 값이 증가함에 따라 지역적 바이옴의 선택 확률이 지수적으로 증가하도록 설계되었다.
예를 들어 &lt;code&gt;bspInfluence&lt;/code&gt;가 0.5일 때, 허용된 바이옴은 1.5배의 가중치를,
주도 바이옴은 1.75배의 가중치를 받는다. 만약 &lt;code&gt;bspInfluence&lt;/code&gt;가 0.9로 증가하면 각각 1.9배와 2.35배의 가중치를 받게 되어,
BSP의 구조적 의도가 WFC의 확률적 생성 과정을 강력하게 영향을 미치게 된다.&lt;/p&gt;
&lt;h3&gt;5.4.2. 시드 배치 전략과 구조적 안정성 확보&lt;/h3&gt;
&lt;p&gt;높은 &lt;code&gt;bspInfluence&lt;/code&gt; 값에서는 단순한 가중치 조정을 넘어서 &lt;code&gt;PlaceSeedCells&lt;/code&gt; 함수를 통한 시드 배치 전략이 활용된다.
이 함수는 각 지역의 중심부에 해당 지역의 주도 바이옴으로 이미 붕괴된 시드 셀들을 미리 배치함으로써,
WFC 생성 과정이 시작되기 전에 구조적 앵커 포인트를 제공한다.&lt;/p&gt;
&lt;p&gt;시드의 배치 범위는 지역 크기의 20%에 &lt;code&gt;bspInfluence&lt;/code&gt; 값을 곱한 반지름으로 결정되므로,
BSP의 영향력이 클수록 더 넓은 영역에 시드가 배치된다.
이는 WFC의 엔트로피 기반 생성이 무작위성에만 의존하지 않고 BSP가 설계한 거시적 구조를 중심으로 전개되도록 유도하는 핵심 메커니즘이다.
시드 셀들로부터 시작된 제약 전파는 주변 지역으로 해당 바이옴의 특성을 확산시키며,
결과적으로 지역의 중심부는 강한 정체성을, 경계부는 상대적으로 혼재된 특성을 갖게 된다.&lt;/p&gt;
&lt;p&gt;이러한 중심-경계 구조는 실제 생태학에서 관찰되는 핵심지역(core area)과 전이대(ecotone)의 개념을 자연스럽게 모방한다.
핵심지역에서는 해당 생태계의 전형적 특성이 강하게 나타나고,
인접한 생태계 간의 경계에서는 두 생태계의 특성이 혼재되어 나타나는 현상이 컴퓨터 알고리즘으로 자연스럽게 재현되는 것이다.&lt;/p&gt;
&lt;h3&gt;5.4.3. 경계 블렌딩을 통한 자연스러운 전이 구현&lt;/h3&gt;
&lt;p&gt;알고리즘적 생성만으로는 종종 인위적이거나 급작스러운 전이가 나타날 수 있다.
특히 BSP의 명확한 경계선은 자연 생태계에서는 거의 관찰되지 않는 급격한 바이옴 변화를 만들어낼 수 있다.
이를 해결하기 위해 &lt;code&gt;ApplyBoundaryBlending&lt;/code&gt; 함수는 구역 경계 근처에서 가중 평균을 사용한 속성 블렌딩을 수행한다.&lt;/p&gt;
&lt;p&gt;블렌딩 과정은 각 타일의 구역 경계로부터의 거리를 기준으로 한다. &lt;code&gt;blendingRadius&lt;/code&gt;
값 이하의 거리에 있는 모든 타일에 대해,
주변 타일들의 생태학적 속성을 거리 기반 가중치로 평균화한다.
가중치는 거리에 반비례하므로 가까운 타일의 영향이 더 크게 반영되며, 이는 자연스러운 확산 효과를 만들어낸다.&lt;/p&gt;
&lt;p&gt;주목할 점은 타일의 타입 자체는 변경되지 않고 fertility, moisture, temperature와 같은 수치적 속성만 블렌딩된다는 것이다.
즉, 사막 타일이 숲 타일로 바뀌지는 않지만 인접한 숲 지역의 영향으로 습도가 약간 증가하고 온도가 약간 감소할 수 있다.
이러한 섬세한 접근 방식은 바이옴의 기본적 정체성은 유지하면서도 경계에서의 급격한 속성 변화를 완화하여 생태계 전이를 구현한다.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./PCG/Blend.png&quot; alt=&quot;Blending&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$$\text{[그림 7] Blending 후처리 전후 비교} $$&lt;/p&gt;
&lt;p&gt;&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;5.4.4. 고립 타일 정리와 연결성 개선&lt;/h3&gt;
&lt;p&gt;절차적 생성의 확률적 특성으로 인해 주변과 완전히 다른 특성을 가진 고립된 타일들이 종종 생성된다.
이러한 고립 타일들은 시각적으로 부자연스러울 뿐만 아니라 생태학적으로도 비현실적이다.
&lt;code&gt;CleanupIsolatedTiles&lt;/code&gt; 함수는 각 타일 주변 8방향을 검사하여 자신과 동일한 타입의 이웃이 1개 이하인 경우 고립된 것으로 판단하고,
해당 타일을 주변에서 가장 빈번하게 나타나는 타일 타입으로 교체한다.&lt;/p&gt;
&lt;p&gt;이러한 후처리는 단순해 보이지만 생성 품질에 미치는 영향은 상당하다.
고립 타일의 제거는 각 바이옴 영역의 연속성과 응집성을 높여 더욱 자연스럽고 설득력 있는 지형을 만들어낸다.
동시에 시각적 노이즈를 줄여 전체적인 맵의 가독성을 향상시킨다.&lt;/p&gt;
&lt;p&gt;물 타일에 대해서는 특별한 연결성 개선 로직이 적용된다. &lt;code&gt;ImproveWaterConnectivity&lt;/code&gt; 함수는 기존의 물 타일들을 식별한 후,
일정 범위 내에 있는 다른 물 타일들을 선형 경로로 연결한다.
이는 강과 호수의 자연스러운 수계망을 모방한 것으로, 분리된 물 영역들을 유기적으로 연결하여 더욱 현실적인 수문학적 구조를 만들어낸다.
연결 과정에서는 기존의 풀이나 숲 지역만을 물로 변환하여 산이나 사막 같은 부적절한 지역을 통과하지 않도록 제한한다.&lt;/p&gt;
&lt;h3&gt;5.4.5. 개선 방안&lt;/h3&gt;
&lt;p&gt;WFC는 전체적인 구조를 잘 담아내는데 문제가 있다.
결과를 보면 알 수 있듯이 아직까지 &apos;자연스럽다&apos; 라고 생각이 들지는 않는다. 또한 아직 자연이라고는 생각이 들지 않는다. 나무, 풀 등 식생이 존재하지 않으며, 생성 로직 또한 소개되지 않았다.
그래서 자연스러운 생태계 생성과 더불어 식생을 만드는데 필요한 알고리즘을 [3], [4]의 논문을 기반으로 구현을하여 개선할 수 있다.&lt;/p&gt;
&lt;p&gt;절차적 생성으로 식생을 생태학적 원리에 기반하여 생성하며, 강화학습을 통해서 자연스러운 환경 타일 등을 생성하도록 만들어 현재의 구현 문제점을 보완한다.&lt;/p&gt;
&lt;h1&gt;6. 결론 및 향후 연구 방향&lt;/h1&gt;
&lt;h2&gt;6.1. 연구 성과 및 기여점&lt;/h2&gt;
&lt;h3&gt;6.1.1. 성능 최적화 달성&lt;/h3&gt;
&lt;p&gt;본 리포트에서 제안한 Unity Job System 기반 중앙화 병렬 A* 시스템은 기존 순차 처리 방식 대비 &lt;strong&gt;18.8배의 성능 향상&lt;/strong&gt;을 달성했다.
메인 스레드 처리 시간을 74.11ms에서 3.94ms로 단축하고 평균 FPS를 43에서 120으로 개선한 것은 대규모 생태계 시뮬레이션의 실시간 처리 가능성을 크게 높인 성과다.&lt;/p&gt;
&lt;p&gt;특히 배치 처리 방식과 공유 자료구조를 통한 메모리 효율성 확보는 수천 개체의 동시 시뮬레이션을 가능하게 하는 핵심 기반 기술이 되었다.
이는 기존 NetLogo, Repast 같은 플랫폼이 천 개체 수준에서 성능 저하를 보이는 한계를 극복한 것이다 [16].&lt;/p&gt;
&lt;h3&gt;6.1.2. 유연한 행동 모델링 프레임워크 구축&lt;/h3&gt;
&lt;p&gt;FSM과 Behaviour Tree의 심층적 비교 분석을 통해 에이전트 복잡도와 환경 특성에 따른 &lt;strong&gt;적응적 선택 기준&lt;/strong&gt;을 제시했다.
단순 에이전트에는 FSM의 효율성을, 복잡한 에이전트에는 Behaviour Tree의 표현력을 활용하는 하이브리드 접근법은 계산 리소스와
행동 복잡성 사이의 최적 균형점을 제공한다.&lt;/p&gt;
&lt;p&gt;특히 생명주기 관리, 동적 중단 메커니즘, 강제 완료 시스템 등의 확장 기능을 통해 자연스러운 동물 행동 패턴 구현이 가능해졌다.
이는 전통적인 게임 AI 기법을 생태계 시뮬레이션 분야로 성공적으로 확장 가능성을 제안한 것이다.&lt;/p&gt;
&lt;h2&gt;6.2. 시스템의 한계점 및 개선 과제&lt;/h2&gt;
&lt;h3&gt;6.2.1. 환경 모델의 사실성 부족&lt;/h3&gt;
&lt;p&gt;현재 구현된 WFC+BSP 통합 시스템은 기본적인 바이옴 분포만을 다루며,
실제 생태계의 복잡한 상호작용을 충분히 반영하지 못한다.
식생의 부재, 수계망의 단순성, 토양-기후-생물군 간의 피드백 메커니즘 부족 등이 주요 한계점이다.&lt;/p&gt;
&lt;p&gt;특히 현재의 타일 기반 접근법은 연속적인 환경 변화나 미기후 효과를 표현하기에 해상도가 제한적이다.
보다 사실적인 생태계 시뮬레이션을 위해서는 다중 스케일 환경 모델링이 필요하다.&lt;/p&gt;
&lt;h3&gt;6.2.2. 확장성과 모듈화의 개선 필요&lt;/h3&gt;
&lt;p&gt;각 구성 요소(경로탐색, 행동 모델링, 환경 생성)가 상대적으로 독립적으로 구현되어 있어,
시스템 간 유기적 상호작용을 구현하기에는 통합 아키텍처가 부족하다.
특히 환경 변화가 에이전트 행동에 미치는 영향이나, 에이전트 활동이 환경에 미치는 피드백 효과를 체계적으로 다루는 프레임워크가 필요하다.&lt;/p&gt;
&lt;h2&gt;6.3. 향후 연구 방향&lt;/h2&gt;
&lt;h3&gt;6.3.1. 강화학습 기반 적응형 생태계&lt;/h3&gt;
&lt;p&gt;에이전트의 행동 패턴이 환경과의 상호작용을 통해 동적으로 진화하는 시스템을 구축할 수 있다.
개별 에이전트는 강화학습을 통해 최적 생존 전략을 학습하고, 집단 차원에서는 유전 알고리즘을 통해 행동 특성이 세대를 거쳐 전승되는 모델을 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;이는 기존의 고정된 행동 규칙에서 벗어나 실제 진화 과정을 모방한 동적 생태계 시뮬레이션을 가능하게 할 것이다.&lt;/p&gt;
&lt;h2&gt;6.4. 결론&lt;/h2&gt;
&lt;p&gt;본 리포트는 게임 개발 기술을 생태계 시뮬레이션 분야에 적용하여 성능, 개발 효율성 측면에서 의미 있는 진전을 이루었다.
더 나아가 이러한 기술의 적용은 컴퓨터 시뮬레이션이 환경 문제 해결, 예측과 같은 과제를 기여할 수 있는 가능성을 제시했다.&lt;/p&gt;
&lt;p&gt;물론 현재의 구현은 실제 생태계의 복잡성을 모두 담아내기에는 여전히 한계가 있다.
하지만 제시된 방법론과 기술적 기반은 향후 더욱 정교하고 사실적인 생태계 시뮬레이션 시스템으로 발전할 수 있는 견고한 출발점을 제공한다.&lt;/p&gt;
&lt;p&gt;위 구현된 코드는 [[여기] (https://github.com/COMPY07/EcoSimReport)]에서 확인할 수 있다.&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&gt;
&lt;hr /&gt;
&lt;p&gt;[1] C. Shih, C. Yang, and M. Fukuda, &quot;Benchmarking the Agent Descriptivity of Parallel Multi-agent Simulators,&quot; in Highlights of Practical Applications of Agents, Multi-Agent Systems, and Complexity: The PAAMS Collection, Communications in Computer and Information Science, vol. 887, Springer, Cham, 2018, pp. 480–492.&lt;/p&gt;
&lt;p&gt;[2] Unity Technologies, &quot;Job system,&quot; Unity Manual 6000.1. Accessed: Aug. 25, 2025. [Online]. Available: https://docs.unity3d.com/6000.1/Documentation/Manual/job-system-overview.html&lt;/p&gt;
&lt;p&gt;[3] C. Gasch, J. M. Sotoca, M. Chover, I. Remolar, and C. Rebollo, &quot;Procedural modeling of plant ecosystems maximizing vegetation cover,&quot; Multimedia Tools and Applications, vol. 81, no. 12, pp. 16195-16217, May 2022, doi: 10.1007/s11042-022-12292-7.&lt;/p&gt;
&lt;p&gt;[4] A. S. Joshi, &quot;Reinforcement Learning-Enhanced Procedural Generation for Dynamic Narrative-Driven AR Experiences,&quot; in Proc. 20th Int. Joint Conf. Comput. Vision, Imaging Comput. Graph. Theory Appl. (GRAPP), 2025, pp. 385-397, doi: 10.5220/0013373200003912.&lt;/p&gt;
&lt;p&gt;[5] M. Ben-Ari and F. Mondada, &quot;Finite State Machines,&quot; in Elements of Robotics, Cham: Springer, 2018, ch. 4, pp. 59-81, doi: 10.1007/978-3-319-62533-1_4.&lt;/p&gt;
&lt;p&gt;[6] S. Snodgrass, &quot;Levels from Sketches with Example-Driven Binary Space Partition,&quot; in Proc. AAAI Conf. Artif. Intell. Interact. Digit. Entertain., vol. 15, no. 1, pp. 73-79, 2019, doi: 10.1609/aiide.v15i1.5227.&lt;/p&gt;
&lt;p&gt;[7] P. E. Hart, N. J. Nilsson, and B. Raphael, &quot;A formal basis for the heuristic determination of minimum cost paths,&quot; IEEE Trans. Syst. Sci. Cybern., vol. 4, no. 2, pp. 100-107, Jul. 1968.&lt;/p&gt;
&lt;p&gt;[8] &quot;NavMeshAgent Performance Issue,&quot; Unity Discussions, Unity Technologies, Jan. 6, 2018. [Online]. Available: https://discussions.unity.com/t/navmeshagent-performance-issue/687490&lt;/p&gt;
&lt;p&gt;[9] &quot;Memory leak in NavmeshAgent.SetDestination if multiple agents in the same position,&quot; Unity Discussions, Unity Technologies, Feb. 20, 2022. [Online]. Available: https://discussions.unity.com/t/memory-leak-in-navmeshagent-setdestination-if-multiple-agents-in-the-same-position/872528&lt;/p&gt;
&lt;p&gt;[10] Y. A. Sekhavat, &quot;Behavior Trees for Computer Games,&quot; International Journal on Artificial Intelligence Tools, vol. 26, no. 02, p. 1730001, 2017, doi: 10.1142/S0218213017300010.&lt;/p&gt;
&lt;p&gt;[11] S. M. Gardiner, &quot;Climate change as a global test case for moral theory,&quot; Environmental Ethics, vol. 39, no. 1, pp. 5-29, 2017.&lt;/p&gt;
&lt;p&gt;[12] Unity Technologies, &quot;Burst compiler,&quot; Unity Documentation, 2024. [Online]. Available: https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/index.html&lt;/p&gt;
&lt;p&gt;[13] M. Gumin, &quot;WaveFunctionCollapse,&quot; GitHub, 2016. [Online]. Available: https://github.com/mxgmn/WaveFunctionCollapse&lt;/p&gt;
&lt;p&gt;[14] Wikipedia contributors, &quot;Wave function collapse,&quot; Wikipedia, Jul. 28, 2025. [Online]. Available: https://en.wikipedia.org/wiki/Wave_function_collapse&lt;/p&gt;
&lt;p&gt;[15] J. E. Bresenham, &quot;Algorithm for computer control of a digital plotter,&quot; IBM Systems Journal, vol. 4, no. 1, pp. 25-30, 1965.&lt;/p&gt;
&lt;p&gt;[16] S. F. Railsback, S. L. Lytinen, and S. K. Jackson, &quot;Agent-based simulation platforms: review and development recommendations,&quot; Simulation, vol. 82, no. 9, pp. 609-623, 2006.&lt;/p&gt;
</content:encoded></item><item><title>[SunrinLife] 굉장히 많은 배움을 준 SHARC 1학년</title><link>https://compy07.github.io/Blog/posts/sunrin/diary/life/sharc_diary/nubmertheory/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/sunrin/diary/life/sharc_diary/nubmertheory/</guid><pubDate>Tue, 25 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;24년 6월 10일에 진행된 정수론 수업을 얘기해볼까 한다.&lt;/p&gt;
&lt;p&gt;사실 아직 배운게 많지 않고 1학년을 상대로하는 수업이기 때문에 급발진도 못해서, 한 수업에 많은걸 담지는 못하지만 그래도 꽤 많은 내용을 다뤘다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;에라토스테네스의 체&lt;/li&gt;
&lt;li&gt;모듈러 연산&lt;/li&gt;
&lt;li&gt;중국인의 나머지 정리&lt;/li&gt;
&lt;li&gt;유클리드 호제법&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 4가지를 개념적으로 알려주고, 활용 방법을 설명한다.
그리고 이를 &quot;백준&quot;에서 문제를 풀어보면서 응용 능력을 키웠다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;img.png&quot; alt=&quot;수업1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;boj_scoreboard.png&quot; alt=&quot;수업2&quot; /&gt;
수업이 끝나고 문제푸는 시간에서 많은 친구들이 시도하고, 생각하면서 실력을 많이 올렸다.&lt;/p&gt;
&lt;p&gt;4월달에 첫 수업을 했을때는 구현, 문제 이해, 솔루션의 탐색이 아직 멀었구나... 라고 생각했던게 고작 몇 달전이라는게
너무나 놀라웠다.&lt;/p&gt;
&lt;p&gt;백준은 건강에 좋다, 백준해라, ps 꼭 해라 매일매일 풀어봐라 라고
말했던 것을 직접 실천해준 1학년들이 너무나 대견했다.
무려 4달만에 실, 골, 플 정도 심지어 지금 3달째 되는
달에는 다이아5를 풀 수 있을 정도로 성장하였다.&lt;/p&gt;
&lt;p&gt;나도 알고리즘을 진짜 잘하고, 가르칠 정도는 아니지만 소개하고 방법에 대해서 공유하는 것을 좋아하여 나눴던 것인데
이런 나의 말을 잘 듣고 성장하는 1학년들이 너무나 고맙고 대단하게 느껴졌다.&lt;/p&gt;
&lt;p&gt;알연부의 수업은 1, 2학년 모두 얻어가는 것이 있고, 배운다는 것이 누군가 나를 가르쳐서 얻는 것도 있지만 내가 가르치며 아니면 경험을 하면서 얻는 배움도 있다는 깨달음도 주었다.&lt;/p&gt;
&lt;p&gt;정말 뜬금없이 일기 비스무리한 것을 쓰고 있는데, 내가 느낀 것을 다른 이에게 공유하는 것도 좋을것 같아 포스팅을 시작하게 되었다.
(사실 이런 이유도 있지만, 1일 1포스팅을 목표로 하고 있어서 쓸거 고민하느라 이런거 씁니다.)&lt;/p&gt;
</content:encoded></item><item><title>[PARA] 2024 선린인터넷고등학교 PARA 소프트웨어나눔축제</title><link>https://compy07.github.io/Blog/posts/sunrin/diary/schoolclub/para/</link><guid isPermaLink="true">https://compy07.github.io/Blog/posts/sunrin/diary/schoolclub/para/</guid><description>선린인터넷고등학교 SSF</description><pubDate>Wed, 06 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;SSF.png&quot; alt=&quot;SSF&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;24년 11월 2일에 진행된 선린 소프트웨어나눔축제는 중학생들을 대상으로 파이썬 기초 문법 및 프로그래밍 기초를 가르치며, LLM과 프롬프트 엔지니어링을
이용해서 나만의 친구 만들기 프로젝트를 진행하였다.
&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;pyoji.png&quot; alt=&quot;표지&quot; /&gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;교재 집필 및 발표자료와 나눔축제에 쓰일 프로젝트 전부 우리 부원들이 힘을 합쳐 만들었다.
figma에서 하나하나 교재 집필을 하면서, 선배들의 피드백을 잘 수용해주고 따라준 이번 파라 1기 후배들에게 너무 감사하다.&lt;/p&gt;
&lt;p&gt;그리고 열정적으로 이끌고, 도와준 우리 0기 부원들에게도 너무 고마움을 표하고 싶다. (특히 유﹡호, 박﹡규, 김﹡호 고맙다 친구들이여)&lt;/p&gt;
&lt;p&gt;그리고 발표자료부터 발표를 시간에 맞춰서 또 최대한 내용을 더 많이 알려주려고 노력한 발표자들과 그걸 피드백한 선배들에게 수고했다고 말하고 싶다.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;night_work.jpeg&quot; alt=&quot;야근&quot; /&gt;
하루종일 연습하다가 드디어 집에 가서 기쁜 우리 후배들...&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;night_work2.jpeg&quot; alt=&quot;야근&quot; /&gt;
또 다시 하루종일 연습하다가 다같이 퇴근하는 우리 동아리 부원들.. 수고해써요!!&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;night_work3.jpeg&quot; alt=&quot;야근&quot; /&gt;
발표 준비를 마치고, 실습실 세팅이 모두 끝난 후 다시 늦게 퇴근하는 후배들....&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;night_work5.jpeg&quot; alt=&quot;야근&quot; /&gt;
소나축 D-2일 마지막 리허설 돌리면서 계속 반복 시작(이날도 9시까지 실습실 남아있다 퇴근...)&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;진짜 다들 너무 수고가 많았다. 드디어 D-DAY!!!&lt;/h2&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;ready.jpeg&quot; alt=&quot;준비 과정&quot; /&gt;
아침 일찍 등교 후 실습실 재확인 및 발표자료 셋팅 중...
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;ready2.jpeg&quot; alt=&quot;준비 과정&quot; /&gt;
발표 다시한번 리허설 시작(점검 시작)&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;onClass.jpeg&quot; alt=&quot;소나축 시작!&quot; /&gt;
&lt;img src=&quot;onClass2.jpeg&quot; alt=&quot;소나축 시작!&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;Last.jpeg&quot; alt=&quot;소나축 마무리&quot; /&gt;
소나축을 마무리하며 발표자 2명과 단체사진!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;para_img.jpeg&quot; alt=&quot;파라&quot; /&gt;
모두 수고했어!!! 정말 고마워!!
&amp;lt;/center&amp;gt;&lt;/p&gt;
</content:encoded></item></channel></rss>