[{"content":"","date":"4 August 2025","externalUrl":null,"permalink":"/tags/2022/","section":"Tags","summary":"","title":"2022","type":"tags"},{"content":"","date":"4 August 2025","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"\u0026lt;script\u0026gt; if (confirm(\u0026#39;⚠️ Deprecated! \\nGo to log.j2234.dev?\u0026#39;)) { window.location.href = \u0026#39;https://log.j2234.dev\u0026#39;; } \u0026lt;/script\u0026gt; ","date":"4 August 2025","externalUrl":null,"permalink":"/posts/2022-tistory/186-deprecated/","section":"Posts","summary":"\u003cscript\u003e if (confirm('⚠️ Deprecated! \\nGo to log.j2234.dev?')) { window.location.href = 'https://log.j2234.dev'; } \u003c/script\u003e","title":"Deprecated","type":"posts"},{"content":"","date":"4 August 2025","externalUrl":null,"permalink":"/categories/dev/","section":"Categories","summary":"","title":"DEV","type":"categories"},{"content":"","date":"4 August 2025","externalUrl":null,"permalink":"/","section":"j2234's log","summary":"","title":"j2234's log","type":"page"},{"content":"","date":"4 August 2025","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"4 August 2025","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"16 May 2025","externalUrl":null,"permalink":"/tags/aws/","section":"Tags","summary":"","title":"Aws","type":"tags"},{"content":" Intro # 이미지를 S3에 업로드하면 CloudFront를 통해 서빙하면서, CloudFront 기본 도메인을 CloudFlare의 내 커스텀 도메인으로 매핑하는 과정을 기록한다. DNS로 Route53을 사용할 때보다 몇 가지 추가 설정이 필요하다. 기억이 안나서 엄청 헤멧다\n선행 조건 # S3 bucket (ACL 등 설정) Origin=S3인 CloudFront 배포 (기본 작동 확인) Cloudfront DNS 관리 권한 인증서 발급 # 가장 먼저, ACM(AWS Certificate Manager) 인증서를 발급 받아야 한다.\nCloudfront는 글로벌 서비스이기 때문에 무조건 us-east-1 리전을 사용한다\nACM 인증서(public) 요청 화면에서 내가 사용할 도메인 이름을 입력하고, DNS 검증을 선택한다. 이러면 다음과 같은 화면이 나오게 된다. 여기서 ACM에 DNS 검증을 위해 CNAME 레코드를 제시하는데, 이를 CloudFlare DNS에 추가한다 (proxy는 설정하지 않는다) NAME에 CNAME이름(1), Target에 CNAME값(2)을 설정하고 잠시 기다리면, 상태가 인증 중으로 바뀐다.\nCloudFront 배포에 커스텀 도메인 연결 # Cloudfront 배포의 general 설정에서 작업한다. Alternative domain name에 내 커스텀 도메인을 입력하고 (ACM에 등록한 도메인과 일치해야 한다), Custom SSL Certificate에서 방금 발급한 ACM을 설정하고 변경사항을 저장한다. 설정이 변경되는데 약 5분정도 소요된다.\nCloudFlare DNS 설정 # CloudFlare에서 이전 단계의 Alternative domain name에 입력한 도메인으로, CloudFront의 원본 URL을 매핑한다. proxy는 사용하지 않는다.\n이렇게 정리하면 정말 간단해 보이는데, 기억에만 의존해서 작업하다가 1시간 반이 넘게 걸렸다 ㅋㅋㅋㅋ\n","date":"16 May 2025","externalUrl":null,"permalink":"/posts/velog/002-aws-cloudfront-%EB%82%B4-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0-cloudflare/","section":"Posts","summary":" Intro # 이미지를 S3에 업로드하면 CloudFront를 통해 서빙하면서, CloudFront 기본 도메인을 CloudFlare의 내 커스텀 도메인으로 매핑하는 과정을 기록한다. DNS로 Route53을 사용할 때보다 몇 가지 추가 설정이 필요하다. 기억이 안나서 엄청 헤멧다\n선행 조건 # S3 bucket (ACL 등 설정) Origin=S3인 CloudFront 배포 (기본 작동 확인) Cloudfront DNS 관리 권한 인증서 발급 # 가장 먼저, ACM(AWS Certificate Manager) 인증서를 발급 받아야 한다.\n","title":"AWS CloudFront 내 도메인 연결하기 (CloudFlare)","type":"posts"},{"content":"","date":"27 April 2025","externalUrl":null,"permalink":"/tags/rabbitmq/","section":"Tags","summary":"","title":"Rabbitmq","type":"tags"},{"content":" 이 글은 RabbitMQ의 기본적인 개념과 spring에서 연동 방법을 기초 수준에서 설명한다.\nRabbitMQ란? # 플랫폼 중립적인 메시징 및 스트리밍 브로커로,AMQP,MQTT 등의 여러 개방형 표준 프로토콜을 지원해 다양한 언어와 플랫폼 간 메시지 송수신을 처리한다.\n오픈소스이며 MPL2.0 라이센스를 따른다.\n설치 # 다음 docker-compose.yml 파일로 연습용 RabbitMQ 서버를 간단히 띄울 수 있다. 클러스터 구성 등의 정보도 구글링하면 쉽게찾을 수 있음!!\nversion: \u0026#39;3.7\u0026#39; services: rabbitmq: image: rabbitmq:4.0-management ports: - \u0026#34;5672:5672\u0026#34; - \u0026#34;15672:15672\u0026#34; environment: RABBITMQ_DEFAULT_USER: admin RABBITMQ_DEFAULT_PASS: admin #관리 UI: http://localhost:15672, 기본 계정: admin/admin 용어정리 # Producer: 메시지를 생성하고 Exchange로 발행하는 애플리케이션 Exchange: Producer로부터 받은 메시지를 Binding 규칙과 Routing Key에 따라 적절한 Queue로 라우팅 Queue: Exchange가 라우팅한 메시지를 저장하고 대기시키는 버퍼 역할 Routing Key: 메시지를 발행할 때 Producer가 Exchange에 전달하는 키로, Exchange의 라우팅 규칙에 사용 Binding: Exchange와 큐 사이의 규칙.(필요하면 binding key를 함께 설정하여 어떤 메시지를 어떤 큐로 보낼지 결정) Consumer: 큐에서 메시지를 가져와 처리하는 애플리케이션 메시지 전송 프로세스 # Producer → Broker(Exchange → Queue) → Consumer\n메시지 발행\nProducer가 메시지를 발행한다. 퍼블리셔 컨펌\n브로커가 메시지를 Queue에 저장한 뒤 Producer에 Publisher Ack/Nack을 응답한다. (비동기로 확인을 생략할 수 있다.) Ack: 정상 저장 Nack: 저장 실패 → 재전송 로직 필요 라우팅\nExchange가 Binding 규칙과 Routing Key를 기준으로 메시지를 적절한 Queue로 라우팅한다. 큐\nQueue가 메시지를 내부에 저장하고, Consumer의 요청을 대기한다. 메시지 전달\n브로커가 Queue에서 메시지를 꺼내 Consumer로 전송한다. 컨슈머 컨펌\nConsumer가 처리 결과를 Consumer Ack/Nack으로 브로커에 알린다. Ack: 메시지가 큐에서 완전 제거된다. Nack: 설정에 따라 재큐잉(requeue) 또는 DLQ(Dead-Letter Queue)로 이동한다. 스프링 코드 # org.springframework.boot:spring-boot-starter-amqp 의존성이 필요하다.\n빈 등록 # 참고: 예제에 등장하는 Queue, DirectExchange, Binding 등 타입은 모두 org.springframework.amqp.core 패키지이다.\n/** * Queue Bean 등록 * * @return Queue * - name: 큐 이름 * - durable: false일 경우 브로커 재시작 시 삭제되는 휘발성 큐 */ @Bean fun queue(): Queue { return Queue(\u0026#34;QUEUE_NAME\u0026#34;, false) } /** * DirectExchange Bean 등록 * * @return DirectExchange * - name: 익스체인지 이름 (\u0026#34;EXCHANGE_NAME\u0026#34;) * - durable: true (브로커 재시작 시에도 유지) */ @Bean fun directExchange(): DirectExchange { return DirectExchange(\u0026#34;EXCHANGE_NAME\u0026#34;) } /** * Queue와 Exchange를 바인딩 * * @param queue 생성된 Queue 빈 * @param directExchange 생성된 DirectExchange 빈 * @return Binding * - routingKey: \u0026#34;ROUTING_KEY\u0026#34;와 일치하는 메시지를 해당 큐로 전달하도록 설정 */ @Bean fun binding(queue: Queue, directExchange: DirectExchange): Binding { return BindingBuilder .bind(queue) .to(directExchange) .with(\u0026#34;ROUTING_KEY\u0026#34;) } /** * JSON 메시지 컨버터 Bean 등록 * * - Producer가 전송하는 객체를 JSON 문자열로 직렬화하고, * Consumer가 수신한 JSON 문자열을 객체로 역직렬화하는 데 사용 * * @return MessageConverter 인스턴스 */ @Bean fun messageConverter(): MessageConverter { return Jackson2JsonMessageConverter() } 메시지 발행, 소비 # // 지정된 토픽 익스체인지로 메시지를 발행 rabbitTemplate.convertAndSend( RabbitMQConfig.TOPIC_EXCHANGE, // 전송 대상 익스체인지 이름 routingKey, // 토픽 패턴 기반의 라우팅 키 message // 전송할 페이로드 object (MessageConverter를 통해 직렬화) ) /** * 큐에 바인딩된 메시지를 수신하는 Listener 메서드 * * @param message SomeObject 타입의 메시지 페이로드 * - 메시지 컨버터에 의해 객체로 역직렬화된 값 */ @RabbitListener(queues = [\u0026#34;QUEUE_NAME\u0026#34;]) fun consumeWarn(message: SomeObject) { logger.info { \u0026#34;message = $message\u0026#34; } } 추가:Exchange 종류 # DirectExchange 라우팅 키가 바인딩 키와 정확히 일치할 때만 전달 TopicExchange 와일드카드(*, #) 패턴 매칭을 통해 복수 큐로 전달 FanoutExchange Binding Key를 사용하지 않고, 바인딩된 모든 큐로 브로드캐스팅 브로커는 이름이 빈 문자열인 기본 익스체인지를(Direct) 미리 선언해 두고, 큐를 선언할 때 큐 이름과 같은 라우팅 키로 자동으로 바인딩한다. 따라서 익스체인지를 별도로 등록하지 않아도 기본 익스체인지(\u0026quot;\u0026quot;)와 큐 이름만으로 메시지를 보낼 수 있다.\n","date":"27 April 2025","externalUrl":null,"permalink":"/posts/velog/003-rabbitmq-%EA%B8%B0%EC%B4%88/","section":"Posts","summary":" 이 글은 RabbitMQ의 기본적인 개념과 spring에서 연동 방법을 기초 수준에서 설명한다.\nRabbitMQ란? # 플랫폼 중립적인 메시징 및 스트리밍 브로커로,AMQP,MQTT 등의 여러 개방형 표준 프로토콜을 지원해 다양한 언어와 플랫폼 간 메시지 송수신을 처리한다.\n오픈소스이며 MPL2.0 라이센스를 따른다.\n설치 # 다음 docker-compose.yml 파일로 연습용 RabbitMQ 서버를 간단히 띄울 수 있다. 클러스터 구성 등의 정보도 구글링하면 쉽게찾을 수 있음!!\n","title":"RabbitMQ 기초","type":"posts"},{"content":"","date":"27 April 2025","externalUrl":null,"permalink":"/tags/spring/","section":"Tags","summary":"","title":"Spring","type":"tags"},{"content":"","date":"27 April 2025","externalUrl":null,"permalink":"/tags/%EB%B9%84%EB%8F%99%EA%B8%B0/","section":"Tags","summary":"","title":"비동기","type":"tags"},{"content":"","date":"19 April 2025","externalUrl":null,"permalink":"/tags/oauth2/","section":"Tags","summary":"","title":"Oauth2","type":"tags"},{"content":"","date":"19 April 2025","externalUrl":null,"permalink":"/series/oauth2-oidc/","section":"Series","summary":"","title":"OAuth2 OIDC","type":"series"},{"content":"","date":"19 April 2025","externalUrl":null,"permalink":"/tags/oidc/","section":"Tags","summary":"","title":"OIDC","type":"tags"},{"content":"이 글에 등장하는 전체 코드는 깃허브 저장소에서 확인할 수 있다.\nOIDC는 OAuth2에 인증 기능을 추가한 확장 프로토콜이므로, 구현 과정은 이전 글과 거의 동일하지만\u0026hellip; 제공자가 OIDC를 지원해야만 이 방식으로 구현이 가능하다.\n사용자 로그인 진입점 # OIDC에서는 로그인 페이지로 진입할 때,\nOAuth2에서 전달하는 기본 파라미터(client_id, response_type, redirect_uri)에 scope파라미터가 필수로 포함된다. 이 scope에는 반드시 openid가 포함되어야 하며, 필요한 사용자 정보 항목을 추가로 명시할 수 있다.\n예: scope=openid profile email\n사실 구글은 openid가 포함되지 않아도 oidc를 지원한다\n해당 화면에서 사용자는 구글 계정으로 인증을 진행하게 된다.\n리다이렉트 # 리다이렉트 단계는 이전 글과 동일하다.\n마찬가지로 code가 전달되고, 이 code를 사용해 토큰과 교환한다.\n하지만 차이점은,\nOIDC 프로토콜에서는 사용자의 정보를 담은 JWT 형식의 id_token이 함께 응답에 포함된다!!! 이 ID 토큰을 검증하면, 사용자를 신뢰할 수 있는 방식으로 인증할 수 있다.\n구글 응답 예제:\n{ \u0026#34;access_token\u0026#34;: \u0026#34;access_token\u0026#34;, \u0026#34;expires_in\u0026#34;: 3598, \u0026#34;scope\u0026#34;: \u0026#34;https://www.googleapis.com/auth/userinfo.email openid\u0026#34;, \u0026#34;token_type\u0026#34;: \u0026#34;Bearer\u0026#34;, \u0026#34;id_token\u0026#34;: \u0026#34;{{대충 jwt토큰}}\u0026#34; } 물론 Google의 다른 API를 사용하려면 access_token을 활용해야 하지만, 더 이상 사용자를 인증하기 위해 다시 제공자에게 요청할 필요가 없다.\n추가 # OIDC가 특히 빛을 발하는 순간은 모바일 앱과 같이\n“추가적인 사용자 정보 API 호출이 어렵거나, 클라이언트 시크릿을 안전히 보관하기 어려운” 환경이다.\n이때 프론트엔드는 Authorization Code Flow with PKCE로 받은 id_token을 백엔드로 전달하고, 백엔드에서 JWT 서명 및 클레임(iss, aud, exp 등)을 검증하면 안전하고 간편하게 인증을 처리할 수 있다.\n이 방법을 사용할 때 백엔드 검증이 필수인 이유는,\n악의적인 사용자가 id_token을 위·변조하여 전송할 수 있기 때문이다.\nid_token은 비대칭 암호화(RS256) 방식으로 보호되며, 이 서명을 검증하기 위한 공개 키는 제공자(구글)이 공개한 well‑known jwks_uri 즉, 에서 가져올 수 있다.\n추가 설정은 OpenID Connect 디스커버리 문서 google openid-configuration에서 확인할 수 있다.\n","date":"19 April 2025","externalUrl":null,"permalink":"/posts/velog/004-oidc-%EA%B5%AC%ED%98%84google/","section":"Posts","summary":"이 글에 등장하는 전체 코드는 깃허브 저장소에서 확인할 수 있다.\nOIDC는 OAuth2에 인증 기능을 추가한 확장 프로토콜이므로, 구현 과정은 이전 글과 거의 동일하지만… 제공자가 OIDC를 지원해야만 이 방식으로 구현이 가능하다.\n사용자 로그인 진입점 # OIDC에서는 로그인 페이지로 진입할 때,\nOAuth2에서 전달하는 기본 파라미터(client_id, response_type, redirect_uri)에 scope파라미터가 필수로 포함된다. 이 scope에는 반드시 openid가 포함되어야 하며, 필요한 사용자 정보 항목을 추가로 명시할 수 있다.\n","title":"OIDC 구현(GOOGLE)","type":"posts"},{"content":"","date":"19 April 2025","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"이 글에 등장하는 전체 코드는 깃허브 저장소에서 확인할 수 있다.\n사용자 로그인 진입점 # index.html\n사용자가 네이버 로그인 버튼을 클릭하면, 다음과 같은 URL로 이동한다: https://nid.naver.com/oauth2.0/authorize?\nclient_id={내 네이버 클라이언트 ID} response_type=code redirect_uri={네이버 개발자센터에 등록된 내 서버 URI} 해당 화면에서 사용자는 네이버 계정으로 인증을 진행하게 된다.\n리다이렉트 # 인증이 성공하면, 사용자는 redirect_uri로 이동한다.\n여기서 파라미터로 code가 넘어오는데, 이 코드와 토큰을 교환하게 된다. (Authorization Code Exchange)\n@GetMapping(\u0026#34;/custom/oauth2/code/naver\u0026#34;) fun recieveNaverCodeAnd(@RequestParam code: String) code와 네이버에서 발급받은 client_id, client_secret, grant_type, redirect_uri를 application/x‑www‑form‑urlencoded 형식으로 토큰 발급 엔드포인트에 전송한다.\n참고 : redirect_uri는 로그인 요청 시 전달했던 값과 동일해야 한다는 검증용 파라미터일 뿐, 이 단계에서 실제로 리다이렉트되지는 않는다.\nval form = LinkedMultiValueMap\u0026lt;String, String\u0026gt;().apply { add(\u0026#34;client_id\u0026#34;, oauth2CustomProperties.naverClientId) add(\u0026#34;client_secret\u0026#34;, oauth2CustomProperties.naverClientSecret) add(\u0026#34;code\u0026#34;, code) add(\u0026#34;grant_type\u0026#34;, \u0026#34;authorization_code\u0026#34;) add(\u0026#34;redirect_uri\u0026#34;, \u0026#34;http://localhost:8080/custom/oauth2/code/naver\u0026#34;) } 네이버는 다음과 같이 응답한다.\n{ \u0026#34;access_token\u0026#34;: \u0026#34;AAAANauk2PFBn3CKu...\u0026#34;, \u0026#34;refresh_token\u0026#34;: \u0026#34;Biqwefwef...\u0026#34;, \u0026#34;token_type\u0026#34;: \u0026#34;bearer\u0026#34;, \u0026#34;expires_in\u0026#34;: \u0026#34;3600\u0026#34; } 여기까지가 OAuth2의 인가 코드 플로우(Authorization Code Flow)에 해당한다. 응답의 access_token을 가지고 이제 네이버의 서비스를 사용할 수 있다.\n부록: 네이버 사용자 정보 요청하기 # 위에서 발급받은 access_token을 Bearer 인증 방식으로 헤더에 포함해 (Authorization) \u0026quot;https://openapi.naver.com/v1/nid/me\u0026quot;에 GET 요청을 보내면, 다음과 같은 네이버의 사용자 정보를 가져올 수 있다.\n{ \u0026#34;resultcode\u0026#34;: \u0026#34;00\u0026#34;, \u0026#34;message\u0026#34;: \u0026#34;success\u0026#34;, \u0026#34;response\u0026#34;: { \u0026#34;id\u0026#34;: \u0026#34;YES0Z444xu4u1m9t2jFySZmtS2iz5323k0LLs6nrwXQH8\u0026#34;, \u0026#34;nickname\u0026#34;: \u0026#34;iamwho\u0026#34;, \u0026#34;profile_image\u0026#34;: \u0026#34;https://ssl.pstatic.net/static/pwe/address/img_profile.png\u0026#34;, \u0026#34;age\u0026#34;: \u0026#34;20-29\u0026#34;, \u0026#34;gender\u0026#34;: \u0026#34;M\u0026#34;, \u0026#34;email\u0026#34;: \u0026#34;id@jr.naver.com\u0026#34;, \u0026#34;mobile\u0026#34;: \u0026#34;010-1234-5678\u0026#34;, \u0026#34;mobile_e164\u0026#34;: \u0026#34;+821077777\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;홍길동\u0026#34;, \u0026#34;birthday\u0026#34;: \u0026#34;07-01\u0026#34;, \u0026#34;birthyear\u0026#34;: \u0026#34;1997\u0026#34; } } 다음 글에서는 OIDC 구현 과정을 설명하겠다.\n","date":"18 April 2025","externalUrl":null,"permalink":"/posts/velog/005-oauth2-%EA%B5%AC%ED%98%84naver/","section":"Posts","summary":"이 글에 등장하는 전체 코드는 깃허브 저장소에서 확인할 수 있다.\n사용자 로그인 진입점 # index.html\n사용자가 네이버 로그인 버튼을 클릭하면, 다음과 같은 URL로 이동한다: https://nid.naver.com/oauth2.0/authorize?\nclient_id={내 네이버 클라이언트 ID} response_type=code redirect_uri={네이버 개발자센터에 등록된 내 서버 URI} 해당 화면에서 사용자는 네이버 계정으로 인증을 진행하게 된다.\n리다이렉트 # 인증이 성공하면, 사용자는 redirect_uri로 이동한다.\n","title":"OAuth2  구현(naver)","type":"posts"},{"content":"어떤 멘토 분이 이런 말을 하셨다.\n\u0026ldquo;요즘은 Spring Security에 OAuth2 라이브러리 하나만 추가해놓고\n소셜 로그인 인증을 구현했다고 한다.\n과연 이렇게 구현한 사람들이 OAuth2에 대해 안다고 할 수 있을까?\nOIDC가 뭔지는 알기나 할까?\u0026rdquo;\n이 말을 듣고 찔려서 공부해보겠다.\nOAuth2는 인증 프로토콜이 아니라 \u0026ldquo;인가\u0026rdquo; 프로토콜이다. # Authentication Authorization 많은 글들이 OAuth2예시를 설명하면서, 이를 구분하지 않는다. 거의 소셜 로그인 == OAuth2 로그인 정도로 취급되지만, OAuth2의 목적은 인증이 아닌 인가이다.\nOAuth2(2.1) RFC의 첫 문단을 번역하면 이렇다:\nOAuth 2.1 인증 프레임워크는 리소스 소유자와 인증 서비스 간의 승인 상호 작용을 조율하여 리소스 소유자를 대신하여 타사 애플리케이션이 HTTP 서비스에 대한 제한된 액세스 권한을 얻거나 타사 애플리케이션이 자체적으로 액세스 권한을 얻을 수 있도록 허용합니다.\n즉, OAuth2는 사용자 인증이 아닌, \u0026ldquo;애플리케이션이 사용자 리소스에 접근할 수 있는 권한\u0026rdquo; - 즉 인가에 초점이 맞춰져 있다. 다음은 GPT가 준 예시이다:\n사용자 A와 애플리케이션 B가 있다. A는 \u0026ldquo;B가 내 구글 드라이브 목록 정도는 봐도 돼\u0026quot;라고 동의한다. 구글은 B에게 access token을 발급한다. 그 토큰으로 A의 리소스(API)를 호출한다. 여기 어디에도 **\u0026ldquo;A가 진짜 A인지 인증하는 과정\u0026rdquo;**은 없다. 권한을 위임할 뿐이다. \u0026ldquo;어? 근데 애가 알아서 사용자 정보 가져오던데?\u0026rdquo;\n사용자 정보를 가져오는 것은 맞다.\n그러나 이는 위임된 access token으로 사용자 리소스(API)를 호출한 결과일 뿐이다.\n즉, 인증을 한 게 아니라, 권한을 받아서 사용자 정보를 요청한 것이다. 하지만\u0026hellip;.\nOIDC(OpenID Connect) # 위 설명처럼, OAuth2만으로는 사용자가 누군지 검증이 불가능하다. 이를 보완하기 위해 등장한 것이 바로 OIDC이다.\nOIDC는 OAuth2의 \u0026ldquo;인가\u0026rdquo; 기능에 **\u0026ldquo;인증\u0026rdquo;**을 더한 프로토콜이다. 즉, 추가된 id_token을 통해 사용자가 누구인지를 검증할 수 있다.\n다음은 OIDC를 지원하지 않는 네이버의 OAuth2 응답이다.\n{ \u0026#34;access_token\u0026#34;: \u0026#34;Aawefwaefawefawefo\u0026#34;, \u0026#34;refresh_token\u0026#34;: \u0026#34;awefwaefwaefgh\u0026#34;, \u0026#34;token_type\u0026#34;: \u0026#34;bearer\u0026#34;, \u0026#34;expires_in\u0026#34;: \u0026#34;3600\u0026#34; } 다음은 OIDC를 지원하는 Google의 OAuth2 응답이다.\n{ \u0026#34;access_token\u0026#34;: \u0026#34;aa\u0026#34;, \u0026#34;expires_in\u0026#34;: 3598, \u0026#34;scope\u0026#34;: \u0026#34;https://www.googleapis.com/auth/userinfo.email openid\u0026#34;, \u0026#34;token_type\u0026#34;: \u0026#34;Bearer\u0026#34;, \u0026#34;id_token\u0026#34;: \u0026#34;{{대충 jwt토큰}}\u0026#34; } id_token # OIDC는 토큰 응답 시, access_token 외에 JWT 형식의 id_token을 함께 반환한다. 이 id_token은 JWT 형식으로 되어 있고, 고유 식별자, 이메일, 발급자 정보 등 + 스코프에 요청한 정보를 담고 있다.\n이 id_token이 바로 \u0026ldquo;이 사용자가 인증되었고, Google이 발급했다\u0026quot;는 인증서 역할을 한다.\nOIDC 사용 시 장점 # ID 토큰만으로 인증이 가능하다. 즉, 응답으로 받은 access token을 가지고 사용자 정보를 가져오는 추가적인 요청이 필요하지 않다.\n발급자의 공개키로 서명된 JWT 방식이라, 서버에서 이를 검증할 수 있다.\n표준화되어있다. OIDC를 사용하지 않고 /userinfo/me등을 호출해 사용자의 정보를 가져오는 방식은 서버마다 무슨 정보를 내려줄지 알 수 없다. OIDC는 scope에 따라 정해진 사용자 정보를 제공한다.\n다음 글에서는 이 개념들을 바탕으로 Spring Security 없이 직접 OAuth2 + OIDC 구현 예제를 코드와 함께 소개할 예정이다.\n","date":"18 April 2025","externalUrl":null,"permalink":"/posts/velog/006-oauth2%EC%99%80-oidc/","section":"Posts","summary":"어떤 멘토 분이 이런 말을 하셨다.\n“요즘은 Spring Security에 OAuth2 라이브러리 하나만 추가해놓고\n소셜 로그인 인증을 구현했다고 한다.\n과연 이렇게 구현한 사람들이 OAuth2에 대해 안다고 할 수 있을까?\nOIDC가 뭔지는 알기나 할까?”\n이 말을 듣고 찔려서 공부해보겠다.\nOAuth2는 인증 프로토콜이 아니라 “인가” 프로토콜이다. # Authentication Authorization 많은 글들이 OAuth2예시를 설명하면서, 이를 구분하지 않는다. 거의 소셜 로그인 == OAuth2 로그인 정도로 취급되지만, OAuth2의 목적은 인증이 아닌 인가이다.\n","title":"OAuth2와 OIDC","type":"posts"},{"content":"\n🌱 계기 # Reddit 눈팅중에, mongoDB student program를 통해 20만원 (150$) 짜리 시험을 무료로 볼 수 있게 해준다는 글을 발견했다. mongoDB에서 제공하는 자격증 강의를 전부 수강하면 무료 응시권을 받을 수 있었다. 별도로 자료를 찾아 공부할 필요 없이 직접 강의를 제공했기에 바로 시작했다.\nDeveloper 자격증 시험은 프로그래밍 언어를 선택해야 하는데, 제일 간단한 Python을 선택했다.\n💻 준비 # 이전에 Riot api를 사용할 때 잠깐 찍먹해본 적은 있으나, 실질적으로 mongoDB에 대한 아무런 지식도 없는 상태에서 시작했다. 총 준비 기간은 3주가 걸렸는데, 빡시게 한다면 1주면 충분할 듯 하다.\nMongoDB Developer Path의 모든 강의를 듣는다 (web상에서 실습도 가능하다) 유데미 연습문제(15000원) 6개를 모두 풀어본다. MongoDB 문서를 1번 정독한다.(필요한부분만) 유데미 연습문제는 오래된 자료도 많고 (findAndModiy 등), 쓰레기같은 문제도 많았지만, CRUD + aggregation 유형에 익숙해질 수 있다. 또한 시험범위는 아니지만 자연스럽게 샤딩 등 추가적인 지식도 얻을 수 있다 나처럼 mongoDB에 대한 기본지식이 아예 없는 상태라면 풀어보는걸 추천한다.\n🧐 시험 # 분명 문서에는 구글 번역이 가능하다고 안내하지만, 사용하는건 거의 불가능하다 근데 사용할만한 일이 없다 CRUD가 대부분을 차지한다. 전체적으로 쉬운 편이였고, 만약 프로그래밍에 익숙하다면 눈치로 풀 수 있는 문제가 많다.\n나는 SQL(정규화)에 뇌가 절여진 것 같다.\n▶️ 앞으로 # 사실 뭐할지 아직 못정했다. 쿠버네티스 쪽을 한번 해보고 싶긴 한데\u0026hellip; 일단 1주는 쉴려고 한다.\n","date":"22 February 2025","externalUrl":null,"permalink":"/posts/velog/007-mongodb-developer-associate-%EC%9E%90%EA%B2%A9%EC%A6%9D-%ED%9B%84%EA%B8%B0/","section":"Posts","summary":"\n🌱 계기 # Reddit 눈팅중에, mongoDB student program를 통해 20만원 (150$) 짜리 시험을 무료로 볼 수 있게 해준다는 글을 발견했다. mongoDB에서 제공하는 자격증 강의를 전부 수강하면 무료 응시권을 받을 수 있었다. 별도로 자료를 찾아 공부할 필요 없이 직접 강의를 제공했기에 바로 시작했다.\nDeveloper 자격증 시험은 프로그래밍 언어를 선택해야 하는데, 제일 간단한 Python을 선택했다.\n","title":"MongoDB Developer associate 자격증 후기","type":"posts"},{"content":"","date":"22 February 2025","externalUrl":null,"permalink":"/tags/%EC%9E%90%EA%B2%A9%EC%A6%9D/","section":"Tags","summary":"","title":"자격증","type":"tags"},{"content":" ⛅️어쩌다가 SPECIALTY까지? # 생일날 갑자기 삘빧아서 AWS 자격증에 도전 이후 벌써 4번째 자격증을 취득했다.\n원래 AWS는 해킹당한 이후 처다도 안보다가, 개발자로서 DVA 만 따려고 했었는데 \u0026hellip;\n😎 DVA 시험비용이 150$가 넘네? =\u0026gt; AWS Ec2만 만들어봤는데 떨어지면 한달 생활비 $$\\frac{1}{2}$$?? ㅈ되네?\n🤔 CLF를 따면 다음 시험을 50% 할인해주니깐 3만원 정도밖에 차이가 안나 =\u0026gt; 안전하게 가야지!\n😎 CLF 취득했으니 원래 목표인 DVA를 따야지\n😅 막상 땄는데, AWS의 기본인 VPC, SUBNET등에 대해선 하나도 모르겠어 =\u0026gt; SAA까진 따야겠다.\n😎 SCS도 땃으니깐, 이제 백엔드 공부에만 집중해야겠다.\n☁️ : 저번 시험 체크인 지연의 보상으로, 어떤 시험이던 무료료 볼 수 있는 바우처를 드릴게요❗️\n😳 막상 기회가 생겼으니 안딸수도 없고.. 300$짜리 젤비싼 자격증으로 가야겠다. 40만원이면 ㄷㄷㄷㄷㄷㄷ\n대충 이렇게 됬다.\n🌩️ 준비와 취득 # 준비 과정은 AWS 시험과 동일했다. # Stéphane Maarek 강의를 듣는다. examtopics 기출문제를 풀어본다. 노가다 AWS 테스트 시험과 6시간짜리 요점정리를 듣는다. 다만, 가장 어려운 시험인 만큼, 직접 해볼 수 없는 서비스 개비쌈 도 많고 지문 자체도 길고 헷갈리게 나와서 비교적 어려웠다.\n이전 시험들과 다르게 시험종료 후 7시간이 지나도 메일이 오지 안아 집에서 좀비처럼 유튜브만 보고 있었는데, 결국 8시 넘어 메일이 왔다.\n▶️ 앞으로 # 솔직하게 AWS 자격증만 있을 뿐 실력은 많이 부족하다고 느낀다. 하지만 이제 AWS 공부보다는 백엔드 쪽을 더 파려고 한다. AWS는 사이드프로젝트 배포용으로만 쓰다가, 만일 내가 AWS를 제대로 활용하는 곳에서 일을 하게 된다면 다시 공부해야지.\n","date":"21 December 2024","externalUrl":null,"permalink":"/posts/velog/008-aws-scs-c02-%ED%9B%84%EA%B8%B0/","section":"Posts","summary":" ⛅️어쩌다가 SPECIALTY까지? # 생일날 갑자기 삘빧아서 AWS 자격증에 도전 이후 벌써 4번째 자격증을 취득했다.\n원래 AWS는 해킹당한 이후 처다도 안보다가, 개발자로서 DVA 만 따려고 했었는데 …\n😎 DVA 시험비용이 150$가 넘네? =\u003e AWS Ec2만 만들어봤는데 떨어지면 한달 생활비 $$\\frac{1}{2}$$?? ㅈ되네?\n🤔 CLF를 따면 다음 시험을 50% 할인해주니깐 3만원 정도밖에 차이가 안나 =\u003e 안전하게 가야지!\n","title":"AWS SCS-C02 후기","type":"posts"},{"content":"","date":"27 November 2024","externalUrl":null,"permalink":"/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":" Docker 기본개념\n멀티스테이지 빌드란? # DockerFile 내에서 여러 단계를 정의한 후, 각 단계마다 따로 작업을 수행하고 최종 이미지에 최소한의 파일만 포함시키는 방식\n예시 # 실제 썼던 파일\n원래 DockerFile # FROM golang:1.23-alpine WORKDIR /app # go.mod와 go.sum 복사 후 의존성 다운로드 COPY go.mod go.sum ./ RUN go mod download # 소스 코드 전체 복사 및 빌드 COPY . ./ RUN go build -o crawler . # 실행 명령어 CMD [\u0026#34;./crawler\u0026#34;] 혹시나 golang을 처음 보는 사람을 위해 설명하자면, go.mod ,go.sum은 package.json, build.gradle 등과 같이 라이브러리를 관리하는 파일이다.\n멀티스테이지 빌드 DockerFile # # 빌드 스테이지 FROM golang:1.23-alpine AS build WORKDIR /app # go.mod와 go.sum 복사 후 의존성 다운로드 및 빌드 COPY go.mod go.sum ./ RUN go mod download # 소스 코드 전체 복사 및 빌드 COPY . . RUN go build -o crawler . ######################################################### #실제 도커파일❗❗ FROM alpine:latest WORKDIR /app # 빌드된 바이너리 복사 COPY --from=build /app/crawler /app/crawler CMD [\u0026#34;/app/crawler\u0026#34;] 멀티스테이징 빌드에서는 첫 스태이지에서 의존성을 다운받고 파일을 빌드, 두번째 스테이지에서 첫 스태이지의 결과물(실행파일)을 가져와 사용한다. 왜 귀찮게 이러는걸까?\n최종 이미지 크기가 줄어든다 go를 예시로 들자면, 이미 라이브러리를 사용해서 빌드한 후, 라이브러리 파일은 더이상 필요하지 않다. 따라서, 꼭 필요한 결과물만 최종 단계로 가져올 수 있다. (+보안)\n실험 결과 한번에 했을때 354MB, 멀티 스태이징 빌드시 18MB로 용량이 엄청 줄어들었다.\n캐싱(이는 꼭 멀티 스테이징지 이점은 아님) 소스 코드는 개발 도중 계속 바뀌지만, 의존성은 드물게 바뀐다. 이미지 빌드시 의존성이 바뀌지 않았다면, 의존성을 가져오는 단계를 이전에 빌드했던 캐시를 통해 사용해 빌드 속도를 높일 수 있다.\n명령어,입력 파일,환경이 완전히 동일하면, 캐시를 사용한다 따라서 자주 변경될수록 아래쪽에 위치하는 것이 효율적이다.\n캐싱 예시 # 첫번째 빌드시에는 모든 과정을 다 수행한다.\n=\u0026gt; [stage-1 2/3] WORKDIR /app 0.0s =\u0026gt; [build 2/6] WORKDIR /app 0.2s =\u0026gt; [build 3/6] COPY go.mod go.sum ./ 0.0s =\u0026gt; [build 4/6] RUN go mod download 1.0s =\u0026gt; [build 5/6] COPY . . 0.0s =\u0026gt; [build 6/6] RUN go build -o crawler . 4.2s =\u0026gt; [stage-1 3/3] COPY --from=build /app/crawler /app/crawler 0.0s =\u0026gt; exporting to image 두번째 빌드부터는, 의존성을 가져오는 과정이 캐시를 통해 생략된다.\n=\u0026gt; CACHED [build 2/6] WORKDIR /app #cahced =\u0026gt; CACHED [build 3/6] COPY go.mod go.sum ./ #cached =\u0026gt; CACHED [build 4/6] RUN go mod download #cahced =\u0026gt; [build 5/6] COPY . . =\u0026gt; [build 6/6] RUN go build -o crawler . =\u0026gt; CACHED [stage-1 2/3] WORKDIR /app # 단순 설정이라 캐시사용 =\u0026gt; [stage-1 3/3] COPY --from=build /app/crawler /app/crawler =\u0026gt; exporting to image =\u0026gt; =\u0026gt; exporting layer 이 예제는 아주 간단한 경우지만, 만약 프론트는 node, 백엔드는 spring 등으로 이미지가 여러 단계를 거치게 되면 더 효율적일 것이다.👍\n이거랑 커밋시 도커허브에 push하는 워크플로우 테스트하다보니 깃허브에서 메일이 30개정도 와있었다\n","date":"27 November 2024","externalUrl":null,"permalink":"/posts/velog/009-docker-%EB%B9%8C%EB%93%9C-%EC%BA%90%EC%8B%9C%EC%99%80-%EB%A9%80%ED%8B%B0%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EB%B9%8C%EB%93%9C/","section":"Posts","summary":" Docker 기본개념\n멀티스테이지 빌드란? # DockerFile 내에서 여러 단계를 정의한 후, 각 단계마다 따로 작업을 수행하고 최종 이미지에 최소한의 파일만 포함시키는 방식\n예시 # 실제 썼던 파일\n원래 DockerFile # FROM golang:1.23-alpine WORKDIR /app # go.mod와 go.sum 복사 후 의존성 다운로드 COPY go.mod go.sum ./ RUN go mod download # 소스 코드 전체 복사 및 빌드 COPY . ./ RUN go build -o crawler . # 실행 명령어 CMD [\"./crawler\"] 혹시나 golang을 처음 보는 사람을 위해 설명하자면, go.mod ,go.sum은 package.json, build.gradle 등과 같이 라이브러리를 관리하는 파일이다.\n","title":"Docker 빌드 캐시와 멀티스테이지 빌드","type":"posts"},{"content":"","date":"5 November 2024","externalUrl":null,"permalink":"/tags/leetcode/","section":"Tags","summary":"","title":"Leetcode","type":"tags"},{"content":"","date":"5 November 2024","externalUrl":null,"permalink":"/tags/string/","section":"Tags","summary":"","title":"String","type":"tags"},{"content":" Levenshtein distance? # 문자열 비교 알고리즘으로, 문자열 a,b가 있을 때 a를b로 만드는데 드는 비용을 동적 계획법으로 구하는 알고리즘 문자열의 유사성을 판단하는데 쓰인다.\n기본 풀이 # dp[i][j] = 문자열 a의 [i]까지 고려했을때, 문자열 b의[j]를 만들 수 있는 최소 변환 수\n문자열은 빈 문자열 ''부터 시작한다. 빈 문자열은 다른 문자열의 길이만큼 특정 문자를 추가해야 같아지므로 각각 인덱스값을 가지게 된다. dp[1][1]은 세 가지 중 하나로 만들어지는데\ndp[0][1](빈 문자열에서 r을 만듬)에서 h를 삭제 dp[1][0](h를 빈 문자열로 만듬)에서 r을 추가 dp[-1][-1](빈 문자열)에서 이번에 추가된 h를 r로 교체 만일 word1[i]==word2[j]일 경우, 교체비용은 0 이중 최적의 경우 + 해당 문자열을 연산하는 비용(1) 이다. 만일 i번째 문자열이 j번째 문자열과 같다면, 굳이 대체/삭제/추가할 필요가 없이, dp[i-1][j-1]의 결과를 그대로 가져다 쓰면 된다.\n점화식 # $$ D[i][j] = \\begin{cases} D[i-1][j-1] \u0026 \\text{if } \\text{str1}[i-1] = \\text{str2}[j-1] \\\\ \\min(D[i-1][j] + 1, D[i][j-1] + 1, D[i-1][j-1] + 1) \u0026 \\text{if } \\text{str1}[i-1] \\neq \\text{str2}[j-1] \\end{cases} $$ 결과 # 결과는 다음과 같다. 세로 문자열을 최종적으로 가로 문자열로 변환하므로, dp[-1][-1]인 3이 된다.\n코드 # 릿코드 문제링크\n기본풀이 # class Solution: def minDistance(self, word1: str, word2: str) -\u0026gt; int: n,m = len(word1),len(word2) dp = [[None] * (m+1) for _ in range(n+1)] for i in range(n+1): dp[i][0] = i for j in range(m+1): dp[0][j] = j for i in range(1,n+1): for j in range(1,m+1): if word1[i - 1] == word2[j - 1]: # 문자가 같다면 dp[i][j] = dp[i - 1][j - 1] # 대각선 값 그대로 사용 else: # 문자가 다를 때만 삽입, 삭제, 대체 중 최소값 + 1 dp[i][j] = min( dp[i - 1][j - 1], # 대체 dp[i - 1][j], # 삭제 dp[i][j - 1] # 삽입 ) + 1 return (dp[-1][-1]) 메모리 효율 개선 # class Solution: def minDistance(self, word1: str, word2: str) -\u0026gt; int: n,m = len(word1),len(word2) dp = [i for i in range(m+1)] tmp = [-1] + [0]*m for i in range(1,n+1): tmp[0] = i for j in range(1,m+1): if word1[i-1] == word2[j-1]: tmp[j] = dp[j-1] else: tmp[j] = min( dp[j-1], dp[j], tmp[j-1], )+1 for k in range(m+1) : dp[k] = tmp[k] return dp[-1] 재귀 # class Solution: def minDistance(self, word1: str, word2: str) -\u0026gt; int: @cache def dfs(i, j): if i == 0: return j if j == 0: return i if word1[i-1] == word2[j-1]: return dfs(i-1, j-1) else: replace = dfs(i - 1, j -1) insert = dfs(i, j - 1) delete = dfs(i - 1, j) return min(replace, insert, delete) + 1 return dfs(len(word1), len(word2)) bfs(큐) # class Solution: def minDistance(self, word1: str, word2: str) -\u0026gt; int: return self.bfs(word1,word2) def bfs(self, word1, word2): m, n = len(word1), len(word2) q = deque([(0, 0)]) numEdits = 0 visited = set() while q: qLen = len(q) for _ in range(qLen): i, j = q.popleft() if (i,j) in visited: continue visited.add((i,j)) while i \u0026lt; m and j \u0026lt; n and word1[i] == word2[j]: i += 1 j += 1 ","date":"5 November 2024","externalUrl":null,"permalink":"/posts/velog/010-%EB%A0%88%EB%B2%A4%EC%8A%88%ED%83%80%EC%9D%B8-%EA%B1%B0%EB%A6%AC/","section":"Posts","summary":"Levenshtein distance? # 문자열 비교 알고리즘으로, 문자열 a,b가 있을 때 a를b로 만드는데 드는 비용을 동적 계획법으로 구하는 알고리즘 문자열의 유사성을 판단하는데 쓰인다.\n기본 풀이 # dp[i][j] = 문자열 a의 [i]까지 고려했을때, 문자열 b의[j]를 만들 수 있는 최소 변환 수\n문자열은 빈 문자열 ''부터 시작한다. 빈 문자열은 다른 문자열의 길이만큼 특정 문자를 추가해야 같아지므로 각각 인덱스값을 가지게 된다. dp[1][1]은 세 가지 중 하나로 만들어지는데\n","title":"레벤슈타인 거리","type":"posts"},{"content":"문제링크\n설명 # 임의의 ASCII 문자열 S가 주어졌을때, 모음 {'a','e','i','o','u'} 를 각각 짝수번 포함(0번도 가능) 하고 있는 부분 문자열 중 가장 긴 길이를 구하여라\n\u0026quot;leetcodeisgreat\u0026quot; =\u0026gt; \u0026quot;leetc\u0026quot; =\u0026gt; 5\n💡풀이 # 처음에 투포인터 문제인줄 알아서 많이 헤멨다\n첫 상태를 정의한다 ( 모든 모음이 0번 나타난 상태) 모음을 2의 제곱 비트로 정의하고, 모음이 나타날때마다 비트마스크를 업데이트한다. 이전에 기록되지 않았다면 map에 넣는다 비트마스크가 이미 존재한다면, 이전 비트마스크와 현재 비트마스크 사이에는 모든 모음이 짝수번 존재하므로, 최대길이와 비교한다 코드 # class Solution: def findTheLongestSubstring(self, s: str) -\u0026gt; int: vowels = {\u0026#39;a\u0026#39;: 1\u0026lt;\u0026lt;0, \u0026#39;e\u0026#39;: 1\u0026lt;\u0026lt;1, \u0026#39;i\u0026#39;: 1\u0026lt;\u0026lt;2, \u0026#39;o\u0026#39;: 1\u0026lt;\u0026lt;3, \u0026#39;u\u0026#39;: 1\u0026lt;\u0026lt;4} # vowels = {\u0026#39;a\u0026#39;:1, \u0026#39;e\u0026#39;: 2, \u0026#39;i\u0026#39;: 3, \u0026#39;o\u0026#39;: 4, \u0026#39;u\u0026#39;: 5} # 왜 안되는지 모르겠다면, 3인 경우를 생각해보자 my_map ={ 0 : -1} mask = 0 answer= 0 for i,c in enumerate(s): if c in vowels: mask ^= vowels[c] if mask not in my_map: my_map[mask] = i else : answer = max(answer, i-my_map[mask]) return answer 😎그다지 어렵진 않지만, 풀이가 재미있는 문제였다.\n","date":"15 September 2024","externalUrl":null,"permalink":"/posts/velog/011-1371.-find-the-longest-substring-containing-vowels-in-even-counts/","section":"Posts","summary":"문제링크\n설명 # 임의의 ASCII 문자열 S가 주어졌을때, 모음 {'a','e','i','o','u'} 를 각각 짝수번 포함(0번도 가능) 하고 있는 부분 문자열 중 가장 긴 길이를 구하여라\n\"leetcodeisgreat\" =\u003e \"leetc\" =\u003e 5\n💡풀이 # 처음에 투포인터 문제인줄 알아서 많이 헤멨다\n첫 상태를 정의한다 ( 모든 모음이 0번 나타난 상태) 모음을 2의 제곱 비트로 정의하고, 모음이 나타날때마다 비트마스크를 업데이트한다. 이전에 기록되지 않았다면 map에 넣는다 비트마스크가 이미 존재한다면, 이전 비트마스크와 현재 비트마스크 사이에는 모든 모음이 짝수번 존재하므로, 최대길이와 비교한다 코드 # class Solution: def findTheLongestSubstring(self, s: str) -\u003e int: vowels = {'a': 1\u003c\u003c0, 'e': 1\u003c\u003c1, 'i': 1\u003c\u003c2, 'o': 1\u003c\u003c3, 'u': 1\u003c\u003c4} # vowels = {'a':1, 'e': 2, 'i': 3, 'o': 4, 'u': 5} # 왜 안되는지 모르겠다면, 3인 경우를 생각해보자 my_map ={ 0 : -1} mask = 0 answer= 0 for i,c in enumerate(s): if c in vowels: mask ^= vowels[c] if mask not in my_map: my_map[mask] = i else : answer = max(answer, i-my_map[mask]) return answer 😎그다지 어렵진 않지만, 풀이가 재미있는 문제였다.\n","title":"1371. Find the Longest Substring Containing Vowels in Even Counts","type":"posts"},{"content":" ☁️ DVA-C03 # 🌤️준비 # 8/15 ~ 9/14 약 한달정도 공부\n이전시험 합격 후 9/14일에 시험 유데미 스테판강의, exampleTopics 덤프로 공부 후기 # 이전시험 (DVA) 은 lambda, SQS, CI/CD 등 개발자에 초점이 맞춰져 있다면, 이번 시험은 말그대로 전체적인 클라우드 설계에 대해 다룸.\n양은 방대했지만 이전시험때 공부한 내용과 겹치는 부분이 많아 편했음 이 시험을 공부했다고 aws에 익숙해지진 않았지만, 최소한 실무에서 aws를 쓰게될때 이해하기 편할듯? 😎이제 당분간 AWS는 미뤄두고, postgres를 깊이 파볼 생각임\n","date":"15 September 2024","externalUrl":null,"permalink":"/posts/velog/012-aws-saa-co3-%ED%9B%84%EA%B8%B0/","section":"Posts","summary":"☁️ DVA-C03 # 🌤️준비 # 8/15 ~ 9/14 약 한달정도 공부\n이전시험 합격 후 9/14일에 시험 유데미 스테판강의, exampleTopics 덤프로 공부 후기 # 이전시험 (DVA) 은 lambda, SQS, CI/CD 등 개발자에 초점이 맞춰져 있다면, 이번 시험은 말그대로 전체적인 클라우드 설계에 대해 다룸.\n","title":"AWS SAA-CO3 후기","type":"posts"},{"content":" ☁️ DVA-C02 # **8/15 합격! **\n🌤️준비기간 # 대략 한달정도 준비\n❗️7월 1일(cloud Practitioner)시험을 통과한 후 광복절에 시험을 쳤다 중간에 공부못한 기간 포함하면 약 한달정도 퇴근후 집에서 공부\n🌤️ 준비방법 # 유데미스테판강의 (진짜 얻는게 많음)\nexampleTopic (필수)\n❗️이전 시험과 마찬가지로, 자격증 뱃지만이 목적이라면, 강의들을 필요 없이 기출문제 돌리면 된다 본인 성장이 목적이면 무조건 강의수강하는걸 추천한다.\n99000(원가) 내고 수강하면 호구임\n🌤️난이도 # 기출만 잘 돌리면 일주일도 가능할듯? 대부분의 문제가 정답과 오답이 명확하게 나옴 🌤️후기 # 무조건 강의를 듣는걸 권장한다\n❗️이전의 cloud Practitioner 시험공부하면서 얻은건, AWS용어에 익숙해진게 전부였다. 하지만 이번 시험을 공부하면서 얻은게 정말 많다. 특히 현재 사이드프로젝트를 AWS를 이용해서 하고 있는데, 이전에는 아무것도 모른채 블로그 따라하면서 서버띄우는게 전부였다면, 지금은 의미와 이유를 조금이나마 이해하고 있다.\n😎이제 SAA-03을 준비할 예정이다. # ","date":"15 August 2024","externalUrl":null,"permalink":"/posts/velog/013-aws-dva-c02-%ED%9B%84%EA%B8%B0/","section":"Posts","summary":"☁️ DVA-C02 # **8/15 합격! **\n🌤️준비기간 # 대략 한달정도 준비\n❗️7월 1일(cloud Practitioner)시험을 통과한 후 광복절에 시험을 쳤다 중간에 공부못한 기간 포함하면 약 한달정도 퇴근후 집에서 공부\n🌤️ 준비방법 # 유데미스테판강의 (진짜 얻는게 많음)\n","title":"AWS DVA-C02 후기","type":"posts"},{"content":" 😎노베이스 # 다른 후기들을 찾아보면, 전부다 노베이스라고 해놓고 어느정도 지식이 있는 상태에서 봤다는 말을 하고 있다. 나는 1년쯤 전에 aws계정을 만들고 얼마안되 해킹당해서 900$를 뜯기고*(물론 온몸비틀어서 받음)* 그 이후에 처다보지도 않았다.\n다른 노베이스 상태의 독자에게 도움이 되었으면 한다\n☁️결심 # 마지막으로 자격증을 딴지 너무 오래되어서, 뭐라도 자격증을 취득하려고 알아보던중, 갑자기 AWS자격증이 끌렸다. 원래 한단계 위인 DVA 자격증부터 취득하려고 했는데 내가 지금까지 봤던 모든 시험 중 가장 비싼 시험료😱(CLF:100$ , DVA:150$)에 놀라, 기초인 CLF부터 따기로 마음먹었다.\n💡 AWS의 시험에 합격하면, 그 다음 시험은 50%에 볼 수 있다. 나같은 고민을 하는 사람이 있다면, 25$차이니 CLF부터 따는것을 추천한다. 치킨2마리안먹지뭐\n🌤️준비 # 순서대로\nAWS Cloud Practitioner Essentials(Korean)\nAWS공식 한국어 강의로, 클라우드의 기본에 대한 내용을 설명해준다 깊은 내용은 아니지만, 노베이스에서 파악하기엔 상당히 도움이 된다 (그리고 재미있다) 정우성닮은 아저씨가 인상깊었다 Exam Prep Standard Course\n1과 거의 비슷하지만, 시험볼때의 팁과, 좀더 많은 정보를 제공한다 영어로 진행되지만, 한글 자막과 자료를 지원한다 ❗️❗️exampleTopics❗️❗️\n거의 모든 후기에 공통적으로 나오는 Dump문제 사이트인데, 거의 시험이 그대로 나온다. 💡사실 단순히 시험통과만이 목표라면 1,2 는 스킵가능하다 덤프 뺑뺑이만 돌려도 무조건 붙는다 ** 💡 문제의 반만 무료로 볼 수 있으며, 그마저도 페이지 이동시 CAPTCHA로 귀찬게 한다 유료나는 상당히 비싸므로exampletopics CLF-C02 {문제번호}** 이런식으로검색하면, 문제를 우회해서 볼 수 있다.\n🌪️시험 # 시험접수과정은 생략하겠다\n💡 원격시험을 볼 경우, 감독관 확인 대기열을 기다리게 된다. 후순위면 상당히 오래 걸리므로 최대한 빨리 들어가자 나는 경우 30분 이상을 웹캠 모니터만 보면서 기다렸는데, 나가지도 못하고 폰도 못만지므로 정신나가는줄 알았다\n💡 대기중 오류메시지가 떠도, 대기열이 줄어들고 있으면 안심해도 된다. 실제 시험시간이 넘어도 마찬가지이다\n💡 감독관은 인도 억양에다가 통화품질까지 안좋기때문에, 못알아듯겠다면 채팅으로 써달라고 요청하면 된다\n💡 문제 개쉬우니깐 쫄지 마라\n👍자랑 # 감독관이 시험을 체크하는데, 무슨 문제가 있었는지 30분정도 기다렸다고, 어떤 시험에서든 쓸 수 있는 무료 바우처를 받았다.\n이제 DVA취득이 목표이다\n","date":"1 July 2024","externalUrl":null,"permalink":"/posts/velog/014-%EB%85%B8%EB%B2%A0%EC%9D%B4%EC%8A%A4-aws-clf-%ED%9B%84%EA%B8%B0/","section":"Posts","summary":" 😎노베이스 # 다른 후기들을 찾아보면, 전부다 노베이스라고 해놓고 어느정도 지식이 있는 상태에서 봤다는 말을 하고 있다. 나는 1년쯤 전에 aws계정을 만들고 얼마안되 해킹당해서 900$를 뜯기고*(물론 온몸비틀어서 받음)* 그 이후에 처다보지도 않았다.\n다른 노베이스 상태의 독자에게 도움이 되었으면 한다\n☁️결심 # 마지막으로 자격증을 딴지 너무 오래되어서, 뭐라도 자격증을 취득하려고 알아보던중, 갑자기 AWS자격증이 끌렸다. 원래 한단계 위인 DVA 자격증부터 취득하려고 했는데 내가 지금까지 봤던 모든 시험 중 가장 비싼 시험료😱(CLF:100$ , DVA:150$)에 놀라, 기초인 CLF부터 따기로 마음먹었다.\n","title":"노베이스 AWS CLF 후기","type":"posts"},{"content":"문제링크\n설명 # arr[i] 는 각각 i번째 노동자의 품질,월급을 나타내는 quality, wage 배열이 주어진다 가장 최소한의 비용으로 k 명을 고용할때 드는 비용을 구하여라\n조건\ni번째 노동자를 고용하려면, 최소 wage[i] 보다 돈을 많이줘야됨 각 작업자의 월급은, quality에 정비례해야 함 quality가 얼마던 간에 최소한의 비용이면 상관없음ㅋㅋ 💡풀이 # 가장 중요한건, 기준으로 삼을 작업자를 정하는 것이다\n가장 효율적인 노예 작업자를 순서로 정렬한다 (돈을 적게 받고, 능률은 뛰어난 $\\frac{quality}{weight}$) i번 노예는, 무조건 i+1번 노예가 받는 돈을 기준으로 했을때 만족하게 된다\n효율적인 순서대로 k명을 뽑는다. 이러면, 우리는 가장 효율적인 k을 고르게 된다\n사실, 효율은 정답과는 아무 상관이 없다. 노예1이 노예2에 비해 아무리 효율이 좋아도, 노예2가 퀄리티가 낮아 돈을 덜 받으면 노예 2를 써야 한다\n점점 비효율적인 얘들을 차례로 탐색하며, 최소값을 구한다 사실, 효율적인 놈들 대신 비효율적인 애들을 쓸 경우는 퀄리티가 너무 높아서, 효율이 좋은데도 돈을 많이 받는 경우 이다\n따라서, 현재 뽑은 사람들 중 퀄리티가 제일 높은놈을 제외하고, i번째 비효율적인 놈 에 맞춰서 계산을 할 때, 총 월급이 더 적게 드는지를 계산하여 업데이트하면 된다.\n전체코드 # class Solution: def mincostToHireWorkers(self, quality: List[int], wage: List[int], k: int) -\u0026gt; float: worker_len = len(quality) efficent_arr = sorted( [ (wage[i]/quality[i], quality[i]) for i in range(worker_len) ] ) max_heap , all_quality =[],0 for i in range(k): #(k개를 만족하는)가장 효율적 경우부터 all_quality += efficent_arr[i][1] heapq.heappush( max_heap, - efficent_arr[i][1] ) answer = efficent_arr[i][0] * all_quality # 가장 효율적인 팀은 구성했을때의 총 금액 # 비 효율적이여도, quality가 낮으면, 필요한 돈은 오히려 적을 수 있음 for i in range(k, worker_len): ith_worker_efficent, ith_worker_quality = efficent_arr[i][0], efficent_arr[i][1] # 가장 높은 quality를 대채한 후, [i]번째 효율적인 놈을 기준으로 계산하여 정답 업데이트 most_quality = -heapq.heappop(max_heap) all_quality -= most_quality all_quality += ith_worker_quality heapq.heappush(max_heap,-ith_worker_quality) answer = min(answer, ith_worker_efficent*all_quality) return answer ","date":"11 May 2024","externalUrl":null,"permalink":"/posts/velog/015-857.-minimum-cost-to-hire-k-workers/","section":"Posts","summary":"문제링크\n설명 # arr[i] 는 각각 i번째 노동자의 품질,월급을 나타내는 quality, wage 배열이 주어진다 가장 최소한의 비용으로 k 명을 고용할때 드는 비용을 구하여라\n조건\ni번째 노동자를 고용하려면, 최소 wage[i] 보다 돈을 많이줘야됨 각 작업자의 월급은, quality에 정비례해야 함 quality가 얼마던 간에 최소한의 비용이면 상관없음ㅋㅋ 💡풀이 # 가장 중요한건, 기준으로 삼을 작업자를 정하는 것이다\n","title":"857. Minimum Cost to Hire K Workers","type":"posts"},{"content":"","date":"11 May 2024","externalUrl":null,"permalink":"/tags/greedy/","section":"Tags","summary":"","title":"Greedy","type":"tags"},{"content":" 문제링크\n설명 # 문자열 중에 최대 한 글자만 홀수 번 나타나면 완벽한 문자열임 단어 word가 주어졌을때, 여기에서 나타날 수 있는 모든 완벽한 문자열의 수를 구하셈 (world는 a~j만으로 구성)\n풀이 # 단어를 순회하면서, 해당 단어를 비트로 바꿔 bitmask 집합에 넣는다. 이 비트마스를 key로 하는 dict에서, dict[key] 가 존재한다면, 그만큼 더한다 dict[key]를 1증가시킨다 왜? XOR연산은, 같은 숫자가 짝수번 연산되면 모두 상쇄되어 사라진다. 비트마스크는 현재의 상태 를 의미하는데, 이전에 똑같은 마스크가 존재했다는 뜻은, 그 사이에 있는 모든 단어들은 짝수번 등장한다는 뜻이다 근데 왜 dict[key]만큼 더함??\n우리가 단어를 순회하면서 구하는것은, 현재 문자를 끝으로 하는 완벽한 부분 문자열의 수이다 만약 지금의 비트마스크 전에 n개의 비트마스크가 있다고 가정하면, 그 n개의 비트마스크 모두 부분 문자열의 시작이 될 수 있는 것이다 하지만, 이건 부분문자열의 모든 단어 등장 짝수번 등장하는 경우이다.\n빈도가 홀수번 등장하는 단어를 찾는다. 위에서 구했던 모든 단어가 짝수번 등장하는 문자열 에서, 강제로 단어를 넣어 하나의 문자가 홀수번 등장하고, 나머지가 모두 짝수인 문자열 을구한다 위와 같은 원리로, 이 문자열의 상태(bitmask)가 존재한다면 그만큼 더해주면 된다.\n전체코드 # def wonderfulSubstrings(word: str) -\u0026gt; int: bitmask=0 counter=defaultdict(int) counter[0] = 1 answer=0 for w in word: bit = 1\u0026lt;\u0026lt;(ord(w)-97) bitmask ^=bit #현재비트를 토글해서 \u0026#34;상태\u0026#34;를 구함 answer+=counter[bitmask] #이전에 \u0026#34;현재상태\u0026#34;가 등장한 수만큼 구함(모든단어빈도짝수) counter[bitmask] +=1 #현재상태 추가 for i in range(10): answer += counter[bitmask ^ 1\u0026lt;\u0026lt;i] # 강제로 단어를 집어넣어서, 하나가 홀수일 경우의 수 구함 return answer 솔직히 어색하고 개어려움 더 최적화한사람들도 있는데 여기까지만 하려고 함 담에 비슷한 문제가 나올때 풀 수 있을지 모르겠음\n","date":"30 April 2024","externalUrl":null,"permalink":"/posts/velog/016-1915.-number-of-wonderful-substrings/","section":"Posts","summary":" 문제링크\n설명 # 문자열 중에 최대 한 글자만 홀수 번 나타나면 완벽한 문자열임 단어 word가 주어졌을때, 여기에서 나타날 수 있는 모든 완벽한 문자열의 수를 구하셈 (world는 a~j만으로 구성)\n풀이 # 단어를 순회하면서, 해당 단어를 비트로 바꿔 bitmask 집합에 넣는다. 이 비트마스를 key로 하는 dict에서, dict[key] 가 존재한다면, 그만큼 더한다 dict[key]를 1증가시킨다 왜? XOR연산은, 같은 숫자가 짝수번 연산되면 모두 상쇄되어 사라진다. 비트마스크는 현재의 상태 를 의미하는데, 이전에 똑같은 마스크가 존재했다는 뜻은, 그 사이에 있는 모든 단어들은 짝수번 등장한다는 뜻이다 근데 왜 dict[key]만큼 더함??\n","title":"1915. Number of Wonderful Substrings","type":"posts"},{"content":"","date":"30 April 2024","externalUrl":null,"permalink":"/tags/bitmask/","section":"Tags","summary":"","title":"Bitmask","type":"tags"},{"content":"문제링크\n설명 # 💀 0~n-1까지 방향이 없는 트리와, 연결고리edges가 주어졌을때,\nanswer[i] = i번째 노드와 다른 모든 노드들과 거리의 합 를 만족하는 answer을 리턴하기\ndef sumOfDistancesInTree(self, n: int, edges: List[List[int]]) -\u0026gt; List[int]: # n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]] 풀이과정 # 1. 배열 2개 answer,node_cnt 생성 # answer : 정답용 answer[i] = 다른 모든 노드들과의 거리 합 node_cnt : 현재 정점의 누적 노드 개수\n2. root를 기준으로 answer,node_cnt 채우기 # 루트를 기준으로, 밑에서 위로올라오면서 배열 채우기\nanswer[i] = answer[i의 자식]+node_cnt[i의 자식] 의 합 node_cnt[i] = i의 자식노드들 정점의 수 +1 (자기자신)\n## dfs1() //node_cnt [6]#0 #root = 1+4+1=6 / \\ [1]#1 [4]#2 #1=1 #2=1+3 = 4 / | \\ [1][1][1] //answer [8]#0 #root = (0+1)+(3+4) =8 / \\ [0]#1 [3]#2 #1=\u0026gt; 0+(0*0) =0 #2=0+(3*1) = 3 / | \\ [0][0][0] #3,4,5=\u0026gt; 0+0 = 0 3.각 노드의 answer,node_cnt 채우기 # 한번 순회했으면, node_cnt는 완성된 상태고 answer[0]의 값 (루트) 는 건들지 않아도 됨👍 하지만 answer의다른 노드들은 밑에서 올라온 값만 계산한 상태임 따라서, 이번에는 위에서 내려가면서 배열을 갱신해줌\n**answer[i] =(answer[i의부모] - node_cnt[i]) + (n-node_cnt[i]) ** 생각을 해보자 만일 2번 노드에 대한 정답을 구하려면, 루트노드와 2번의 하위 노드들을 토대로 구해야 한다\nanswer[0](루트)는 이미 2번에서 올라온 값을 가지고 있으므로, 이 값들은 중복됨. 따라서 node_cnt[i]만큼을 빼줘야 함 반대로, 2번의 하위 노드가 아닌 얘들은, 길이가 하나씩 늘어날거임 =\u0026gt; (전체 개수-node_cnt[2])를 하면 2번에 속하지 않은 애들 개수가 나오니, 이를 더해줌 ## dfs2() //node_cnt [6]#0 / \\ [1]#1 [4]#2 / | \\ [1][1][1] //answer (위에서부터 계산됨) [8]#0 #root는 그대로 / \\ [12]#1 [6]#2 #1=\u0026gt; (8-1)+(6-1)= 12 #2=\u0026gt;(8-4)+(6-4)=6 / | \\ [5][5][5] #3,4,5=\u0026gt; (6-1)+(6-1) = 5 이러면 answer이 완성됨\n전체 코드 # class Solution: def sumOfDistancesInTree(self, n: int, edges: List[List[int]]) -\u0026gt; List[int]: tree = [[] for _ in range(n)] answer = [0 for _ in range(n)] node_cnt = [1 for _ in range(n)] for a,b in edges: tree[a].append(b) tree[b].append(a) def dfs(now, parent): for child_node in tree[now]: if child_node == parent: continue; dfs(child_node,now) node_cnt[now] += node_cnt[child_node] answer[now] += answer[child_node] + node_cnt[child_node] dfs(0,-1) def dfs2(now,parent): for child_node in tree[now]: if child_node == parent: continue answer[child_node] = (answer[now]-node_cnt[child_node]) + (n-node_cnt[child_node]) dfs2(child_node, now) dfs2(0,-1) return answer ","date":"28 April 2024","externalUrl":null,"permalink":"/posts/velog/017-834.-sum-of-distances-in-tree/","section":"Posts","summary":"문제링크\n설명 # 💀 0~n-1까지 방향이 없는 트리와, 연결고리edges가 주어졌을때,\nanswer[i] = i번째 노드와 다른 모든 노드들과 거리의 합 를 만족하는 answer을 리턴하기\ndef sumOfDistancesInTree(self, n: int, edges: List[List[int]]) -\u003e List[int]: # n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]] 풀이과정 # 1. 배열 2개 answer,node_cnt 생성 # answer : 정답용 answer[i] = 다른 모든 노드들과의 거리 합 node_cnt : 현재 정점의 누적 노드 개수\n","title":"834. Sum of Distances in Tree","type":"posts"},{"content":" ✅프로그램과 프로세스 # 프로그램은 정적인 파일(코드,데이터) 의 집합이며, 시스템의 디스크에만 공간을 차지함 template 이를 실행하면 프로세스가 되는데, 프로그램의 코드와 데이터가 메모리에 로드되고, CPU,RAM 등의 시스템 리소스를 사용하는 동적인 상태가 됨 이미지 💿 # 프로그램과 대응 프로그램(들)과, 이를 실행시킬 수 있는 환경이 디스크 공간을 차지하는 상태 불변이며, 따로 자원을 소모하지 않는 상태 컨테이너🐋 # 프로세스와 대응 실행 상태의 이미지로, 이미지를 복사해 격리된 공간을 생성하고 시스템 리소스를 사용 이미지의 구성 # 이미지는 base image부터 레이어들을 쌓아올려서 생성됨 이때 이 레이어들은 불변 이며, 이전 레이어에서 변경된 사항임 (환경변수 등)\n기본 nginx 이미지의 history # ❯ docker history nginx IMAGE CREATED CREATED BY SIZE COMMENT 070027a3cbe0 5 weeks ago CMD [\u0026#34;nginx\u0026#34; \u0026#34;-g\u0026#34; \u0026#34;daemon off;\u0026#34;] 0B buildkit.dockerfile.v0 \u0026lt;missing\u0026gt; 5 weeks ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0 ..... nginx 설정파일을 추가해 이미지 생성 # ❯ docker history mynginx:1.0.0 IMAGE CREATED CREATED BY SIZE COMMENT 02b2c4b62ed4 2 minutes ago COPY nginx.conf /etc/nginx/nginx.conf # buil… 242B buildkit.dockerfile.v0 \u0026lt;missing\u0026gt; 5 weeks ago CMD [\u0026#34;nginx\u0026#34; \u0026#34;-g\u0026#34; \u0026#34;daemon off;\u0026#34;] 0B buildkit.dockerfile.v0 \u0026lt;missing\u0026gt; 5 weeks ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0 컨테이너의 구성 # 이미지에서 컨테이너가 실행될 때, 레이어들의 불변 이미지들 위에 새로운 쓰기가능한레이어가 추가됨!! 이 레이어에는 컨테이너에서 발생하는 모든 변경사항이 기록됨 =\u0026gt; commit으로 새로운 이미지도 생성 가능 당연한 말이겠지만, 컨테이너가 삭제되면 쓰기 레이어도 삭제됨 =\u0026gt; 이를 막기 위해 volume기능 제공 ","date":"25 March 2024","externalUrl":null,"permalink":"/posts/velog/018-%EB%8F%84%EC%BB%A4-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B8%B0%EB%B3%B8%EA%B0%9C%EB%85%90/","section":"Posts","summary":" ✅프로그램과 프로세스 # 프로그램은 정적인 파일(코드,데이터) 의 집합이며, 시스템의 디스크에만 공간을 차지함 template 이를 실행하면 프로세스가 되는데, 프로그램의 코드와 데이터가 메모리에 로드되고, CPU,RAM 등의 시스템 리소스를 사용하는 동적인 상태가 됨 이미지 💿 # 프로그램과 대응 프로그램(들)과, 이를 실행시킬 수 있는 환경이 디스크 공간을 차지하는 상태 불변이며, 따로 자원을 소모하지 않는 상태 컨테이너🐋 # 프로세스와 대응 실행 상태의 이미지로, 이미지를 복사해 격리된 공간을 생성하고 시스템 리소스를 사용 이미지의 구성 # 이미지는 base image부터 레이어들을 쌓아올려서 생성됨 이때 이 레이어들은 불변 이며, 이전 레이어에서 변경된 사항임 (환경변수 등)\n","title":"도커 이미지,컨테이너 기본개념","type":"posts"},{"content":"","date":"8 October 2023","externalUrl":null,"permalink":"/series/jpa/","section":"Series","summary":"","title":"JPA","type":"series"},{"content":"","date":"8 October 2023","externalUrl":null,"permalink":"/tags/jpa/","section":"Tags","summary":"","title":"JPA","type":"tags"},{"content":" 경로 표현식 # .으로 객체 그래프를 탐색\nSELECT\tm.username -\u0026gt; 상태필드 FROM Member m join m.team t -\u0026gt; 단일 값 연관 필드 join m.orders o -\u0026gt; 컬렉션 값 연관 필드 where t.name = \u0026#39;TEAM_A\u0026#39; 상태 필드 : 단순히 값을 저장하기 위한 필드 -\u0026gt; 경로 탐색의 끝이므로 탐색 ❌ 연관 필드 : 연관관계를 위한 필드 -\u0026gt; 묵시적 내부 조인 발생 단일값 : @ManyToOne, @OneToOne, 엔티티(m.team) -\u0026gt; 탐색 ⭕️ 컬렉션 : @OneToMany, 대상이 컬렉션 (m.orders) -\u0026gt; 탐색 ❌ 실무에선 묵시적 내부조인이 최대한 발생하지 않는 쪽으로 하자 =\u0026gt; 무조건 명시적 조인 사용\nfetch join ❗️❗️❗️ # JPQL에서 성능 최적화를 위해 제공하는 기능 join fetch\n연관된 엔티티나 컬렉션을 SQL 한 번에 같이 조회하는 기능 (즉시 로딩) FetchType.LAZY 보다 우선됨 Distinct # DB에서는 join에 여러 결과가 있으면, 다중 row를 반환한다.\nString query = \u0026#34;select t From Team t join fetch t.members \u0026#34;; List\u0026lt;Team\u0026gt; resultList = em.createQuery(query, Team.class) .getResultList(); 따라서, resultList에는 중복된 team이 들어가게 된다.\nDistinct를 사용해서, 엔티티의 중복을 제거할 수 있다.\nString query = \u0026quot;select distinct t From Team t join fetch t.members \u0026quot;; 한계 # 폐치 조인 대상에는 별칭 사용 ❌(권장) 둘 이상의 컬렉션엔 사용❌ 컬렉션 패치 조인시, 페이징API 사용 ❌ 지금은 쓸 일이 없겠지만, 나중에 이 이걸 읽을때, hibernate.default_batch_fetch_size에 대해 알아보자\n엔티티 직접 사용시 # String query = \u0026quot;select m From Member m where m.team = :team \u0026quot;; 이런식으로 엔티티를 직접 사용시엔 엔티티 ID값을 사용함\nString query = \u0026quot;select m From Member m where m.team.id = :teamid \u0026quot;; 여기에 teamid를 직접넘기는것도 같은 SQL 생성\nNamed쿼리 # @NamedQuery를 사용해서 미리 이름을 부여해두고 사용하는 JPQL ( ** 정적쿼리만 가능 **)\n애플리케이션 로딩 시점에 초기화(검증) 후 재사용) @Entity @NamedQuery( name = \u0026#34;Member.findByUsername\u0026#34;, query = \u0026#34;select m from Member m where m.username = :username\u0026#34; ) public class Member { /////////////////////// //main List\u0026lt;Member\u0026gt; resultList = em.createNamedQuery(\u0026#34;Member.findByUsername\u0026#34;, Member.class) .setParameter(\u0026#34;username\u0026#34;, \u0026#34;user1\u0026#34;) .getResultList(); 스프링 데이터 JPA서는 repository에 코딩 가능\n벌크 연산 # 한방에 여러개 업데이트\nint resultCount = em.createQuery(\u0026#34;update Member m set m.age = 20 where 1=1 \u0026#34;) .executeUpdate(); // 업데이트 한 수 반환 영속성 컨텍스트를 무시하고 DB에 쿼리가 들어가기 때문에, 벌크연산을 먼저 실행하고 -\u0026gt; 영속성 컨텍스트 초기화 em.claer()\n","date":"8 October 2023","externalUrl":null,"permalink":"/posts/velog/019-jpql-%EC%A4%91%EA%B8%89/","section":"Posts","summary":"경로 표현식 # .으로 객체 그래프를 탐색\nSELECT\tm.username -\u003e 상태필드 FROM Member m join m.team t -\u003e 단일 값 연관 필드 join m.orders o -\u003e 컬렉션 값 연관 필드 where t.name = 'TEAM_A' 상태 필드 : 단순히 값을 저장하기 위한 필드 -\u003e 경로 탐색의 끝이므로 탐색 ❌ 연관 필드 : 연관관계를 위한 필드 -\u003e 묵시적 내부 조인 발생 단일값 : @ManyToOne, @OneToOne, 엔티티(m.team) -\u003e 탐색 ⭕️ 컬렉션 : @OneToMany, 대상이 컬렉션 (m.orders) -\u003e 탐색 ❌ 실무에선 묵시적 내부조인이 최대한 발생하지 않는 쪽으로 하자 =\u003e 무조건 명시적 조인 사용\n","title":"JPQL-중급","type":"posts"},{"content":" 조인 # 내부(inner) 조인 SELECT m FROM Member m [INNER] JOIN m.team t 외부(left) 조인 SELECT m FROM Member m LEFT [OUTER] JOIN m.team t 세타? (연관관계 없는거) 조인 SELECT count(m) from Member m, Team t where m.username = t.name ON절 # 조인 대상 필터링\nJPQL : SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A' SQL : SELECT m.*, t.* FROM Member m LEFT JOIN TEAM t ON m.TEAM_ID and t.name='A'\n연관관계 없는 엔티티 외부 조인\nJPQL : SELECT m, t FROM Member m LEFT JOIN TEAM t on m.username = t.name SQL : SELECT m.*, t.* FROM Member m LEFT JOIn TEAM t ON m.username = t.name\n서브쿼리 # 나이가 평균보다 많은 회원 select m from Member m where m.age \u0026gt; (select avg(m2.age) from Member m2) 한 건이라도 주문한 고객 select m from Member m where (select count(o) from Order o where m = o.member) \u0026gt; 0 *{ALL | ANY | SOME}, EXISTS, IN 등 사용 가능 *\n인라인뷰(FROM절 서브쿼리)는 불가 = \u0026gt; 웬만하면 조인으로 풀어서 해결\n타입 표현 # 문자 : 'HELLO', She''s' 숫자: 1[L|D|F] Boolean: TRUE /FALSE Enum : 패키지명 포함 엔티티 : TYPE(m) = Member (상속관계일때) CASE, COALESCE, NULLIF 사용 가능\n기본 함수 # 기본적으로 공통 제공 : CONCAT,SUBSTRING, TRIM , LOWER/UPPER,LENGTH,LOCATE,SIZE,INDEX\u0026hellip; 웬만한 DB의 함수들은 (DB종속적이긴 하지만) 등록되어 있음 정안되면 NativeQuery() 도 있음\n","date":"8 October 2023","externalUrl":null,"permalink":"/posts/velog/020-jpql-%EA%B8%B0%EB%B3%B82/","section":"Posts","summary":"조인 # 내부(inner) 조인 SELECT m FROM Member m [INNER] JOIN m.team t 외부(left) 조인 SELECT m FROM Member m LEFT [OUTER] JOIN m.team t 세타? (연관관계 없는거) 조인 SELECT count(m) from Member m, Team t where m.username = t.name ON절 # 조인 대상 필터링\n","title":"JPQL-기본2","type":"posts"},{"content":" JPQL이란? # SQL을 추상화한 객체 지향 쿼리 언어\nJPQL은 엔티티 객체를 대상으로 쿼리 SQL은 DB 테이블 대상으로 쿼리 네이티브 SQL # em.createNativeQuery\nJPA가 제공하는 SQL을 직접 사용 특정 DB에 의존적인 기능을 사용할때 (오라클 CONNECT BY 등) 기본문법 # 엔티티와 속성은 대소문자 구분함 (키워드는 구분안함) 엔티티 이름 사용, 별칭(m)은 필수 as는 생략가능\nSelect_문 :: =\nselect_절 from_절 [where_절] [groupby_절] [having_절] [orderby_절]\n+ update_문 :: = update_절 [where_절] + delete_문 :: = delete_절[where_절]\n*COUNT, SUM, AVG *등도 사용가능\nTypeQuery, Query # TypeQuery\u0026lt;X\u0026gt; : 반환타입이 명확할때\n에:em.createQuery(\u0026quot;select m from Member m\u0026quot;, Member.class); Query : 명확하지 않을 때 예: em.createQuery(\u0026quot;select m.username, m.age from Member m\u0026quot;); 결과조회 API # query.getResultList() : 리스트로반환 query.getSingleResult() : 결과가 정확히 하나 (없거나 많으면 에러) 파라미터 바인딩 # = :xxx 를 사용\nMember singleResult = em.createQuery(\u0026#34;select m from Member m where m.username = :username \u0026#34;, Member.class) .setParameter(\u0026#34;username\u0026#34;, \u0026#34;member1\u0026#34;) .getSingleResult(); 프로젝션 # Select 절에 조회할 대상을 지정 (엔티티, 임베디드, 스칼라)\nSelect m from Member m -\u0026gt; 엔티티 (Member) select m.team From Member m -\u0026gt; 엔티티 (Team) select m.address From Member m -\u0026gt; 임베디드 select m.username, m.age From Member m -\u0026gt; 스칼라 Select m.username, m.age From Member m 같이 여러 값을 가져온다면?\nObject[]로 반환되는 값을 사용하는 방법 (거의안쓸듯) new 명령어로 조회 List\u0026lt;MemberDTO\u0026gt; resultList = em.createQuery(\u0026#34;select new jpql.MemberDTO(m.username, m.age) from Member m \u0026#34;, MemberDTO.class) .getResultList(); 패키지가 길어지는 문제가있으나 QueryDsl등에서 해결 가능\n페이징 # 페이징을 두 API로 추상화\nsetFirstResult(int startPosition) : 조회시작위치 setMaxResults(int maxResult) : 조회할 데이터 수 웬만한 DB에 다 맞게 지원됨 ","date":"7 October 2023","externalUrl":null,"permalink":"/posts/velog/021-jpql-%EA%B8%B0%EB%B3%B81/","section":"Posts","summary":"JPQL이란? # SQL을 추상화한 객체 지향 쿼리 언어\nJPQL은 엔티티 객체를 대상으로 쿼리 SQL은 DB 테이블 대상으로 쿼리 네이티브 SQL # em.createNativeQuery\nJPA가 제공하는 SQL을 직접 사용 특정 DB에 의존적인 기능을 사용할때 (오라클 CONNECT BY 등) 기본문법 # 엔티티와 속성은 대소문자 구분함 (키워드는 구분안함) 엔티티 이름 사용, 별칭(m)은 필수 as는 생략가능\nSelect_문 :: =\n","title":"JPQL-기본1","type":"posts"},{"content":"JPA의 값 타입은 Entity와 값 타입이 있다.\nEntity : @Entity로 정의하며, 식별자로 추적 가능 값 타입 : String처럼 단순히 값으로 사용 =\u0026gt; 변경시 완전히 다른 값으로 대체됨(추적불가) equals()사용 (equals(),hashcode() 필요할수도) 임베디드 타입 # @Embeddable,@Embedded\n기본 값 타입입을 모아 만든 복합 값 타입 엔티티가 아닌 그냥 값임 =\u0026gt; 엔티티에 생명주기를 의존 =\u0026gt; 테이블은 변화없음 또다른 임베디드타입이나 Entity를 내부에 가질 수 있음 재사용이 가능하고, 응집도가 높아짐 객체와 테이블을 세밀하게 매핑 가능 @Embedded 는 필요없지만 쓰는걸 권장 한 엔티티에서 같은 타입을 사용한다면, @AttributeOverride()사용 가능\n불변객체 # 임베디드 타입 같은 값 타입은, 여러곳에서 공유되면 위험함 (의도치 않은 변경) 인스턴스를 복사해서 사용해야 함 그러나, 코드 레벨에서 이를 막을 수 있는 방법이 없음 =\u0026gt; 불변 객체 사용 아예 객체 타입을 수정할 수 없게 만듬 =\u0026gt; - 생성자로만 값을 설정, 수정자 ❌ 값 타입 컬랙션 # 값 타입을 하나이상 저장할 때, @ElementCollection,@CollectionTable을 사용해 별도의 테이블 만듬\n지연로딩 CASCDE + 고아 제거 가능 주의!! : 값 타입은 추적이 불가능하기 때문에, 변경시 모든 값을 모두다시저장\n따라서, 실무시에는 일대다 관계를 만들고, CASCADE+ orphanRemoval을 사용 이런 경우에는, 1:N 연관관계의 주인이 1쪽이 될 수도 있음\n","date":"7 October 2023","externalUrl":null,"permalink":"/posts/velog/022-%EA%B0%92-%ED%83%80%EC%9E%85/","section":"Posts","summary":"JPA의 값 타입은 Entity와 값 타입이 있다.\nEntity : @Entity로 정의하며, 식별자로 추적 가능 값 타입 : String처럼 단순히 값으로 사용 =\u003e 변경시 완전히 다른 값으로 대체됨(추적불가) equals()사용 (equals(),hashcode() 필요할수도) 임베디드 타입 # @Embeddable,@Embedded\n기본 값 타입입을 모아 만든 복합 값 타입 엔티티가 아닌 그냥 값임 =\u003e 엔티티에 생명주기를 의존 =\u003e 테이블은 변화없음 또다른 임베디드타입이나 Entity를 내부에 가질 수 있음 재사용이 가능하고, 응집도가 높아짐 객체와 테이블을 세밀하게 매핑 가능 @Embedded 는 필요없지만 쓰는걸 권장 한 엔티티에서 같은 타입을 사용한다면, @AttributeOverride()사용 가능\n","title":"값 타입","type":"posts"},{"content":" CASCADE란? # 연관관계, 지연로딩 등과 아무 관계 ❌ A를 영속 상태로 만들 때, 편리하게 연관된 B도 같이 영속 상태로 만들고 싶을 때\nCascadeType 종류\nALL : 모두 PERSIST : 영속 이외REMOVE MERGE DETACH A와 B의라이프사이클이 같거나, A만 B를 관리할때만 사용\n고아 ? # 부모가 버린 자식 죽이기? 부모 엔티티와 연관관계가 끊어진 자식 엔티티 삭제하기\norphanRemovel=true CascadeType.ALL과 함께 사용하면 부모가 자식의 생명주기를 관리 가능 실무에서는 논리적으로 삭제하는 경우가 많기 때문에, 쓸일이 잘 없을듯? ","date":"7 October 2023","externalUrl":null,"permalink":"/posts/velog/023-cascade-%EA%B3%A0%EC%95%84/","section":"Posts","summary":"CASCADE란? # 연관관계, 지연로딩 등과 아무 관계 ❌ A를 영속 상태로 만들 때, 편리하게 연관된 B도 같이 영속 상태로 만들고 싶을 때\nCascadeType 종류\nALL : 모두 PERSIST : 영속 이외REMOVE MERGE DETACH A와 B의라이프사이클이 같거나, A만 B를 관리할때만 사용\n고아 ? # 부모가 버린 자식 죽이기? 부모 엔티티와 연관관계가 끊어진 자식 엔티티 삭제하기\n","title":"CASCADE, 고아","type":"posts"},{"content":" 프록시 # em.find VS em.getRefrence # em.find() : 실제 DB에서 가져옴 em.getReference() : DB 조회를 미루는 가짜(프록시 extends 실제) 를 가져옴 프록시 객체를 호출하면, 실제 객체의 메소드 호출 프록시의 특징 # 처음 한번만 초기화 초기화할때, 실제 Entity로 바뀌지 않음 =\u0026gt; 프록시를 통해 실제에 접근하는것 원본 엔티티를 상속받음 (비교시 instance of ) 영속성 컨텍스트에 찾는 엔티티가 있으면, em.getReference()는 실제 엔티티 반환 준영속 상태일때, 초기화시 문제 발생 지연로딩 # 지연로딩\nfetch = FetchType.LAZY 처음에 프록시를 가져오고, 실제 사용할때 초기화 주의점 # 가급적 지연 로딩만 사용\n예상치 못한 SQL 발생 N+1문제 @ManyToOne,OneToOne은 디폴트가 즉시로딩 지연로딩을 사용하고, 한방쿼리가 필요할 땐 fetch 조인, 엔티티 그래프 등의 기능 사용 ","date":"6 October 2023","externalUrl":null,"permalink":"/posts/velog/024-%ED%94%84%EB%A1%9D%EC%8B%9C-%EC%A7%80%EC%97%B0%EB%A1%9C%EB%94%A9/","section":"Posts","summary":"프록시 # em.find VS em.getRefrence # em.find() : 실제 DB에서 가져옴 em.getReference() : DB 조회를 미루는 가짜(프록시 extends 실제) 를 가져옴 프록시 객체를 호출하면, 실제 객체의 메소드 호출 프록시의 특징 # 처음 한번만 초기화 초기화할때, 실제 Entity로 바뀌지 않음 =\u003e 프록시를 통해 실제에 접근하는것 원본 엔티티를 상속받음 (비교시 instance of ) 영속성 컨텍스트에 찾는 엔티티가 있으면, em.getReference()는 실제 엔티티 반환 준영속 상태일때, 초기화시 문제 발생 지연로딩 # 지연로딩\n","title":"프록시, 지연로딩","type":"posts"},{"content":" 상속관계 매핑이란? # 객체 지향 프로그래밍에서의 상속 개념을 데이터베이스 스키마에 매핑 (객체) 상속구조 \u0026lt;=\u0026gt; (DB)슈퍼-서브타입\n구현방법\n각각 테이블로 (조인) 통합 테이블로 (단일 테이블) 서브타입 테이블로 (구현 클래스마다 테이블로) @Inheritance(strategy = ? ) 조인 전략 # InheritanceType.JOINED\n정석적인 방법 단일테이블 전략 # InheritanceType.SINGLE_TABLE\n한테이블에 다 때려넣기 클래스마다 테이블 전략 추천 X # @MappedSuperClass # 여러 엔티티에서 공통으로 적용하는 정보를 모을 때 사용\n상속관계 매핑이 아니라, 단순히 상속받는 클래스에 매핑 정보만 제공 엔티티나 테이블이 아니기 때문에 조회, 검색 불가 직접 생성할일 ❌ =\u0026gt; 추상 클래스 권장 Entity 클래스는, @Entity나 @MappedSuperclass 밖에 상속받지 못함\n","date":"6 October 2023","externalUrl":null,"permalink":"/posts/velog/025-%EC%83%81%EC%86%8D%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91/","section":"Posts","summary":"상속관계 매핑이란? # 객체 지향 프로그래밍에서의 상속 개념을 데이터베이스 스키마에 매핑 (객체) 상속구조 \u003c=\u003e (DB)슈퍼-서브타입\n구현방법\n각각 테이블로 (조인) 통합 테이블로 (단일 테이블) 서브타입 테이블로 (구현 클래스마다 테이블로) @Inheritance(strategy = ? ) 조인 전략 # InheritanceType.JOINED\n정석적인 방법 ","title":"상속관계 매핑","type":"posts"},{"content":" 연관관계 매핑이 필요한 이유 # DB는 기본적으로, 연관관계의 방향이 없다. (FK로 join걸면 다 해결됨) 하지만 객체는 참조를 사용해서 관계를 맺는다. 따라서, 객체지향적인 방법으로 데이터를 조작하려면 연관관계 매핑이 필수적이다.\n단방향 연관관계 # 객체의 참조 와 FK를 매칭 @Entity public class Member{ @Id @GeneratedValue private Long id; ... @ManyToOne @JoinColumn(name = \u0026#34;TEEAM_ID\u0026#34;) private Team team; } @ㄷEntity public Class Team{ ... @OneToManty(mappedBy = \u0026#34;team\u0026#34;) private List\u0026lt;Member\u0026gt; members = new ArrayList\u0026lt;\u0026gt;(); //관례적으로 넣어줌 } 양방향 연관관계 # 연관관계의 주인이란? # 테이블은 FK 하나로 연관관계를 관리하지만, 객체는 양쪽에 참조 객체가 있다. 따라서, 두 관계의 주인(OWNER)를 지정해줘야 한다.\n연관관계의 주인만이 FK를 관리하고, 다른쪽은 읽기만 가능하다. 주인은 @JoinColumn으로 외래키 속성 설정 주인이 아닌 쪽에서 mappedBy 를 이용해 주인을 지정한다. 그러면 어느 쪽을 주인으로 해야 할까?\nFK가 있는쪽 (여기선 Member.team) 을 주인으로 정하자. 이건 단지 비즈니스상의 중요성과는 아무 관계가 업는, FK를 관리할 쪽을 정하는거임.\n주인에 항상 값을 넣어줘야 함 # // 틀린 예시 Team team = new Team(); em.persist(team); Member member = new Member(); team.getMembers.add(member); //역뱡향에서 값을 넘 em.persist(member) ; // member.TEAM_ID = NULL // 올바른 예시 Team team = new Team(); em.persist(team); Member member = new Member(); //team.getMembers.add(member); //주인이 아닌 곳에서 값을 넘 memeber.setTeam(team) // 주인이 값을 넘 em.persist(member) ; // member.Team_id값이 들어감 주인 쪽에도 값을 넣어줘야 하는 이유 # em.flush(), em.clear()가 호출되지 않는 상태에서, 엔티티는 1차 캐시에 있음 -\u0026gt; 이 상황에서는 member.team은 설정되어 있어도, team.members는 Null임 -\u0026gt; 따라서, 이왕이면 Team에도 Member 값이 세팅되어 있어야 함\n주의사항 # @ToString, Json 생성 라이브러리 등을 사용할때, 무한루프가 돌 수 있음\n///member.toString() @Override public String toString() { return \u0026#34;Member{\u0026#34; + \u0026#34;id=\u0026#34; + id + \u0026#34;, username=\u0026#39;\u0026#34; + username + \u0026#39;\\\u0026#39;\u0026#39; + \u0026#34;, team=\u0026#34; + team + // team.toString() 호출 \u0026#39;}\u0026#39;;} @Override public String toString() { return \u0026#34;Team{\u0026#34; + \u0026#34;id=\u0026#34; + id + \u0026#34;, name=\u0026#39;\u0026#34; + name + \u0026#39;\\\u0026#39;\u0026#39; + \u0026#34;, members=\u0026#34; + members + // (member1.toSTring,2.toString ...) \u0026#39;}\u0026#39;; } System.out.println(team.getMember()) ==\u0026gt; StackOverflowError 해결법 # Entity 자체를 내보내거나, 받아오지 말자 (DTO 사용) @ToString 등을 사용할땐 조심하자 정리 # 단방향 매핑만 해도 이미 연관관계 매핑은 끝난거임 양방향이라는건, 반대 방향으로 조회하는 기능이 추가된 것 뿐임 따라서, 단방향 매핑만 잘 해놓고 필요할 때 추가하면 됨 편의 메서드 이용 public void addMember(Member member) { member.setTeam(this); members.add(member); } ","date":"4 October 2023","externalUrl":null,"permalink":"/posts/velog/026-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91/","section":"Posts","summary":"연관관계 매핑이 필요한 이유 # DB는 기본적으로, 연관관계의 방향이 없다. (FK로 join걸면 다 해결됨) 하지만 객체는 참조를 사용해서 관계를 맺는다. 따라서, 객체지향적인 방법으로 데이터를 조작하려면 연관관계 매핑이 필수적이다.\n단방향 연관관계 # 객체의 참조 와 FK를 매칭 ","title":"연관관계 매핑","type":"posts"},{"content":"직접할당 :@Id만 사용 자동생성 : @GeneratedValue\n@GeneratedValue(strategy= ? ) # GenerationTpye.Identity: 전략을 DB에 위임 # DB에 들어간 후에, ID값을 알 수 있음 =\u0026gt; persist() 후 바로 쿼리 날라감 AUTO_INCREMENT 생각 GenerationTpye.Sequence # DB의 Sequence 사용 persist() 후 Sequence값만 가져왔다가, 끝날때 날림 allocationSize 등을 통해 성능향상 가능 @Entity @SequenceGenerator( name = “MEMBER_SEQ_GENERATOR\u0026#34;, sequenceName=“MEMBER_SEQ\u0026#34;,//시퀀스이름 initialValue = 1, allocationSize = 50) //한번에 50개 가져오고,그동안은 메모리에서 가져옴 public class Member { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = \u0026#34;MEMBER_SEQ_GENERATOR\u0026#34;) private Long id; GenerationType.TABLE # Sequence 역활을 하는 테이블 사용 정략 Sequence 없는 DB도 가능하지만, 성능 이슈 GenerationType.UUID # 강의에는 없는데 추가된거같음 말그대로 UUID 사용 권장 식별자 전략 # Long + 대체키 + 생성전략\n","date":"3 October 2023","externalUrl":null,"permalink":"/posts/velog/027-%EC%97%94%ED%8B%B0%ED%8B%B0-%EB%A7%A4%ED%95%91-%EA%B8%B0%EB%B3%B8%ED%82%A4/","section":"Posts","summary":"직접할당 :@Id만 사용 자동생성 : @GeneratedValue\n@GeneratedValue(strategy= ? ) # GenerationTpye.Identity: 전략을 DB에 위임 # DB에 들어간 후에, ID값을 알 수 있음 =\u003e persist() 후 바로 쿼리 날라감 AUTO_INCREMENT 생각 GenerationTpye.Sequence # DB의 Sequence 사용 persist() 후 Sequence값만 가져왔다가, 끝날때 날림 allocationSize 등을 통해 성능향상 가능 @Entity @SequenceGenerator( name = “MEMBER_SEQ_GENERATOR\", sequenceName=“MEMBER_SEQ\",//시퀀스이름 initialValue = 1, allocationSize = 50) //한번에 50개 가져오고,그동안은 메모리에서 가져옴 public class Member { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = \"MEMBER_SEQ_GENERATOR\") private Long id; GenerationType.TABLE # Sequence 역활을 하는 테이블 사용 정략 Sequence 없는 DB도 가능하지만, 성능 이슈 GenerationType.UUID # 강의에는 없는데 추가된거같음 말그대로 UUID 사용 권장 식별자 전략 # Long + 대체키 + 생성전략\n","title":"엔티티 매핑-기본키","type":"posts"},{"content":" 객체와 테이블 매핑 # @Entity # @Entity가 붙은 클래스는 JPA가 관리\n주의 : 기본생성자(public | protected) 필요\n@Table # 엔티티와 매핑할 테이블 지정\n속성\nname : 테이블명 catalog : DB 카탈로그 schema : 스키마 unique 스키마 자동 생성 # JPA에서는, 로딩 시점에 DDL 자동 생성 지원 운영에서 쓸때 조심 옵션\ncreate : 시작할때 다시만들기 create-drop : 종료시 drop update : 변경분만 반영 `ALTER TABLE validate : 정상 매핑인지 확인 =\u0026gt; 없으면 persistenceException 항상 주의해서 사용할것 !! =\u0026gt; 운영에선 validate,none 만 사용하자\n필드와 컬럼 매핑 # @Column: 컬럼 @Temporal : 날짜타입 @Enumerated : enum @Lob : BLOB,CLOB @Transient 매핑 ❌ @Column # name: 컬럼이름 (default:필드이름) insertable/updatable : 업데이트 가능 여부 nullable(DDL) : notnull unique(DDL) : unique (컬럼에선 잘안쓰고 @Table에서 씀) columnDefinition(DDL) : 컬럼정보를 직접 줌 length(DDL) : 길이 (varchar) precision,scale(DDL) : 정밀한 소수? @Enumerated # 무조건 EnumType.String 사용할것\n@TemporalType # 시간관련 1.8부터 필요없음\nLocalDate : Date타입 LocalDateTime : timestamp @Lob # 문자 =\u0026gt; Clob 나머지 =\u0026gt; Blob\nTransient # 필드 매핑 ❌ , DB 저장, 조회❌ 메모리상에서만 임시로 사용하고플때\n","date":"3 October 2023","externalUrl":null,"permalink":"/posts/velog/028-%EC%97%94%ED%8B%B0%ED%8B%B0-%EB%A7%A4%ED%95%91/","section":"Posts","summary":"객체와 테이블 매핑 # @Entity # @Entity가 붙은 클래스는 JPA가 관리\n주의 : 기본생성자(public | protected) 필요\n@Table # 엔티티와 매핑할 테이블 지정\n속성\nname : 테이블명 catalog : DB 카탈로그 schema : 스키마 unique 스키마 자동 생성 # JPA에서는, 로딩 시점에 DDL 자동 생성 지원 운영에서 쓸때 조심 ","title":"엔티티 매핑","type":"posts"},{"content":" JPA에서 가장 중요한 2가지 # ORM, 영속성 컨텍스트 영속성 컨텍스트란? # Entity를 영구 저장하는 환경 엔티티 객체의 생명주기 관리, 캐싱, 트랜잭션 처리 등을 담당하는 핵심 컴포넌트\n이점 # 1차캐시, 동일성보장, 변경 감지(dirty checking), 지연 로딩(lazy loading) 등 엔티티의 생명주기 # 비영속new (전혀 관계가 없음) 영속managed (관리되는 상태) 준영속detached (저장되었다가 분리된 상태 ) 삭제removed (삭제된 상태) flush # 저장\nem.persist(entity) flush\nem.flush()가 호출되면 , SQL 저장소에 있던 SQL 들을 DB로 보냄 커밋시 자동호출\nflush()를 호출해서, 즉시 보낼수도 있음 기본 설정으로는, 커밋 또는 JPQL이 실행되면 flush()가 실행됨 영속성 컨텍스트를 비우는게 아니라, 변경 내용을 데이터베이스에 동기화하는것 Dirty Checking # 1차 캐시에 있는 스냅샷과 엔티티를 배교해서, 변경사항이 있으면 flush()할때 DB에 보냄 ","date":"3 October 2023","externalUrl":null,"permalink":"/posts/velog/029-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8/","section":"Posts","summary":"JPA에서 가장 중요한 2가지 # ORM, 영속성 컨텍스트 영속성 컨텍스트란? # Entity를 영구 저장하는 환경 엔티티 객체의 생명주기 관리, 캐싱, 트랜잭션 처리 등을 담당하는 핵심 컴포넌트\n이점 # 1차캐시, 동일성보장, 변경 감지(dirty checking), 지연 로딩(lazy loading) 등 엔티티의 생명주기 # 비영속new (전혀 관계가 없음) 영속managed (관리되는 상태) 준영속detached (저장되었다가 분리된 상태 ) 삭제removed (삭제된 상태) flush # 저장\n","title":"영속성 컨텍스트","type":"posts"},{"content":" INIT # 추석할인으로 김영한님 JPA 강의 득템!!\n최소한 하루에 한 챕터씩은 공부할 예정이다.\n","date":"2 October 2023","externalUrl":null,"permalink":"/posts/velog/030-jpa-%EA%B0%95%EC%9D%98-%EC%8B%9C%EC%9E%91/","section":"Posts","summary":"INIT # 추석할인으로 김영한님 JPA 강의 득템!!\n최소한 하루에 한 챕터씩은 공부할 예정이다.\n","title":"JPA 강의 시작","type":"posts"},{"content":"원래 웹 계층 개발이지만, 딱히 쓸 게 없다\n준영속 엔티티 수정 # 준영속 엔티티란 \u0026lt;- 영속성 컨텍스트가 더이상 관리하지 않는 엔티티\nJPA 엔티티는 변경을 자동으로 감지해서 UPDATE를 날라지만, 준영속 엔티티는 (예: DB에서 꺼내지 않고, 임의로 만듬)UPDATE를 날리지 않음 따라서, 업데이트를 하려면 아래의 두가지 방법을 쓸 수 있다. 근데 무조건 첫번째를 쓰자 변경감지 기능 (무조건 이거씀) # DB에서 다시 엔티티를 조회하고, 그 엔티티를 수정하는 방법\n트랜잭션 커밋 시점에 변경이 감지되어, UPDATE문이 날라감\n☠️ 병합 ☠️ # 알아만 두자\nmerge()를 실행해서 덮어씌움\n조회-\u0026gt; 교체 =\u0026gt; 커밋시점에 UPDATE문 날림\n왜 ? # 병합(merge)시에는, 모든 필드를 덮어씌우고, 없다면 NULL로 업데이트한다.\n모든 화면에서 항상 모든 필드값을 유지하기 싫으면, 쓰지말자\n이전에 Mybatis \u0026lt;if test=\u0026quot;someCondition != null and someCondition != ''\u0026quot;\u0026gt; 없이 UPDATE를 날렸다가, 필드가 NULL로 초기화된 경험이 생각난다.\n","date":"30 September 2023","externalUrl":null,"permalink":"/posts/velog/031-4.-%EB%B3%80%EA%B2%BD-%EA%B0%90%EC%A7%80%EC%99%80-%EB%B3%91%ED%95%A9/","section":"Posts","summary":"원래 웹 계층 개발이지만, 딱히 쓸 게 없다\n준영속 엔티티 수정 # 준영속 엔티티란 \u003c- 영속성 컨텍스트가 더이상 관리하지 않는 엔티티\nJPA 엔티티는 변경을 자동으로 감지해서 UPDATE를 날라지만, 준영속 엔티티는 (예: DB에서 꺼내지 않고, 임의로 만듬)UPDATE를 날리지 않음 따라서, 업데이트를 하려면 아래의 두가지 방법을 쓸 수 있다. 근데 무조건 첫번째를 쓰자 변경감지 기능 (무조건 이거씀) # DB에서 다시 엔티티를 조회하고, 그 엔티티를 수정하는 방법\n","title":"4. 변경 감지와 병합","type":"posts"},{"content":"이 강의에서는 따로 적을게 별로없다. 그냥 개발하는 내용이 다임\n테스트 환경설정 # 테스트를 할때, 기본적으로 /test/resources/application.yml에 있는 설정 파일을 먼져 읽는다. (없으면 기본 파일읽음) 따라서, 이곳에 테스트에서의 설정을 생성할 수 있다.\n또한, 스프링부트는 아무 설정이 안되어있는 경우, 기본적으로 메모리DB(H2)로 설정을 한다. 따라서, 빈 application.yml을 만들기만 하면, 기본적으로 메모리DB가 설정된다.\n동적쿼리 # JPA에서 기본적인 동적쿼리는 지원하지만, 코드가 진짜 끔찍해진다. 따라서 QueryDsl과의 동작이 잘 어울린다.\nJPA # 혐주의 ```java // 하이라이트는 노가다로 해야되는듯? js도안먹히고 public List findAllByString(OrderSearch orderSearch) { //language=JPAQL String jpql = \"select o From Order o join o.member m\"; boolean isFirstCondition = true; //주문 상태 검색 if (orderSearch.getOrderStatus() != null) { if (isFirstCondition) { jpql += \" where\"; isFirstCondition = false; } else { jpql += \" and\"; } jpql += \" o.status = :status\"; } //회원 이름 검색 if (StringUtils.hasText(orderSearch.getMemberName())) { if (isFirstCondition) { jpql += \" where\"; isFirstCondition = false; } else { jpql += \" and\"; } jpql += \" m.name like :name\"; } TypedQuery query = em.createQuery(jpql, Order.class) .setMaxResults(1000); //최대 1000건 if (orderSearch.getOrderStatus() != null) { query = query.setParameter(\"status\", orderSearch.getOrderStatus()); } if (StringUtils.hasText(orderSearch.getMemberName())) { query = query.setParameter(\"name\", orderSearch.getMemberName()); } return query.getResultList(); } ``` QueryDsl # 훨씬 깔끔\npublic List\u0026lt;Order\u0026gt; findAll(OrderSearch orderSearch){ String memberName = orderSearch.getMemberName(); OrderStatus status = orderSearch.getOrderStatus(); return query.select(order) eqOrderStatus(status)) .where(eqOrderStatus(status)) .fetch(); } private BooleanExpression eqMemberName(String memberName){ return StringUtils.hasText(memberName) ? order.member.name.eq(memberName) : null; } private BooleanExpression eqOrderStatus(OrderStatus orderStatus){ return orderStatus != null ? order.status.eq(orderStatus) : null; } ","date":"29 September 2023","externalUrl":null,"permalink":"/posts/velog/032-3.-%EB%8F%84%EB%A9%94%EC%9D%B8-%EA%B0%9C%EB%B0%9C/","section":"Posts","summary":"이 강의에서는 따로 적을게 별로없다. 그냥 개발하는 내용이 다임\n테스트 환경설정 # 테스트를 할때, 기본적으로 /test/resources/application.yml에 있는 설정 파일을 먼져 읽는다. (없으면 기본 파일읽음) 따라서, 이곳에 테스트에서의 설정을 생성할 수 있다.\n또한, 스프링부트는 아무 설정이 안되어있는 경우, 기본적으로 메모리DB(H2)로 설정을 한다. 따라서, 빈 application.yml을 만들기만 하면, 기본적으로 메모리DB가 설정된다.\n동적쿼리 # JPA에서 기본적인 동적쿼리는 지원하지만, 코드가 진짜 끔찍해진다. 따라서 QueryDsl과의 동작이 잘 어울린다.\n","title":"3. 도메인 개발","type":"posts"},{"content":"두 명령어의 차이점을 모른 채 exec -it 만 쓰고 있었는데, 차이점을 알게 됨\nexec # 추가 명령을 실행! 즉, 실행중인건 건드리지 않고, 새로운 프로세스를 실행 (대부분 쉘) -it는 interactive mode, terminal 의 약자 attach # 실행중인 컨테이너의 stdio 에 연걸 새로운 프로세스를 실행 ❌❌, 실행 중인 메인 프로세스에 연결 EX # Ubuntu 같은 컨테이너는, Docker run 명령어만을 실행했을때, 메인인 /bin/bash가 아무런 입력을 받지 못해서 죽어버림 따라서 docker run -dit ubuntu 으로, interactive mode로 계속 실행되게 할 것이 강제됨\n이 상태에서, attach를 사용하면, 실행 중인 /bin/bash에 연결이 가능함\n반대로, mariadb 같은 컨테이너는, 계속 DB가 돌고 있는 상태임 (mariadbd) 따라서, run 할때-dit 옵션이 필요가 없음 (대화형모드를 제공조차 안함)\n이 상태에서는, exec -it 등을 사용해 새 프로세스를 만들어서 접근\n","date":"17 September 2023","externalUrl":null,"permalink":"/posts/velog/033-docker-attach-exec/","section":"Posts","summary":"두 명령어의 차이점을 모른 채 exec -it 만 쓰고 있었는데, 차이점을 알게 됨\nexec # 추가 명령을 실행! 즉, 실행중인건 건드리지 않고, 새로운 프로세스를 실행 (대부분 쉘) -it는 interactive mode, terminal 의 약자 attach # 실행중인 컨테이너의 stdio 에 연걸 새로운 프로세스를 실행 ❌❌, 실행 중인 메인 프로세스에 연결 EX # Ubuntu 같은 컨테이너는, Docker run 명령어만을 실행했을때, 메인인 /bin/bash가 아무런 입력을 받지 못해서 죽어버림 따라서 docker run -dit ubuntu 으로, interactive mode로 계속 실행되게 할 것이 강제됨\n","title":"docker attach, exec ","type":"posts"},{"content":" 이미지 다운로드 # \u0026gt; docker pull redis 실행 # ❯ docker run --name redis -d -p 6379:6379 redis 접속 # \u0026gt; docker exec -it redis /bin/bash root@c000d28c9b96:/data# redis-cli ","date":"14 August 2023","externalUrl":null,"permalink":"/posts/velog/034-docker-redis-%EC%84%A4%EC%B9%98/","section":"Posts","summary":"이미지 다운로드 # \u003e docker pull redis 실행 # ❯ docker run --name redis -d -p 6379:6379 redis 접속 # \u003e docker exec -it redis /bin/bash root@c000d28c9b96:/data# redis-cli","title":"docker redis 설치","type":"posts"},{"content":"","date":"14 August 2023","externalUrl":null,"permalink":"/tags/redis/","section":"Tags","summary":"","title":"Redis","type":"tags"},{"content":"","date":"30 July 2023","externalUrl":null,"permalink":"/tags/jwt/","section":"Tags","summary":"","title":"JWT","type":"tags"},{"content":"이전글\n구조 # .으로 구분된 세 부분\nHeader Payload (정보) Signature (서명) Header # 서명 알고리즘, 토큰 유형 ex)\n{ \u0026#34;alg\u0026#34;: \u0026#34;HS256\u0026#34;, \u0026#34;typ\u0026#34;: \u0026#34;JWT\u0026#34; } Base64Url로 인코딩됨\nPayload # 클래임 (정보의 단위)\n종류 등록된 클레임 (Registered Claims) : 이미 정해저 있는, JWT에 공통적으로 포함되는 클레임 필수❌ 공개 클레임 (Public Claims) : 사용저 정의 클레임 비공개 클레임 (Private Claims) : 등록되지 않음, 두 개체가 합의해서 사용 { \u0026#34;sub\u0026#34;: \u0026#34;1234567890\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;John Doe\u0026#34;, \u0026#34;admin\u0026#34;: true } 해더와 같이 Base64Url로 인코딩됨\n서명 # 헤더와 페이로드를 Base64Url로 인코딩 인코딩된 헤더와 페이로드를 연결하고, 이를 비밀키 또는 개인키와 함께 해시 알고리즘을 사용 이 암호화된 값을 다시 Base64Url로 인코딩하여 서명 생성 ex\nHMACSHA256( base64UrlEncode(header) + \u0026#34;.\u0026#34; + base64UrlEncode(payload), secret) JWT는 암호화가 아니라 서명에 목적이 있다. BASE64로 인코딩하기 때문에, 토큰을 가지고 있다면 누구나 쉽게 읽을 수 있다. 따라서 민감한 정보는 ❌❌\n참고 사이트\n","date":"30 July 2023","externalUrl":null,"permalink":"/posts/velog/035-jwt-%EA%B5%AC%EC%A1%B0/","section":"Posts","summary":"이전글\n구조 # .으로 구분된 세 부분\nHeader Payload (정보) Signature (서명) Header # 서명 알고리즘, 토큰 유형 ex)\n{ \"alg\": \"HS256\", \"typ\": \"JWT\" } Base64Url로 인코딩됨\nPayload # 클래임 (정보의 단위)\n종류 등록된 클레임 (Registered Claims) : 이미 정해저 있는, JWT에 공통적으로 포함되는 클레임 필수❌ 공개 클레임 (Public Claims) : 사용저 정의 클레임 비공개 클레임 (Private Claims) : 등록되지 않음, 두 개체가 합의해서 사용 { \"sub\": \"1234567890\", \"name\": \"John Doe\", \"admin\": true } 해더와 같이 Base64Url로 인코딩됨\n","title":"JWT  구조","type":"posts"},{"content":"JWT 이해에 필요한 배경지식들 정리\n기존 세션 방식의 문제점 # 로드 밸런싱 (여러대의 서버가 나누어 운영)에서, 세션 처리를 어떻게 해야될지 난감 # EX)\nA,B,C라는 서버 3대가 요청을 처리중 사용자가 A서버에 로그인을 처리해서 세션쿠키 발급받음 갑자기 A서버에 트래픽이 몰려서, B서버에서 다음 작업을 수행 B서버는 사용자에 대한 세션이 없음 또한, 서버의 메모리를 소모하므로, 사용자가 많아지면 \u0026hellip;.\n** 이래서 in-memory DB (redis등) 에서 세션을 저장하고. 모든 서버가 이에 접근하게 함 **\nCIA # 미국 중앙정보국 Confidentiality(기밀성), Integrity(무결성), Availability(가용성)\nConfidentiality(기밀성) : 정보는 인가된 사용자만 접근할 수 있어야 함 Integrity(무결성) : 정보가 전송 중 변경되지 않았다는 것을 보장 Availability(가용성) : 정보가 필요할 때, 항상 접근가능해야함 RSA # 공개 키 암호화 알고리즘 public key, private key\nA==\u0026gt;B\nA는 B의 공개키로 암호화하여 데이터 전송 B는 B의 개인키로 복호화 공개키로 잠그면, 개인키로 열 수 있음 (암호화) 개인키로 잠그면, 공개키로 열 수 있음 (전자서명) 디지털 서명 # 위의 송신과정에 더해\nA는 메시지의 해시값을 자신의 개인키로 암호화해서 디지털 서명을 생성하고, 이것을 메시지와 함께 전송 B는 A의 공개키를 사용해서 디지털 서명을 복호화하고, 이렇게 얻은 해시값을 메시지의 해시값과 비교 일치한다면, B는 송신자가 A이고, 중간에 변경되지 않았음을 확신 RFC # Request For Comments\n인터넷과 관련된 표준과 프로토콜을 설명하고 정의하는 공식 문서 집합 JWT는 RFC 7519에 정의되어 있음\n다음 (진짜 JWT에 대해서)\n","date":"30 July 2023","externalUrl":null,"permalink":"/posts/velog/036-jwt-%EB%B0%B0%EA%B2%BD%EC%A7%80%EC%8B%9D/","section":"Posts","summary":"JWT 이해에 필요한 배경지식들 정리\n기존 세션 방식의 문제점 # 로드 밸런싱 (여러대의 서버가 나누어 운영)에서, 세션 처리를 어떻게 해야될지 난감 # EX)\nA,B,C라는 서버 3대가 요청을 처리중 사용자가 A서버에 로그인을 처리해서 세션쿠키 발급받음 갑자기 A서버에 트래픽이 몰려서, B서버에서 다음 작업을 수행 B서버는 사용자에 대한 세션이 없음 또한, 서버의 메모리를 소모하므로, 사용자가 많아지면 ….\n","title":"JWT 배경지식","type":"posts"},{"content":" 이미지 다운로드\u0026amp;확인 # docker pull mysql [:버전] docker images 컨테이너 생성 # docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=0000 -e TZ=Asia/Seoul mysql --character-set-server=utf8 --collation-server=utf8_general_ci --lower_case_table_names=1 # --name [컨테이너이름] -p [포트연결] # -d는 백그라운드에서 돌리겠다는 의미 # -e는 환경변수 설치 # --lower_case_table_names=1 안하면 대소문자 구분 -- 개불편함 시작 # docker start/stop/restart[컨테이너명] # 참고로, restart는 재부팅하는거임 접속 # docker exec -it mysql bash 이후 # ###vim\n먼져, vim을 설치하기 위해서 리눅스 버전확인\ncat /etc/*-release 내경우는 Oracle Linux 8이였다. 구글링해서, 맞는 설치방법을 알아본다.\nmicrodnf install -y vim etc # mariadb docker-compse\nversion: \u0026#39;3.7\u0026#39; services: mariadb: image: mariadb container_name: mariadb ports: - 3306:3306 restart: always volumes: - mariadb:/var/lib/mysql environment: - MARIADB_ROOT_PASSWORD=0000 - MYSQLD_LOWER_CASE_TABLE_NAMES=1 volumes: mariadb: ","date":"21 June 2023","externalUrl":null,"permalink":"/posts/velog/037-docker-mysql-%EC%84%A4%EC%B9%98/","section":"Posts","summary":"이미지 다운로드\u0026확인 # docker pull mysql [:버전] docker images 컨테이너 생성 # docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=0000 -e TZ=Asia/Seoul mysql --character-set-server=utf8 --collation-server=utf8_general_ci --lower_case_table_names=1 # --name [컨테이너이름] -p [포트연결] # -d는 백그라운드에서 돌리겠다는 의미 # -e는 환경변수 설치 # --lower_case_table_names=1 안하면 대소문자 구분 -- 개불편함 시작 # docker start/stop/restart[컨테이너명] # 참고로, restart는 재부팅하는거임 접속 # docker exec -it mysql bash 이후 # ###vim\n","title":"docker mysql 설치","type":"posts"},{"content":"","date":"21 June 2023","externalUrl":null,"permalink":"/tags/mysql/","section":"Tags","summary":"","title":"Mysql","type":"tags"},{"content":"","date":"28 March 2023","externalUrl":null,"permalink":"/tags/http/","section":"Tags","summary":"","title":"Http","type":"tags"},{"content":"HTTP Header1에서 이어짐\n검증 헤더1 # Last-Modified, if-modified-since\n캐시 유효 시간이 초과한 경우, 서버의 데이터는 변경되었을 수도 있다.\n캐시가 만료 후에도 데이터가 변경되지 않았다면, 저장된 캐시 재사용 가능 ❗️단, 데이터가 같다는 사실을 확인할 방법 이 필요 Last-Modified 헤더를 통해서 이를 확인\n동작방식\n서버에서 Last-Modified 헤더를 전송 캐시 시간 초과시, 브라우저가 if-modified-since를 서버에 전송 서버에서 판단 변경안됬으면 304 Not Modified 응답(데이터전송 ❌) =\u0026gt; 캐시 정보 갱신 \u0026amp; 응답 결과 재사용 변경됬으면 200 모든 데이터 포함 검증 헤더 2 # Etag, If-None-Match\nEtag : 캐시의 데이터에 임의의 버전 이름을 달아둠 데이터가 변경대면 Etag를 변경 (Hash) If-None-Match로 Etag 보내서 같으면 304 Not Modified , 다르면 다시 받기(200) 캐시 제어 로직을 서버에서 완전히 관리 헤더 정리 # Cache-Control : 캐시 제어\nmax-age : 유효시간(초단위) no-cache : 데이터는 캐시하지만, 항상 검증❗️ no-store : 저장하면 안됨 (메모리에서 사용하고 바로 삭제) Expires\t: 캐시 만료일 지정 (Cache-control:max-age 권장, 중복시 무시됨)\n검증헤더 Etag,Last-Modified\n조건부 요청\nIf-Match,If-None-Match :Etag사용시 If-Modified-Since, If-Unmodified-Since : Last-Modified 사용시 프록시 캐시 # 기타 캐시 지시어 # Cache-Control : public : 응답이 public 캐시에 저장되어도 됨 Cache-Control : private : 응답이 해당 사용자만을 위한 것, private 캐시에 저장 (default) Cache-Control : s-maxage : 프록시 캐시에만 저장되는 max-age Age :60(Http 헤더) : 오리진 서버에서 응답 후, 프록시 캐시 내에 머문 시간(초) 캐시 무효화 # Cache-Control : no-cache, no-store, must-revalidate\nmust-revalidate 캐시 만료후 최초 조회시, 원 서버에 검증해야됨 원 서버 접근 실패시, 반드시 오류504 매우 중요한 정보일 경우, 무조건 원 서버의 데이터를 가져오기 위하여 ","date":"28 March 2023","externalUrl":null,"permalink":"/posts/velog/038-http-header2/","section":"Posts","summary":"HTTP Header1에서 이어짐\n검증 헤더1 # Last-Modified, if-modified-since\n캐시 유효 시간이 초과한 경우, 서버의 데이터는 변경되었을 수도 있다.\n캐시가 만료 후에도 데이터가 변경되지 않았다면, 저장된 캐시 재사용 가능 ❗️단, 데이터가 같다는 사실을 확인할 방법 이 필요 Last-Modified 헤더를 통해서 이를 확인\n동작방식\n서버에서 Last-Modified 헤더를 전송 캐시 시간 초과시, 브라우저가 if-modified-since를 서버에 전송 서버에서 판단 변경안됬으면 304 Not Modified 응답(데이터전송 ❌) =\u003e 캐시 정보 갱신 \u0026 응답 결과 재사용 변경됬으면 200 모든 데이터 포함 검증 헤더 2 # Etag, If-None-Match\n","title":"HTTP Header2","type":"posts"},{"content":" Header란? # Http 전송에 필요한 모든 부가정보 엄청많음 필요시 임의의 헤더 추가 가능\nMessage body로 데이터를 전달 (payload) header는 데이터를 해석할 수 있는 정보 제공 (유형, 길이 \u0026hellip;.) ** 표현 = 표현 메타데이터 + 표현 데이터 **\n표현 (Representation) # 표현 헤더는 전송, 응답 둘다 사용함\nREST API의 RE가 바로 표현\nContent-Type : 표현 데이터의 형식\n(text/html;charset=utf-8 , application/json \u0026hellip;..) Content-Encoding : 표현 데이터의 압축 방식\n전달하는 곳에서 압축 후 헤더 추가 =\u0026gt; 읽는 쪽에서 헤더정보로 압축 해제 (gzip \u0026hellip;.) Content-Language : 표현 데이터의 자연 언어\n(ko,en \u0026hellip;..) Content-Length : 표현 데이터의 길이\n바이트 단위 Transfer-Encoding 사용시 Content-Length 사용 ❌ Transfer-encoding 협상 # 클라이언트가 선호하는 표현으로 달라고 요청 =\u0026gt; 우선순위에 맞춰서 서버가 표현 (못줄수도)\n요쳥시에만 사용\nAccept : 미디어 타입 Accept-Charset : 문자 인코딩 Accept-Encoding : 압축 인코딩 Accept-Language : 자연 언어 우선순위 # ex)\naccept-language: ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7\n없으면 1, 높을수록 좋은거\n구체적일수록 좋은거\n전송 방식 # 단순 : Content-Length를 줌 압축 : Content-Encoding으로 압축 정보 추가 분할 : Transfer-Encoding : chunked 덩어리로 쪼개서 보냄 (Content-length ❌) 각 덩어리마다 있음 범위 : Content-Range로 범위 지정해줌 일반 정보 # From\n요청 유저 에이전트의 이메일 정보 (잘 사용 x) Referer ❗️ 진짜많이씀\n요청 이전 웹 페이지 주소 (어디서들어왔는가?) 유입 경로 분석 가능 referrer의 오타임 User-Agent\n요청 클라이언트의 애플리케이션 정보(웹 브라우저 등) 통계정보 Server\n응답 요청을 처리하는 Origin 서버의 소프트웨어 정보 Date\n응답 메시지가 발생한 날짜와 시간 특별한 정보 # HOST ❗️\n요청 필수값임 하나의 서버(IP)가 여러 도메인을 처리할 때 구분 Location\n응답 페이지 리다이렉션 3xx응답의 결과에 Location이 있으면, 자동 이동 201도 쓸 수있는데, 요청에 의해 생성된 리소스 URI Allow\n405(Method not allow) 에서 응답에 포함 허용 가능한 HTTP 메서드 Retry-After\n503(Service Unvaliable) 에서 응답에 포함 유저가 다음 요청을 하기까지 기다려야 하는 시간 (날짜|초 표기) 인증 # Authorization 클라이언트의 인증 정보를 서버에 전달 WWW-Authenticate 401(UnAuthorized) 응답과 함께 사용 리소스 접근에 필요한 인증 방법 쿠키 ❗️ # Http는 Stateless 한 프로토콜이므로, 요청과 응답을 주고받고 끝임\n한계를 해결하기 위해, 모든 요청에 쿠키를 보냄\nSet-Cookie 서버에서 클라이언트로 쿠키 전달 (응답) Cookie 클라이언트가 서버로 쿠키 전달 🍪 # 생명주기 # expires : 만료일 지정 max-age : 초단위 지정 (음수면 삭제) 세션 쿠키 : 만료 날짜를 생략하면, 종료시까지 유지 도메인 # 명시 : 명시한 문서 기준 도메인 + 서브 도메인 포함 example.org를 지정하면 dev.example.org 도 쿠키 보냄 생략 : 현재 문서 기준 도메인만 적용 dev.example.org는 쿠키 안보냄 PATH # 일반적으로 path=/ 루트로 지정 ex) path = /home으로 지정했다면 /home 밑의 경로만 가능\n보안 # Secure : Https에서만 전송 HttpOnly : 자바스크립트에서 document.cookie로 접근 불가 SameSite : 요청 도메인과 쿠키에 설종된 도메인이 같은 경우만 쿠키 전송 (XSRF == CSRF 공격 방지) HTTP2 \u0026lt;- 다음글\n","date":"26 March 2023","externalUrl":null,"permalink":"/posts/velog/039-http-header1/","section":"Posts","summary":"Header란? # Http 전송에 필요한 모든 부가정보 엄청많음 필요시 임의의 헤더 추가 가능\nMessage body로 데이터를 전달 (payload) header는 데이터를 해석할 수 있는 정보 제공 (유형, 길이 ….) ** 표현 = 표현 메타데이터 + 표현 데이터 **\n표현 (Representation) # 표현 헤더는 전송, 응답 둘다 사용함\n","title":"HTTP Header1","type":"posts"},{"content":" 1xx : 처리중 (거의 사용 안함) 2xx : 정상처리 3xx : 추가 행동 필요 4xx : 클라이언트 오류 5xx : 서버 오류 인식할 수 없는 상태코드는 상위상태코드 (ex 299=\u0026gt;2xx)로 해석\n2xx 성공👍 # 200 OK 201 CREATED : (리소스 생성) 202 Accepted : (요청이 접수되었으나, 처리가 완료되지 않음 ) (ex 배치처리) 204 No Content : (서버에서 요청을 수행했지만, 응답 본문에 보낼 데이터는 없음) 3xx (Redirection) ▶️▶️ # 리다이렉션이란? # 웹 브라우저는 3xx 응답에 Location 헤더가 있으면, 자동으로 Location 위치로 이동\n종류\n영구 리다이렉션 : 특정 리소스의 URI가 영구적으로 이동 일시 리다이렉션 : 일시적으로 변경 (PRG) 특수 리다이렉션 : 결과 대신 캐시사용 영구 리다이렉션(301,308) # 리소스의 URI가 영구적으로 이동 원래 URL을 사용 ❌ (검색 엔진 등에서도 변경 인지)\n301 Moved Permanently : 리다이렉트시 메소드가 GET으로 변하고, 본문이 제거될 수도?? 308 Permanent Redirect : 리다이렉트시 요청 메서드와 본문 유지 일시적 리다이렉션 (302,307,303) # 리소스의 URI가 일시적으로 변경 ( 검색 엔진등에서 URL 변경 안함)\n302 Found : 리다이렉트시 요청 메서드가 GET으로 변경되거나 본문이 제거될 수도?? 307 *Temporary Redirect * : 리다이렉트시, 요청 메서드와 본문 유지 303 See other : 302와 비슷, 무조건 GET 으로 변경 302가 모호해서 307, 303 등장\n기타 리다이렉션 (300,304) # 300은 안씀 304 Not Modified\n캐시를 목적으로 사용 클라에게 리소스가 수정되지 않았음을 알려줌 (캐시로 리다이렉트) 로컬 캐시를 사용해야 하므로, 메시지 바디 ❌ 4XX (Client Error) # 오류의 원인이 클라이언트에 있음 (서버가 요청 수행 못함) ** 클라이언트가 잘못된 요청을 보내기 때문에, 재시도는 항상 실패할거임**\n400 Bad Request # 클라이언트의 잘못된 요청으로, 서버가 처리 불가\n요청 구문, 메시지 등 의 오류 (클라가 다시 검토해서 보내야) ex) 요청 파라미터 잘못됨, API 스펙 안맞음 401 * Unauthorized* # ** 인증 필요 **\n응답에 WWW-Authenticate 헤더와 함께 방법을 설명 403 Forbidden # ** 서버가 요청은 이해했지만, 승인 거부 **\n인증은 했지만, 권한없음 ex) 일반사용자가 admin 접근 404 Not Found # ** 요청 리소스를 찾을 수 없음 **\n또는 숨기고 싶을때\n5xx (Server Error) # 서버 내부 문제로 오류\n애매하면 500 4xx와 달리, 재시도시 성공 가능\n503 Service Unavailable # 서비스 이용 불가\n서버가 일시적인 과부하 또는 예정 작업 등으로, 처리 불가 Retry-After 헤더 필드로 복구시간을 보낼 수도 ","date":"26 March 2023","externalUrl":null,"permalink":"/posts/velog/040-http-%EC%83%81%ED%83%9C%EC%BD%94%EB%93%9C/","section":"Posts","summary":" 1xx : 처리중 (거의 사용 안함) 2xx : 정상처리 3xx : 추가 행동 필요 4xx : 클라이언트 오류 5xx : 서버 오류 인식할 수 없는 상태코드는 상위상태코드 (ex 299=\u003e2xx)로 해석\n2xx 성공👍 # 200 OK 201 CREATED : (리소스 생성) 202 Accepted : (요청이 접수되었으나, 처리가 완료되지 않음 ) (ex 배치처리) 204 No Content : (서버에서 요청을 수행했지만, 응답 본문에 보낼 데이터는 없음) 3xx (Redirection) ▶️▶️ # 리다이렉션이란? # 웹 브라우저는 3xx 응답에 Location 헤더가 있으면, 자동으로 Location 위치로 이동\n","title":"HTTP 상태코드","type":"posts"},{"content":" 빈 스코프란? # 빈이 존재할 수 있는 범위\n싱글톤 : 기본 스코프, 스프링 컨테이너의 시작~종료\n프로토타입 : 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여\n웹 관련 스코프\nrequest : 웹 요청이 들어오고 나갈때까지 session : 웹 세션이 생성되고 종료될때까지 application : 웹의 서블릿 컨텍스트와 같은 범위로 유지 프로토타입 스코프 # 싱글톤 스코프의 빈을 조회하면, 항상 같은 인스턴스의 스프링 빈을 반환\n반대로 프로토타입 스코프를 조회하면, 항상 새로운 인스턴스를 생성해서 반환\n생성예제\n@Scope(\u0026#34;prototype\u0026#34;) @Component 싱글톤 빈 요청 항상 같은 인스턴스의 스프링 빈 반환 **프로토타입 빈 요청 ** 1.스프링 컨테이너가 빈 생성, 의존관계 주입 2.반환하고 끝 (@PreDestroy 호출 ❌) 싱글톤과 프로토타입 함께 사용 # 프로토타입 빈은 사용할 때마다 새로 생성되는것이 아니고, 요청시마다 새로 생성된다.\n프로토타입 빈을 주입받는 클라이언트 빈(싱글톤)이 있다고 가정하자 클라이언트 빈이 주입 시점에 프로토타입빈을 받으면, 프로토타입 빈을 주입받고 내부 필드에 보관한다. 프로토타입 빈은 과거에 주입이 끝났으므로, 항상 같은 참조값을 가지게 된다.\n결과적으로, 싱글톤 빈과 함께 프로토타입 빈이 계속 유지되게 된다. 문제점 해결책 # DL 방식(dependency Lookup) # 프로토타입이 필요할때마다 컨테이너에 새로 요청 프로토타입 빈을 주입받는게 아니라, 의존관계를 조회\nObjectProvider (ObjectFactory) # 지정한 빈을 컨테이너에서 찾아주는 DL 서비스 제공\njavax.inject.Provider을 통해서도 가능 (자바표준)\n프로토타입 빈은 거의 사용할 일이 없다\n웹 스코프 # ** 특징 **\n웹 환경에서만 동작 스프링이 해당 스코프의 종료시점까지 관리 =\u0026gt; 종료 메서드가 호출 종류\nrequest : HTTP 요청 하나가 들어오고 나갈 때 까지 유지, 각각의 요청마다 별도의 빈 인스턴스가 생성됨 session : HttpSession과 동일한 생명주기 application : 서블릿 컨텍스트 (ServletContext)와 동일한 생명주기 websocket : 웹 소켓과 동일한 생명주기 예제 스코프와 프록시 # @Scope(value = \u0026#34;request\u0026#34;, proxyMode = ScopedProxyMode.TARGET_CLASS) public class MyLogger {.... 껍데기를 집어넣어 놓고, 기능을 실제 호출하는 시점에 진짜를 찾아서 동작 ObjectProvider처럼 동작함 Provider이던 프록시던, 진짜 객체 조회를 필요한 시점까지 지연처리\n클라이언트는 싱글톤 빈을 쓰는 것처럼, 편리하게 request.scope를 사용 가능\n** 무분별하게 사용하면, 유지보수가 크게 어려워진다**\n","date":"12 March 2023","externalUrl":null,"permalink":"/posts/velog/041-%EB%B9%88-%EC%8A%A4%EC%BD%94%ED%94%84/","section":"Posts","summary":"빈 스코프란? # 빈이 존재할 수 있는 범위\n싱글톤 : 기본 스코프, 스프링 컨테이너의 시작~종료\n프로토타입 : 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여\n웹 관련 스코프\nrequest : 웹 요청이 들어오고 나갈때까지 session : 웹 세션이 생성되고 종료될때까지 application : 웹의 서블릿 컨텍스트와 같은 범위로 유지 프로토타입 스코프 # 싱글톤 스코프의 빈을 조회하면, 항상 같은 인스턴스의 스프링 빈을 반환\n","title":"빈 스코프","type":"posts"},{"content":"","date":"12 March 2023","externalUrl":null,"permalink":"/series/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC%EC%9B%90%EB%A6%AC---%EA%B8%B0%EB%B3%B8/","section":"Series","summary":"","title":"스프링 핵심원리 - 기본","type":"series"},{"content":" 빈 생명주기 콜백이란? # db커넥션 풀이나, 네트워크 소캣처럼 연결을 미리 해두고, 어플리케이션 종료 시점에 연결을 모두 종료하라면 객체의 초기화와 종료 작업이 필요\n스프링은 의존관계 주입이 완료되면, 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려줌 또한, 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 줌 빈 생명주기 콜백 지원 3가지 방법\n인터페이스(InitializingBean, DisposableBean) 설정 정보에 초기화 메서드, 종료 메서드 지정 @PostConstruct, @PreDestroy 인터페이스 # 초창기에 나온 방법으로, 거의 사용안함\nInitializingBean, DisposableBean 상속받아서 진행\nInitializingBean은 afterPropertiesSet()으로 초기화 지원 DisposableBean은 destroy() 로 소멸 지원 빈 등록 초기화, 소멸 메서드 # 얘도 잘 안씀 =\u0026gt; 코드를 고칠 수 없는 경우에만 씀\n설정 정보에 @Bean(initMethod = \u0026quot;init\u0026quot;, destroyMethod = \u0026quot;close\u0026quot;) 처럼 초기화, 소멸 메서드 지정할 수 있다.\n특징\n메서드 이름 자유로움, 고칠수 없는 라이브러리에도 적용가능 스프링 코드에 의존 X 어노테이션 👍👍 # 거의 얘만씀\n시작, 종료시 호출할 메소드에 @PostConstruct,PreDestory 적어놓으면 끝남\n가장 권장함 유일한 단점은 외부 라이브러리에 적용하지 못한다는 것임 이때는 위의 방법을 사용하자 ","date":"12 March 2023","externalUrl":null,"permalink":"/posts/velog/042-%EB%B9%88-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0-%EC%BD%9C%EB%B0%B1/","section":"Posts","summary":"빈 생명주기 콜백이란? # db커넥션 풀이나, 네트워크 소캣처럼 연결을 미리 해두고, 어플리케이션 종료 시점에 연결을 모두 종료하라면 객체의 초기화와 종료 작업이 필요\n스프링은 의존관계 주입이 완료되면, 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려줌 또한, 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 줌 빈 생명주기 콜백 지원 3가지 방법\n인터페이스(InitializingBean, DisposableBean) 설정 정보에 초기화 메서드, 종료 메서드 지정 @PostConstruct, @PreDestroy 인터페이스 # 초창기에 나온 방법으로, 거의 사용안함\n","title":"빈 생명주기 콜백","type":"posts"},{"content":" 의존관계 주입 방법 # 생성자, 수정자, 필드 ,메서드 4가지\n생성자 주입 # 생성자를 통해 주입받는 방법 생성자 호출시점에 단 한번만 (불변, 필수) 생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입\n수정자 주입 # setter을 통해 의존관계 주입 선택, 변경 가능성이 있는 의존관계에 사용 (거의 사용할일 없음) 필드 주입 # 필드에 바로 주입하는 방식 코드가 간결하지만, 외부에서 변경 불가 =\u0026gt; 테스트하기 어려움 DI 프레임워크가 없으면 암것도 못함 @Configuration 같은 곳에서만 특별한 용도로 사용 웬만하면 사용하지 말자!! 실무에서 많이씀 일반 메서드 주입 # 일반적인 메서드를 통한 주입 안씀 옵션 처리 # 주입할 스프링 빈이 없어도 동작해야 될 때\n@Autowired(required=false) : 대상이 없으면 메서드 자체가 호출안됨 org.springframework.lang.@Nullable : 대상없으면 null입력 Optional\u0026lt;\u0026gt; 생성자 주입선택!! # 과거에는 필드 주입을 많이 사용했지만, 최근에는 DI 프레임워크 대부분이 생성자 주입을 권장\n불변\n대부분 DI는 애플리케이션 종료시점까지 의존관계가 변하면 안됨 객체를 생성할 때 딱 1번만 호출됨 누락\n프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우 NPE 위험 방지 final 선언 가능\n생성자에서 뭔가를 잘못해도, 컴파일 시점에 막아줌 final은 항상 생성자에서 들어가야됨 조회할 빈이 2개이상 # @Autowired 는 타입으로 조회하는데, 같은 타입이 2개 이상이라면??\n수동으로 등록해서 해결할 수도 있다\n@Autowired 필드 명 매칭 # @Autowired는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 추가 매칭\n@Qualifier # 추가 구분자를 붙여주는 방법 (주입시 추가적인 방법을 제공)\n주입할 객체, 주입받을 객체에 모두 @Qualifier 붙여줘야됨\n빈등록할때\n@Component @Qualifier(\u0026#34;mainDiscountPolicy\u0026#34;) public class RateDiscountPolicy implements DiscountPolicy... 찾을때\n@Autowired public OrderServiceImpl(MemberRepository memberRepository, @Qualifier(\u0026#34;mainDiscountPolicy\u0026#34;) DiscountPolicy discountPolicy) { ... } @Primary # @primary가 있으면, 다 무시하고 우선권을 가짐\n@Component @Primary public class RateDiscountPolicy implements DiscountPolicy{ 조회한 빈이 모두 필요할 때 List,Map # Map\u0026lt;String, 타입\u0026gt;으로 주입받으면, 해당 타입의 \u0026lt;빈이름 :객체\u0026gt; Map을 받을 수 있다. List로 주입받으면 모든 빈을 담아준다 해당사향이 없으면 빈 컬랙션이나 Map을 주입한다 자동, 수동의 기준 # 웬만하면 자동을 쓰는 추세\n애플리케이션은 크게 업무 로직, 기술지원 로직으로 나눌 수 있다.\n업무 로직 빈 : 컨트롤러, 서비스, 리포지토리 등 기술 지원 빈 : 기술적인 문제나, 공통 관심사를 처리 (DB연결, 공통 로그 처리) 업무 로직의 경우는 자동 빈 권장\n기술 지원 로직은 수가 매우 적고, 애플리케이션 전반에 걸쳐서 광범위한 영향 또한 적용이 잘 되나조차 파악하기 어려운 경우가 많음 따라서 수동으로 등록하여 설정정보에 바로 나타나게 하는것이 유지보수에 좋음\n","date":"6 March 2023","externalUrl":null,"permalink":"/posts/velog/043-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%9E%90%EB%8F%99-%EC%A3%BC%EC%9E%85/","section":"Posts","summary":"의존관계 주입 방법 # 생성자, 수정자, 필드 ,메서드 4가지\n생성자 주입 # 생성자를 통해 주입받는 방법 생성자 호출시점에 단 한번만 (불변, 필수) 생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입\n수정자 주입 # setter을 통해 의존관계 주입 선택, 변경 가능성이 있는 의존관계에 사용 (거의 사용할일 없음) 필드 주입 # 필드에 바로 주입하는 방식 코드가 간결하지만, 외부에서 변경 불가 =\u003e 테스트하기 어려움 DI 프레임워크가 없으면 암것도 못함 @Configuration 같은 곳에서만 특별한 용도로 사용 웬만하면 사용하지 말자!! 실무에서 많이씀 일반 메서드 주입 # 일반적인 메서드를 통한 주입 안씀 옵션 처리 # 주입할 스프링 빈이 없어도 동작해야 될 때\n","title":"의존관계 자동 주입","type":"posts"},{"content":" 컴포넌트 스캔 # 이전 강의들에서는 @Bean 등을 통해서 직접 스프링 빈을 등록함 (귀찮음)\n설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔 설정 정보에 @ComponentScan을 붙여주기만 하면 됨\n컴포넌트 스캔은 이름 그대로 @Component 어노테이션이 붙은 클래스들을 스캔해서 스프링 빈으로 등록\n@Autowired를 사용하면, 생성자에서 여러 의존관계도 한번에 주입 가능\n순서\n@ComponentScan @ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 빈 이름 기본 : 앞글자만 대문자로 MemberServiceImpl =\u0026gt; memberServiceImpl @Component(\u0026quot;name\u0026quot;) 으로 직접 이름 지정도 가능 의존관계 자동 주입 생성자에 @Autowired 를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입 파라미터가 많아도 다 찾아서 주입해줌 탐색 위치, 기본 스캔 대상 # 탐색 위치 지정\n@ComponentScan( basePackages = \u0026#34;hello.core\u0026#34;, //배열도 가능 } 지정하지 않으면, @ComponentScan 이 붙은 클래스의 패키지가 시작 위치가 됨\n설정 정보 클래스의 위치를 프로젝트 최상단에 두는걸 권장\n@Controller : 스프링 mvc 컨트롤러로 인식 @Repository : 데이터 접근 계층으로 인식 =\u0026gt; @Configuration : 설정 정보로 인식, 싱글톤을 위한 추가 처리 @Service : 개발자들에게 알려주는용도 ??? 충돌 # 컴포넌트 스캔시 같은 빈을 등록하면?\ncase 1 : 자동 빈 등록 vs 자동 빈 등록 case 2 : 자동 빈 동록 vs 수동 빈 등록\n자동 vs 자동 # 에러발생 ConflictingBeanDefinitionException 수동 vs 수동 # 수동 등록 빈이 우선권을 가짐 (오버라이딩) *스프링 부트에서는 오류발생! *\n","date":"5 March 2023","externalUrl":null,"permalink":"/posts/velog/044-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94/","section":"Posts","summary":"컴포넌트 스캔 # 이전 강의들에서는 @Bean 등을 통해서 직접 스프링 빈을 등록함 (귀찮음)\n설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔 설정 정보에 @ComponentScan을 붙여주기만 하면 됨\n컴포넌트 스캔은 이름 그대로 @Component 어노테이션이 붙은 클래스들을 스캔해서 스프링 빈으로 등록\n@Autowired를 사용하면, 생성자에서 여러 의존관계도 한번에 주입 가능\n순서\n@ComponentScan @ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 빈 이름 기본 : 앞글자만 대문자로 MemberServiceImpl =\u003e memberServiceImpl @Component(\"name\") 으로 직접 이름 지정도 가능 의존관계 자동 주입 생성자에 @Autowired 를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입 파라미터가 많아도 다 찾아서 주입해줌 탐색 위치, 기본 스캔 대상 # 탐색 위치 지정\n","title":"컴포넌트 스캔","type":"posts"},{"content":" 싱글톤 # 웹 어플리케이션 특성상, 굉장히 많은 요청이 들어옴 이 때마다 객체가 생성되고 소멸되면, 이는 엄청난 메모리 낭비임\n해결책 : 해당 객체가 딱 1개만 생성되고, 공유하도록 설계 =\u0026gt; 싱글톤\nsingleton 예제\nprivate static final SingletonService instance = new SingletonService(); public static SingletonService getInstance(){ return instance; } private SingletonService(){ } 싱글톤 패턴은 여러가지 문제점을 가지고 있다.\n싱글톤 컨테이너 # 스프링 컨테이너는, 싱글톤 패턴의 문제점을 해결하면서, 싱글톤 패턴으로 객체 인스턴스를 관리 (Bean)\n싱글톤 주의점] # **무상태(stateless)**로 설계 특정 클라이언트에 의존 ❌ 특정 클라이언트가 값을 변경 ❌ 가급적 읽기만 가능 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터, Threadlocal 사용 위 사항을 안지키면, 정말 큰 에러! @Configuration # @Bean public MemberService memberService() { return new MemberServiceImpl(memberRepository()); } @Bean public MemberRepository memberRepository() { return new MemoryMemberRepository(); } @Bean public OrderService orderService() { return new OrderServiceImpl(memberRepository(), discountPolicy()); } .... 이 자바 코드를 보면, 각각 new MemorymemberRepository()를 호출하고 있다. 하지만 이 모든 memberRepository 인스턴스는 같은 객체이다 왜⁉️\n바이트 조작 # 스프링은 클래스의 바이트코드를 조작하는 라이브러리를 사용한다\n@Test void configurationDeep(){ AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); AppConfig bean =ac.getBean(AppConfig.class); System.out.println(\u0026#34;bean = \u0026#34; + bean.getClass()); } 출력 출럭 : bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$ee2b784e 순수한 클래스라면, class hello.core.AppConfig 가 나와야됨\n따라서, 스프링이 Appconfig 클래스를 상속받은 임의의 클래스를 만들고, 이를 빈으로 등록한 거임 Appconfig의 @Configuration을 주석처리하면, 순수한 클래스가 등록된다 하지만 싱글톤이 깨짐\n따라서, 궁금증을 해결하려는 용도가 아니면 무조건 @Configuration을 사용하자\n","date":"5 March 2023","externalUrl":null,"permalink":"/posts/velog/045-%EC%8B%B1%EA%B8%80%ED%86%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88/","section":"Posts","summary":"싱글톤 # 웹 어플리케이션 특성상, 굉장히 많은 요청이 들어옴 이 때마다 객체가 생성되고 소멸되면, 이는 엄청난 메모리 낭비임\n해결책 : 해당 객체가 딱 1개만 생성되고, 공유하도록 설계 =\u003e 싱글톤\nsingleton 예제\nprivate static final SingletonService instance = new SingletonService(); public static SingletonService getInstance(){ return instance; } private SingletonService(){ } 싱글톤 패턴은 여러가지 문제점을 가지고 있다.\n","title":"싱글톤 컨테이너","type":"posts"},{"content":" 컨테이너 생성 # ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); 1. 스프링 컨테이너 생성 # new AnnotationConfigApplicationContext(AppConfig.class) 2. 스프링 빈 등록 # 파라미터로 넘어온 설정 클래스 정보를 사용해서 스프링 빈 등록 ** 빈 이름 **\n기본적으로 메서드 이름을 사용 직접 부여 가능 @Bean(name= [...]) 빈 이름은 항상 고유해야함 3. 스프링 빈 의존관계 설정 - 준비 # 객체 생성 4. 스프링 빈 의존관계 설정 - 완료 # 설정 정보를 참고해서 의존관계 주입 컨테이너에 등록된 모든 빈 조회 # AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); @Test @DisplayName(\u0026#34;애플리케이션 빈 출력하기\u0026#34;) void findApplicationBean(){ String[]beanDefinitionNames = ac.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName); // ROLE_APPLICATION : 직접 등록한 애플리케이션 빈 // ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈 if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){ Object bean = ac.getBean(beanDefinitionName); System.out.println(\u0026#34;name = \u0026#34; + beanDefinitionName + \u0026#34;obejct = \u0026#34; + bean); } } } 빈 조회 # 가장 기본적인 조회 방법 # ac.getBean(이름, 타입) ac.getBean(타입) 없으면 예외발생 동일한 타입\t2개이상 # 타입으로 조회시 오류 (이름지정필수) ac.getBeanOfType() 상속관계 # 부모 타입으로 조회하면, 자식 타입도 함께 끌려나옴 BeanFactory와 ApplicationContext # ApplicationContext는 BeanFactory 상속받음 빈 관리기능 + 부가기능 이것이 컨테이너!! ApplicationContext 기능\nBeanFactory 기능을 모두 상속받아 제공 ApplicationContext 부가기능 메시지 소스를 활용한 국제화기능 환경변수 애플리케이션이벤트 편리한 리소스 조회 다양한 설정 지원 # 스프링 컨테이너는 다양한 설정 정보를 받아들일 수 있음\n어노테이션 기반 # new AnnotationConfigApplicationContext(AppConfig.class) 자바 코드로 된 설정정보를 넘기면 됨 XML # GenericXmlApplicationContext 사용 ApplicationContext ac = new GenericXmlApplicationContext(\u0026#34;appConfig.xml\u0026#34;); MemberService memberService = ac.getBean(\u0026#34;memberService\u0026#34;, MemberService.class); 설정 메타 정보 -BeanDefinition # 스프링은 자바 코드던, xml이던 상관없이읽어서 BeanDefinition을 민든다.\nbean당 하나씩 메타 정보가 생성되고,스프링 컨테이너가 이를 기반으로 스프링 빈을 생성한다.\n스프링이 다양한 형태의 설정 정보를 BeanDefinition으로 추상화해서 사용한다 정도만 이해하자.\n","date":"2 March 2023","externalUrl":null,"permalink":"/posts/velog/046-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88/","section":"Posts","summary":"컨테이너 생성 # ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); 1. 스프링 컨테이너 생성 # new AnnotationConfigApplicationContext(AppConfig.class) 2. 스프링 빈 등록 # 파라미터로 넘어온 설정 클래스 정보를 사용해서 스프링 빈 등록 ** 빈 이름 **\n","title":"스프링 컨테이너와 스프링 빈","type":"posts"},{"content":"이전 글에서 언급되었던 OCP,DIP 의 문제점을 스프링에서 해결하는 방법 클라이언트 코드를 변경하지 않고, 새로운 구현체로 갈아끼우려면??\n관심사의 분리 # 어플리케이션의 전체 동작 방식을 구성(config) 하기 위해서, 구현 객체를 생성 하고, 연결 하는 책임을 가지는 별도의 생성 클래스\n어플리케이션의 실제 동작에 필요한 구현 객체를 생성 생성한 객체 인스턴스의 참조(rference)를 생성자를 통해서 주입 즉, 생성자를 통해 어떤 구현체가 들어올지는 오직 외부에서 결정됨 =\u0026gt; 의존관계에 대한 고민은 외부에 맡기고, 실행에만 집중 클라이언트 코드는 절대 변경되지 않음 IOD, DI, 컨테이너 # IOC (제어의 역전) # Inversion of Control\n프로그램의 제어 흐름을 직접 제어하지 않고, 외부에서 관리(프레임워크) DI (의존관계 주입) # Dependency Injection\n정적인 클래스 의존관계\n어플리케이션을 실행하지 않아도 import만으로 분석이 가능 **동적 객체 의존관계 ** DI\n어플리케이션 실행 시점에 생성된 객체 인스턴스의 참조가 연결 클라이언트 코드를 변경없이 클라이언트 호출 대상의 타입 인스턴스 변경 IOC컨테이너 == DI컨테이너 # 객체를 생성,관리,의존관계 연결해주는것 ","date":"1 March 2023","externalUrl":null,"permalink":"/posts/velog/047-%EC%8A%A4%ED%94%84%EB%A7%81-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9B%90%EB%A6%AC-%EC%A0%81%EC%9A%A9/","section":"Posts","summary":"이전 글에서 언급되었던 OCP,DIP 의 문제점을 스프링에서 해결하는 방법 클라이언트 코드를 변경하지 않고, 새로운 구현체로 갈아끼우려면??\n관심사의 분리 # 어플리케이션의 전체 동작 방식을 구성(config) 하기 위해서, 구현 객체를 생성 하고, 연결 하는 책임을 가지는 별도의 생성 클래스\n어플리케이션의 실제 동작에 필요한 구현 객체를 생성 생성한 객체 인스턴스의 참조(rference)를 생성자를 통해서 주입 즉, 생성자를 통해 어떤 구현체가 들어올지는 오직 외부에서 결정됨 =\u003e 의존관계에 대한 고민은 외부에 맡기고, 실행에만 집중 클라이언트 코드는 절대 변경되지 않음 IOD, DI, 컨테이너 # IOC (제어의 역전) # Inversion of Control\n","title":"스프링 객체지향원리 적용","type":"posts"},{"content":" SQLID (좋은 객체 지향 설계의 5원칙) # SRP (단일책임) # *Single Responsibility priniple *\n한 클래스는 하나의 책임만 하나의 책임이란? 문맥과 상황에 라 다름 중요한 기준은 변경 =\u0026gt; 변경이 있을 때 파급 효과가 적으면 👍 OCP (개방-폐쇄)❗️ # Open/closed principle\n소프트웨어 요소는 ** 확장에는 열려 ** 있으나 변경에는 닫혀 있어야 다형성을 활용 (인터페이스) 역활과 구현의 분리 문제점 # 구현 객체를 변경하려면 무조건 클라이언트 코드를 변경해야 함 따라서 객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자 가 필요 LSP (리스코프 치환) # Liskov substitution principle\n다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다. 컴파일 성공을 넘어서는 이야기 ISP (인터페이스 분리 원칙) # Interface segregation principle\n특정 클라이언트를 위한 인터페이스 여러 개가, 범용 인터페이스 하나보다 낫다 인터페이스 명확, 대체 가능성 up DIP (의존관계 역전)❗️ # Dependency inversion principle\n프로그래머는 추상화에 의존해아지, 구체화에 의존하면 안됨* =\u0026gt; 유연한 변경 즉, 구현 클래스에 의존하지 말고, 인터페이스(Role)에 의존 다형성 만으로는, OCP, DIP를 지킬 수 없음\n뭔가 더 필요함\n","date":"27 February 2023","externalUrl":null,"permalink":"/posts/velog/048-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84/","section":"Posts","summary":"SQLID (좋은 객체 지향 설계의 5원칙) # SRP (단일책임) # *Single Responsibility priniple *\n한 클래스는 하나의 책임만 하나의 책임이란? 문맥과 상황에 라 다름 중요한 기준은 변경 =\u003e 변경이 있을 때 파급 효과가 적으면 👍 OCP (개방-폐쇄)❗️ # Open/closed principle\n","title":"객체 지향 설계","type":"posts"},{"content":"","date":"25 February 2023","externalUrl":null,"permalink":"/series/springmvc2/","section":"Series","summary":"","title":"SpringMVC2","type":"series"},{"content":" 전송방식 # 폼을 전송하는 두 방법\napplication/x-www-form-urlencoded multipart/form-data 파일을 전송할 땐, 이진 데이터를 전송해야 되므로, 이 방식 사용 불가 또한, 보통 폼과 파일을 같이 전송하므로, 문자와 바이너리를 동시에 전송해야 함\n이 문제를 해결하기 위해서, HTTP는 multipart/form-data 방식 제공!\nmultipart/form-data # 다른 종류의 여러 파일과 폼의 내용을 함께 전송하는 방식 Content-Disposition 이라는 항목별 헤더가 있음 폼의 일반 데이터는 각 항목별로 문자가 전송 파일의 경우는 파일이름, Content-type이 추가되고, 이진데이터 전송 서블릿과 파일 업로드 # 업로드 사이즈 제한\nspring.servlet.multipart.max-file-size=1MB spring.servlet.multipart.max-request-size=10MB 사이즈를 넘으면 SizeLimitExceededException 발생 spring.servlet.multipart.enabled 끄기(multipart 처리 안함) spring.servlet.multipart.enabled=false (default=true)\n파일 저장 경로 application.properties file.dir= {...}\n서블릿이 제공하는 part 메서드\npart.getSubmittedFileName() part.getInputStream() part.write() 스프링의 파일 업로드 # 스프링에선 MultipartFile 인터페이스로 편리하게 지원\n@PostMapping(\u0026#34;/upload\u0026#34;) public String saveFile(@RequestParam String itemName, @RequestParam MultipartFile file, HttpServletRequest request) throws IOException { log.info(\u0026#34;request={}\u0026#34;,request); log.info(\u0026#34;itemName={}\u0026#34;,itemName); log.info(\u0026#34;multipartFile={}\u0026#34;,file); if(!file.isEmpty()){ String fullPath = fileDir + file.getOriginalFilename(); log.info(\u0026#34;file fullpath={}\u0026#34;,fullPath); file.transferTo(new File(fullPath)); } return \u0026#34;upload-form\u0026#34;; } **MultipartFile 주요 메서드 **\nfile.getOriginalFilename() file.transferTo{...} (파일저장) 다중 파일 업로드\nList\u0026lt;MultiPartFile\u0026gt;을 멤버 변수로 가지고 있는 클래스의 @ModelAttribute 사용 Html \u0026lt;input type=\u0026quot;file\u0026quot; multiple=\u0026quot;multiple\u0026quot;\u0026gt; 사용\n**이미지 보여주기 ** 웹에서 \u0026lt;img\u0026gt; 태그로 이미지를 조회할때, URLResource로 이미지 파일을 읽어서 @ResponseBody로 이미지 바이너리 반환 ex)\n@ResponseBody @GetMapping(\u0026#34;/images/{filename}\u0026#34;) public Resource downloadImage(@PathVariable String filename) throws MalformedURLException { return new UrlResource(\u0026#34;file:\u0026#34; + fileStore.getFullPath(filename)); } 파일 다운받기 @ResponseEntity\u0026lt;Resource\u0026gt; 사용 Content-Disposition헤더가 없으면, 파일의 내용을 그대로 가져와서 보여주기만 한다. 이를 넣어 주어야만 다운로드가 가능하다\npublic ResponseEntity\u0026lt;Resource\u0026gt; downloadAttach(@PathVariable Long itemId) throws MalformedURLException { Item item = itemRepository.findById(itemId); String storeFileName = item.getAttachFile().getStoreFileName(); String uploadFileName = item.getAttachFile().getUploadFileName(); UrlResource resource = new UrlResource(\u0026#34;file:\u0026#34; + fileStore.getFullPath(storeFileName)); log.info(fileStore.getFullPath(storeFileName)); log.info(\u0026#34;uploadFileName={}\u0026#34;, uploadFileName); String encodedUploadFileName = UriUtils.encode(uploadFileName, StandardCharsets.UTF_8); String contentDisposition = \u0026#34;attachment; filename=\\\u0026#34;\u0026#34; + encodedUploadFileName + \u0026#34;\\\u0026#34;\u0026#34;; return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) .body(resource); } ","date":"25 February 2023","externalUrl":null,"permalink":"/posts/velog/049-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C/","section":"Posts","summary":"전송방식 # 폼을 전송하는 두 방법\napplication/x-www-form-urlencoded multipart/form-data 파일을 전송할 땐, 이진 데이터를 전송해야 되므로, 이 방식 사용 불가 또한, 보통 폼과 파일을 같이 전송하므로, 문자와 바이너리를 동시에 전송해야 함\n이 문제를 해결하기 위해서, HTTP는 multipart/form-data 방식 제공!\nmultipart/form-data # 다른 종류의 여러 파일과 폼의 내용을 함께 전송하는 방식 ","title":"파일 업로드","type":"posts"},{"content":" 타입 컨버터란? # HTTP 요청 파라미터는 모두 문자로 처리함 Integer intvalue = Integer.valueOf(data)로 숫자로 변환해야됨\n스프링의 RequestParam을 사용하면 쉽게 타입을 변환해서 받을 수 있음 (@RequestParam Integer data)\n스프링은 컨버터 인터페이스 를 사용해서 이를 가능하게 한다. org.springframework.core.convert.converter.Converter를 구현해서 사용가능\n컨버전 서비스 # 스프링이 제공하는, 개별 컨버터를 모아두고 묶어서 사용하는 기능\n인터페이스 분리 원칙 ISP (Interface Segregation Principal) 클라이언트는 자신이 이용하지 앟는 메서드에 의존하지 않아야 한다 한마디로, 꼭 필요한 것만 알면 됨\n스프링은 내부에서 ConversionSercie를 사용해서 @RequestParam 등을 구현\n타임리프에서 {...}는 컨버터 적용 x, {{...}}는 컨버터 적용 O 뷰 템플릿은 무조건 데이터를 문자로 출력\n포멧터 # 객체를 특정한 포멧에 맞추어 문자로 출력하거나, 그 반대의 역활 수행\nConverter VS Converter\nConverter 는 범용 (객체-\u0026gt;객체) Formatter는 문자에 특화 (Converter의 특별버전) Formatter 인터페이스\nString print(T object, Locale locale) : 객체-\u0026gt;문자 T parse(String text, Locale locale)\t: 문자-\u0026gt;객체 스프링이 제공하는 기본 포멧터 # @NumberFormatter : 숫자 관련 @DateTimeFormat : 날짜 관련 주의 HttpMessageConverter 에서는 컨버전 서비스 사용 못함\n","date":"21 February 2023","externalUrl":null,"permalink":"/posts/velog/050-%EC%8A%A4%ED%94%84%EB%A7%81-%ED%83%80%EC%9E%85-%EC%BB%A8%EB%B2%84%ED%84%B0/","section":"Posts","summary":"타입 컨버터란? # HTTP 요청 파라미터는 모두 문자로 처리함 Integer intvalue = Integer.valueOf(data)로 숫자로 변환해야됨\n스프링의 RequestParam을 사용하면 쉽게 타입을 변환해서 받을 수 있음 (@RequestParam Integer data)\n스프링은 컨버터 인터페이스 를 사용해서 이를 가능하게 한다. org.springframework.core.convert.converter.Converter를 구현해서 사용가능\n컨버전 서비스 # 스프링이 제공하는, 개별 컨버터를 모아두고 묶어서 사용하는 기능\n","title":"스프링 타입 컨버터","type":"posts"},{"content":" 시작 # HTML은, 오류가 나면 4xx,5xx 오류 페이지만 있으면 된다. 근데, API의 경우에는 각 오류 상황에 맞는 응답 스펙을 정하고, json으로 줘야됨\nex)\n@RequestMapping(value=\u0026#34;/error-page/500\u0026#34;,produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity\u0026lt;Map\u0026lt;String, Object\u0026gt;\u0026gt; errorPage500Api(HttpServletRequest request , HttpServletResponse response){ Map\u0026lt;String, Object\u0026gt; result = new HashMap\u0026lt;\u0026gt;(); Exception ex = (Exception) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); result.put(\u0026#34;status\u0026#34;,request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)); result.put(\u0026#34;message\u0026#34;, ex.getMessage()); Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); return new ResponseEntity\u0026lt;\u0026gt;(result, HttpStatus.valueOf(statusCode)); } 요청의 Accept가 application/json인 경우를 처리 HttpRequest의 정보를 토대로 json 생성 후 반환 이전글 참고\nAPI 처리시 가장 좋은 방법은 ExceptionHandler 사용이다\nHandlerExceptionResolver # 발생하는 예외에 따라서 각각 처리하고 싶다면?\nex) IllegalArgumentEsxeption을 처리하지 못하면, 400으로 처리하고싶음 HandlerExceptionResolver 컨트롤러 밖으로 던져진 예외를 해결하고, 동작 방식을 변경 Was가 에러 페이지를 찾아서 다시 제공하지 않고, 정상응답 줄여서 ExceptionResolver\n스프링 부트가 제공하는 ExceptionReoslver # ExceptionHandlerExceptionResolver (가장 중요하므로 따로다룸) ResponseStatusExceptionResolver DefaultHandlerExceptionResolver (마지막우선순위) ResponseStatusExceptionResolver 예외에 따라서 HTTP 코드 지정 내부적으로 response.sendError()호출 ex) @responseStatus가 달려있는 예외 또는 ResponseStatusException\n@ResponseStatus(code= HttpStatus.BAD_REQUEST,reason = \u0026#34;잘못된 요청 오류\u0026#34;) public class BadRequestException extends RuntimeException{} **DefaultHandlerExceprtionResolver ** 스프링 내부 예외 해결 (TypeMismatchException 등) 내부적으로 response.sendError()호출\n@ExceptionHandler # ExceptionHandlerExceptionResolver 사용 우선순위도 가장 높고, 실무에서 API 예외 처리 시 거의 이것만 사용\n@ExceptionHandler 어노테이션을 선언하고, 예외를 지정해주면 해당 컨틀롤러에서 해당 예외(자식도)가 발생하면 이 메서드가 호출됨. (컨트롤러와 비슷하게 동작함)\nex)\n@ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(IllegalArgumentException.class) public ErrorResult illegalExHandler(IllegalArgumentException e){ log.error(\u0026#34;[exceptionHandler] ex\u0026#34;,e); return new ErrorResult(\u0026#34;BAD\u0026#34;,e.getMessage()); } 스프링의 우선순위는 항상 자세한 것이 우선권을 가짐 공식문서\n@ControllerAdvice # 정상 처리와 예외 처리 코드를 분리\n대상으로 지정한 여러 컨트롤러에 @ExceptionHandler,@InitBuilder 부여해줌 컨트롤러, 패키지를 직접 지정 가능 (지정안하면 전부) ","date":"13 February 2023","externalUrl":null,"permalink":"/posts/velog/051-api-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC/","section":"Posts","summary":"시작 # HTML은, 오류가 나면 4xx,5xx 오류 페이지만 있으면 된다. 근데, API의 경우에는 각 오류 상황에 맞는 응답 스펙을 정하고, json으로 줘야됨\nex)\n@RequestMapping(value=\"/error-page/500\",produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity\u003cMap\u003cString, Object\u003e\u003e errorPage500Api(HttpServletRequest request , HttpServletResponse response){ Map\u003cString, Object\u003e result = new HashMap\u003c\u003e(); Exception ex = (Exception) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); result.put(\"status\",request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)); result.put(\"message\", ex.getMessage()); Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); return new ResponseEntity\u003c\u003e(result, HttpStatus.valueOf(statusCode)); } 요청의 Accept가 application/json인 경우를 처리 HttpRequest의 정보를 토대로 json 생성 후 반환 이전글 참고\n","title":"API 예외 처리","type":"posts"},{"content":" 프로젝트 생성 # 서블릿 예외 처리 # 순수 서블릿 컨테이너가 에러를 처리하는 방법\nException response.sendError 자바를 직접 실행할때, main()에서 에러를 던지면 해당 쓰레드는 종료됨 웹 어플리케이션은 Was 까지 던져지고, 해당 화면을 보게됨\n서블릿은 Exception이 서블릿 밖으로 전달되거나, response.sendError()가 호출 되었을 때 상황에 맞게 오류 처리 기능을 제공\n작동원리\n예외가 발생해서 WAS까지 전파 WAS는 오류 페이지 경로를 찾아서 내부에서 호출 (오류 페이지 경로로 다시 요청됨) Was는 오류 페이지를 요청하면서 오류 정보를 request의 attribute에 추가해서 넘겨줌\n서블릿은 클라이언트로부터 발생한 정상 요청인지, 아니면 오류 페이지 출력을 위한 내부 요청인지 구분하기 위해 `DispatcherType` 정보 제공 DispatcherType\nREQUEST : 클라이언트 요청 ERROR : 에러 요청 FORWARD : RequestDispatcher.forward(request,response) Include : 서블릿이나 다른 서블릿에서 JSP 결과 포함할때 ASYNC : 비동기 호출 스프링 부트 예외 처리 # 오류가 발생했을 때, /error를 요청. 스프링 부트가 자동 등록한 BasicErrorController는 이 경로를 기본으로 받음\n개발자 할일 BasicErrorController는 기본적인 로직이 모두 개발되어 있으므로, 오류 페이지 화면만 등록하면 됨 /resorces/templates/error/...에 오류 페이지 파일을 넣어두기만 하자\n4xx.html은 모든 400번대 에러를 처리\nBasicErrorController가 제공하는 정보\ntimestamp: Sun Feb 12 15:15:03 KST 2023 path: /error-ex status: 500 message: null error: Internal Server Error exception: null errors: null trace: null ","date":"11 February 2023","externalUrl":null,"permalink":"/posts/velog/052-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EC%98%A4%EB%A5%98/","section":"Posts","summary":"프로젝트 생성 # 서블릿 예외 처리 # 순수 서블릿 컨테이너가 에러를 처리하는 방법\nException response.sendError 자바를 직접 실행할때, main()에서 에러를 던지면 해당 쓰레드는 종료됨 웹 어플리케이션은 Was 까지 던져지고, 해당 화면을 보게됨\n서블릿은 Exception이 서블릿 밖으로 전달되거나, response.sendError()가 호출 되었을 때 상황에 맞게 오류 처리 기능을 제공\n","title":"예외 처리,오류","type":"posts"},{"content":" 서블릿 필터 # 서블릿이 지원하는 수문장\n필터 흐름 HTTP 요청 -\u0026gt; WAS -\u0026gt; 필터(들) -\u0026gt; 서블릿 -\u0026gt; 컨트롤러\n필터에서 막히면 끝낼수 있음 (로그인에 딱임)\n**필터 인터페이스 (javax.servlet.Filter) **\ninit() : 컨테이너가 생성될때 호출 doFilter() : 로직 destroy() : 컨테이너가 종료될 때 호출 스프링시큐리티 등은 필터로 구현되어있음\n인터셉터 # 스프링 MVC가 제공하는 기술로, 필터와 비슷하지만 순서,범위,사용방법이 다름\n인터셉터 흐름\nHTTP 요청 -\u0026gt; WAS -\u0026gt; 필터 -\u0026gt; 서블릿 -\u0026gt; 스프링 인터셉터(들) -\u0026gt; 컨트롤러\nHandlerInterceptor 인터페이스를 구현해서 사용\npreHandle : 호출 전 true면 진행, false면 끝 PostHandle : 호출 후 (컨트롤러 예외면 호출 X) afterCompletion 요청 완료 이후 (뷰 렌더링 이후) (무조건 호출) addPathPatterns,excludePathPatterns로 정밀하게 URL 지정\n","date":"6 February 2023","externalUrl":null,"permalink":"/posts/velog/053-%EB%A1%9C%EA%B7%B8%EC%9D%B82-%ED%95%84%ED%84%B0%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0/","section":"Posts","summary":"서블릿 필터 # 서블릿이 지원하는 수문장\n필터 흐름 HTTP 요청 -\u003e WAS -\u003e 필터(들) -\u003e 서블릿 -\u003e 컨트롤러\n필터에서 막히면 끝낼수 있음 (로그인에 딱임)\n**필터 인터페이스 (javax.servlet.Filter) **\ninit() : 컨테이너가 생성될때 호출 doFilter() : 로직 destroy() : 컨테이너가 종료될 때 호출 스프링시큐리티 등은 필터로 구현되어있음\n인터셉터 # 스프링 MVC가 제공하는 기술로, 필터와 비슷하지만 순서,범위,사용방법이 다름\n","title":"로그인2 (필터,인터셉터)","type":"posts"},{"content":" 쿠키를 사용한 로그인 # 로그인 상태 유지하기 로그인에 성공하면, 쿠키를 생성하고 HttpServletResponse에 담는다. 웹 브라우저는, 세션이 끊기기 전까지 쿠키를 서버에 계속 보내줌\n로그아웃시에는, 똑같은 이름의 새 쿠키를 생성하고,.setMaxAge(0)으로 지속시간을 없애고, HttpServletResponse 에 담아주면 된다.\n보안문제 # 쿠키값은 임의로 바뀔 수도 있고, 웹 브라우저에 보관되기 때문에 누군가 정보를 훔처갈 수도 있음 해커가 한번 쿠키를 가져가면 평생 사용 가능\n대안\n토큰을 사용, 서버에서 매핑해서 인식, 서버에서 토큰 관리(만료시간짧게) 세션 동작 방식 # 쿠키는 여러 보안 이슈가 있다. 이 문제를 해결하기 위해, 중요한 정보는 서버에 저장하고, 클라이언트와 서버를 임의의 식별자 값으로 연결해야 한다.\n로그인 사용자가 loginId,password정보를 전달하면 서버에서 확인한다. 세선 생성 응답 쿠키 결국 쿠키를 사용하지만, 클라이언트는 세션ID만 가지고 있을 뿐 중요한 정보는 서버에 있다\n클라이언트의 쿠키 전달 세션 직접 만들기 # 생성\nsessionID(UUID사용) 생성 세션 저장소에 sessionId와 값 저장 sessionId가 담긴 쿠키를 클라이언트에 전달 조회\n클라이언트가 요청한 sessionId(쿠키) 값으로, 세션 저장소 조회 만료\n클라이언트가 요청한 sessionId 값으로 세션 저장소에서 제거 세션은 특별한게 아니고 단지 쿠키를 사용해서 서버에서 데이터를 유지하는 방법일 뿐이다\n서블릿 Http 세션 # 서블릿의 HttpSession 기능을 사용\n세션 생성 \u0026amp; 조회 request.getSession(true) default\n세션이 있으면 기존 세션 반환, 없으면 생성 false를 주면, 없으면 null반환 ** 정보보관 \u0026amp; 삭제 **\n보관 session.setAttribute(String name, Object value);로 값 넣기 삭제 public String logoutV3(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session != null) { session.invalidate(); } return \u0026#34;redirect:/\u0026#34;; } //request.getSession(false)로 세션이 있는지 확인 //만일 세션이 있으면 invalidate()를 통해 제거 @SessionAttribute를 통해 세션에서 간단하게 꺼낼 수 있음\n@GetMapping(.....) public String homeLoginV3Spring( @SessionAttribute(name = \u0026#34;loginMember\u0026#34;, required = false) Member loginMember, Model model) 세션 정보와 타임아웃 # **세션 기본 정보 **\nsessionId : 세션아이디 maxInactiveInterval 유효시간 creationTime : 생성일시 lastAccessedTime : (사용자가) 최근 서버에 접근한 시간 isNew 새로 생성됬는지 타임아웃 설정 현재 사용자가 로그아웃을 하지 않으면, 서버는 세션 데이터를 계속 보관해야함 따라서, 사용자가 최근에 요청한 시간을 기준으로 생존시간을 유지해줌\n글로벌 설정 application.properties에 설정 server.servlet.session.timeout=60 (60초) 특정 세션 단위로 설정 session.setMaxInactiveInterval(60); lastAcessTime 이후로 timeout 시간이 지나면, Was가 세션을 제거해줌\n** 세션은 기본적으로 메모미를 사용하므로, 최소한의 정보만 보관하자**\n","date":"29 January 2023","externalUrl":null,"permalink":"/posts/velog/054-%EB%A1%9C%EA%B7%B8%EC%9D%B81%EC%BF%A0%ED%82%A4%EC%84%B8%EC%85%98/","section":"Posts","summary":"쿠키를 사용한 로그인 # 로그인 상태 유지하기 로그인에 성공하면, 쿠키를 생성하고 HttpServletResponse에 담는다. 웹 브라우저는, 세션이 끊기기 전까지 쿠키를 서버에 계속 보내줌\n로그아웃시에는, 똑같은 이름의 새 쿠키를 생성하고,.setMaxAge(0)으로 지속시간을 없애고, HttpServletResponse 에 담아주면 된다.\n보안문제 # 쿠키값은 임의로 바뀔 수도 있고, 웹 브라우저에 보관되기 때문에 누군가 정보를 훔처갈 수도 있음 해커가 한번 쿠키를 가져가면 평생 사용 가능\n","title":"로그인1(쿠키,세션)","type":"posts"},{"content":" Bean Validation이란? # 검증 어노테이션과 여러 인터페이스의 모음 일반적으로 하이버네이트 Validator를 구현체로 사용 공식 문서\nbuild.gradle에 의존관계추가\nimplementation \u0026#39;org.springframework.boot:spring-boot-starter-validation\u0026#39; 스프링에선 빈 검증기를 완전히 통합해뒀음\n스프링 적용 # implementation 'org.springframework.boot:spring-boot-starter-validation'이 있으면, 자동으로 통합\n글로벌 Validator를 등록하기 때문에, @Validated만 적용하면 됨 오류가 나면, filedError,ObjectError를 생성해서 BindingResult에 담아줌\n검증 순서 1.@ModelAttriburte로 각각의 필드에 타입 변환 시도\n성공하면 다음으로 실패하면 typeMismatch로 FieldError 추가 2.Validator 적용 (변환에 성공한 필드만)\n에러 코드 # 오류코드는 기본적으로 어노테이션 이름으로 등록된다. 오류코드를 기반으로 MessageCodesResolver를 통해 생성됨\n글로벌(오브젝트) 오류 @ScriptAssert를 사용하는 방법도 있지만, 기능이 약하므로 오브젝트 오류 관련은 자바로 작성 권장\nGroups # 동일한 모델 객체를 등록할 때와 수정할 때 요구사항이 다를 수 있다.\n해결방법2가지\ngroups 기능을 사용한다 ItemsaveFrom, ItemUpdateForm 같은 별도의 모델 객체를 사용한다 인터페이스를 만들고, 어노테이션을 쓸 인터페이스를 지정해준다.\n@NotBlank(groups = {SaveCheck.class, UpdateCheck.class}) @Validate에, 인터페이스를 넘겨주면, 해당 인터페이스가 지정됬을때만 검사한다\npublic String edit2(@PathVariable Long itemId, @Validated(UpdateCheck.class) @ModelAttribute Item item, BindingResult bindingResult) groups 기능은 실제 잘 사용되지 않는다\nForm 전송 객체 분리 # 실무에서는 groups를 잘 사용하지 않는다. 등록시 폼에서 전달하는 데이터가, 도메인 객체와 딱 맞지 않기 때문이다.\n그래서 보통 객체를 직접 전달받지 않고, 폼의 데이터를 컨트롤러까지 전달할 별도의 객체를 만들어서 전달한다.\nItem 도메인 객체 사용 HTML form =\u0026gt; Item =\u0026gt; Controller =\u0026gt; Item =\u0026gt; Repository\n폼만을 위한 별도의 객체 사용 HTML form =\u0026gt; ItemsaveForm =\u0026gt; Controller =\u0026gt; Item 생성 =\u0026gt; Repository\n실무에서는 거의 이 방식을 쓴다\nHTTP 메시지 컨버터 # Validated는 HttpMessageConverter(@RequestBody)에도 적용 가능\nAPI 3가지 경우\n성공 실패 : JSON을 겍체로 생성하는 것 자체가 실패 (컨트롤러 실행안됨) 검증 오류 : JSON을 객체로 생성하는 것까진 성공, 검증 실패 ** ModelAttributes vs RequestBody **\nModelAttributes\n필드 단위로 정교하게 바인딩 특정 필드가 타입이 맞지 않아도, 나머지는 정상 처리 RequestBody\nHttpMessageConverter에서 Json을 객체로 변경하지 못하면 바로 예외 ","date":"26 January 2023","externalUrl":null,"permalink":"/posts/velog/055-%EA%B2%80%EC%A6%9D2-bean-validation/","section":"Posts","summary":"Bean Validation이란? # 검증 어노테이션과 여러 인터페이스의 모음 일반적으로 하이버네이트 Validator를 구현체로 사용 공식 문서\nbuild.gradle에 의존관계추가\nimplementation 'org.springframework.boot:spring-boot-starter-validation' 스프링에선 빈 검증기를 완전히 통합해뒀음\n스프링 적용 # implementation 'org.springframework.boot:spring-boot-starter-validation'이 있으면, 자동으로 통합\n글로벌 Validator를 등록하기 때문에, @Validated만 적용하면 됨 오류가 나면, filedError,ObjectError를 생성해서 BindingResult에 담아줌\n","title":"검증2 - Bean Validation","type":"posts"},{"content":" 검증 요구사항 # 웹 서비스는 폼 입력시 오류가 발생하면, 데이터를 유지한 상태로 오류를 사용자에게 알려주어야 한다.\n컨트롤러의 중요한 역활 중 하나는, HTTP 요청이 정상인지 검증하는 것이다\n클라이언트 검증(js)은 보안에 취약 서버만으로 검증하면, 고객 사용성 부족 적절히 섞어서 사용하되, 최종 서버 검증은 필수 이전 프로젝트를 일부 수정한 validation-start프로젝트에서 진행\n검증 처리 # 고객이 폼을 잘못 입력한 경우 검증시 오류가 발생하면 errors 해시맵에 담아놓고, 이와함께 폼으로 다시가는식 @ModelAttribute는 사용자가 입력한 데이터를 계속 가지고 있음\n** 폼에서 사용자에게 오류 알리기 **\n\u0026lt;div th:if=\u0026#34;${errors?.containsKey(\u0026#39;globalError\u0026#39;)}\u0026#34;\u0026gt; \u0026lt;p class=\u0026#34;field-error\u0026#34; th:text=\u0026#34;${errors[\u0026#39;globalError\u0026#39;]}\u0026#34;\u0026gt;전체 오류 메시지\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; errors? : 에러가 null이면, NPE대신 null 반환 th:if에서 null을 실패로 처리하고 무시 (SpringEL문법) BindingResult # 스프링이 제공하는 검증 오류를 보관하는 객체\npublic String addItemV1(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) { 주의 BindingResult bindingResult는 무조건 ModelAttribute Item item 뒤어야함\nFieldError(String objectName, String field, String defaultmessage)\n특정 필드에 오류가 있을 때 @ModelAttribute 이름, 필드이름, 오류 기본메시지 ObjectError(String objectName, String defaultMessage)\n특정 필드를 넘어서는 오류 (글로벌에러) @ModelAttribute 이름, 오류 기본메시지 ** 타임리프를 통한 처리** ObjectError(글로벌에러)\n\u0026lt;div th:if=\u0026#34;${#fields.hasGlobalErrors()}\u0026#34;\u0026gt; \u0026lt;p class=\u0026#34;field-error\u0026#34; th:each=\u0026#34;err : ${#fields.globalErrors()}\u0026#34; th:text=\u0026#34;${err}\u0026#34;\u0026gt;전체 오류 메시지\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; FieldError th:field로 설정한 필드와, bindingResult의 필드가 일치할 때 오류\n\u0026lt;div\u0026gt; \u0026lt;label for=\u0026#34;itemName\u0026#34; th:text=\u0026#34;#{label.item.itemName}\u0026#34;\u0026gt;상품명\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; id=\u0026#34;itemName\u0026#34; th:field=\u0026#34;*{itemName}\u0026#34; th:errorclass=\u0026#34;field-error\u0026#34; class=\u0026#34;form-control\u0026#34; placeholder=\u0026#34;이름을 입력하세요\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;field-error\u0026#34; th:errors=\u0026#34;*{itemName}\u0026#34;\u0026gt; 상품명 오류 \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div\u0026gt; 기능정리\n#fields : BindingResult가 제공하는 검증 오류에 접근 가능 th:errors : 해당 필드에 오류가 있는 경우 태그 출력 th:if편의버전 th:errorclass : 해당 필드에 오류가 있으면 클래스정보 추가 타임리프 검증오류 공식 메뉴얼\nBindingResult 2 # @ModelAttribute에 바인딩 시 오류가 발생하면?\nBindingResult가 있으면 -\u0026gt; 400오류가 발생하면서 오류페이지로 이동 BindingResult가 없으면 -\u0026gt;오류정보(FieldError)를 BindingResult 에 담아서 컨트롤러를 호출 BindingResult에 검증 오류를 적용하는 3가지방법\n스프링이 FieldError를 BindingResult에 넣어줌 개발자가 직접 넣어줌 Validator사용 BindingResult는 무조건 검증할 대상 다음에 와야하고, 모델에 자동으로 포함된다\nFieldError,ObjectError # ** FieldError 생성자(2개있음)**\nObjectName : 객체 이름 field : 오류 필드 rejectedValue : 사용자가 입력한 값(거절된) bindingFailure : 바인딩 실패인지, 검증 실패인지 구분 codes : 메시지 코드 arguments : 메시지에서 사용하는 인자 defaultMessage : 기본 오류 메시지 new FieldError(\u0026#34;item\u0026#34;,\u0026#34;itemName\u0026#34;,item.getItemName(), false,null,null,\u0026#34;상품 이름은 필수입니다.\u0026#34; 타입 오류로 바인딩에 실패하면 스프링은 FieldError를 생성하면서 입력값을 넣고, bindingResult에 사용자 입력 데이터를 담아 컨트롤러 호출\n타임리프에서 입력 값 유지 th:field=\u0026quot;*{price}\u0026quot; th:field는 정상일땐 모델 객체의 값을 사용하지만, 오류가 발생하면 FieldError에서 보관한 값을 사용해 출력\n오류코드와 메시지 처리 1 # ** errors 메시지파일 ** 여기서 했던 메시지처럼, errors.properties라는 별도의 파일 생성 후 spring.messages.basename=messages,errors 추가하면 resources 밑에 있는 errors.properties를 참고하게됨\nex)\nif(!StringUtils.hasText(item.getItemName())){ bindingResult.addError(new FieldError(\u0026#34;item\u0026#34;,\u0026#34;itemName\u0026#34;,item.getItemName(), false, new String[]{\u0026#34;required.item.itemName\u0026#34;},null,\u0026#34;상품 이름은 필수입니다.\u0026#34;)); } codes에 String 배열로 넣어주면 된다. 배열로 받는 이유는, 첫 없으면 다음 원소를 넣어줘서임 argument가 있으면, Object배열로 넣어주면 된다.\n오류코드와 메시지 처리 2 # bindingResult는 검증할 객체 바로 다음에 오기 때문에, 검증할 객체를 알고있다.\nrejectValue(),reject() # BindingResult가 제공하는 rejectValue(),reject()로 FieldError,ObjectError를 직접 생성하지 않고 오류를 다룰 수 있다.\n바꾼 방식\nbindingResult.rejectValue(\u0026#34;itemName\u0026#34;,\u0026#34;required\u0026#34;, new Object[]{1000,1000000},null); //// void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage); //오류필드명,messageResolver를 위한 오류 코드, Args,기본메시지 오류 코드와 메시지 처리 3 # 오류 메시지를 required=필수값 입니다 처럼 단순하게 만들면 범용성이 좋지만, 세밀한 메시지를 작성하기 어렵다. 반대로 required.item.itemName=상품 이름은 필수입니다. 처럼 너무 자세하게 만들면 범용성이 떨어진다.\n따라서 ** 범용성으로 사용하다가, 세밀하게 작성해야 하면 세밀한 내용이 적용되도록 단계를 둔다**\n스프링은 MessageCodesResolver\t로 이러한 기능을 지원한다.\n오류 코드와 메시지 처리 4 # MessageCodesResolver\n검증 오류 코드로 코드들을 생성 기본 구현체는 DefaultMessageCodesResolver 주로 ObjectError,FieldError와 사용 ** DefaultMessageCodesResolver**의 기본 메시지 생성 규칙\n** 객체 오류**\n1.:code+\u0026#34;,\u0026#34;+object name 2.:code ex) 에러코드가 totalPriceMin objectName이 item이라면 totalPriceMin.item (1순위) =\u0026gt; 없으면 totalPriceMin(2순위)\n** 필드 오류**\n1.:code+\u0026#34;.\u0026#34;+object name + \u0026#34;.\u0026#34; + field 2.:code + \u0026#34;.\u0026#34; + field 3. code+ \u0026#34;.\u0026#34; + field type 4. code ex) 에러코드가 required objectName이itemName이라면\n\u0026ldquo;required.item.itemName\u0026rdquo; \u0026ldquo;required.itemName\u0026rdquo; \u0026ldquo;required.java.lang.String\u0026rdquo; \u0026ldquo;required\u0026rdquo; ** 오류 메시지 출력 ** 타임리프가 랜더링 할때 오류가 있다면 th:errors가 생성된 메시지 코드들을 순서대로 돌아가면서 메시지를 찾고 출력 (없으면 default-\u0026gt;에러남)\n오류 코드와 메시지 처리5 # 정리\nrejectValue() 호출 MessageCodesResolver를 사용해서 검증 오류 코드로 메시지 코드 생성 3.new FieldError()를 생성하면서 메시지 코드들을 보관 4.th:errors에서 순서대로 메시지를 찾음 오류 코드와 메시지 처리 6 # 검증 오류 코드는 2가지로 나뉨\n개발자가 직접 설정 -\u0026gt; rejectValue() 직접 호출 스프링이 직접 검증 오류에 추가 (주로 타입정보가 안맞음) 스프링은 타입 오류가 발생하면 typeMismatch라는 오류 코드를 생성 =\u0026gt; 이게 MessageCodesResolver를 통하면서 4가지 메시지 코드 생성됨\nerrors.properties에 메시지 코드를 정의하면 됨 ex)\ntypeMismatch.java.lang.Integer=숫자를 입력해주세요. typeMismatch=타입 오류입니다. Validator 분리 # 컨트롤러에서 검증하지않고, 따로 Validator를 만들어서 검증!\n스프링에서 제공하는 Validator를 구현 오버라이딩\npublic boolean supports(Class\u0026lt;?? class) return Item.class.isAssignableFrom(clazz);원하는 객체인지 `public void validate(Object target, Errors errors) 검증해서 에러를 담아주는 역활 object=객체, Errors=bindingResult 추가적인 도움 # 컨트롤러에서 WebDataBinder를 통해서 검증기를 추가하고 메소드에서 @Validated를 통해서 검증기를 실행할 수 있다.\n","date":"25 January 2023","externalUrl":null,"permalink":"/posts/velog/056-%EA%B2%80%EC%A6%9D1-validation/","section":"Posts","summary":"검증 요구사항 # 웹 서비스는 폼 입력시 오류가 발생하면, 데이터를 유지한 상태로 오류를 사용자에게 알려주어야 한다.\n컨트롤러의 중요한 역활 중 하나는, HTTP 요청이 정상인지 검증하는 것이다\n클라이언트 검증(js)은 보안에 취약 서버만으로 검증하면, 고객 사용성 부족 적절히 섞어서 사용하되, 최종 서버 검증은 필수 이전 프로젝트를 일부 수정한 validation-start프로젝트에서 진행\n검증 처리 # 고객이 폼을 잘못 입력한 경우 ","title":"검증1 -Validation","type":"posts"},{"content":" 메시지, 국제화 소개 # 메시지 # 기획자가 \u0026ldquo;상품명\u0026rdquo; 이란 단어를 모두 \u0026ldquo;상품이름\u0026rdquo; 으로 고쳐달라고 하면? HTML 파일에 메시지가 하드코딩 되어 있어 모든 화면을 고쳐야 한다 ㅅㅂ\n이런 다양한 메시지를 한 곳(파일)에서 관리하는 기능을 메시지 기능이라 한다.\nEX) messages.properties라는 파일을 만들고\nitem=상품 item.itemName=상품명 ... HTML에서 해당 데이터를 key값으로 불러서 사용한다 addForm.html \u0026lt;label for=\u0026quot;itemName\u0026quot; th:text=\u0026quot;#{item.itemName}\u0026quot;\u0026gt;\u0026lt;/label\u0026gt;\n국제화 # 위의 messages.properties를 각 나라별로 관리하면, 서비스를 국제화 가능\n스프링은 기본적인 메시지와 국제화 기능을 제공하고, 타임리프도 이와 통합해서 제공\n스프링 메시지 소스 설정 # 여기서 검색\n스프링부트를 사용하면, 스프링부트가 MessageSource를 자동으로 빈에 등록\napplication.properties(소스 기본 값) resources 밑의messages.properties 파일 기본 인식 (파일명 마지막에 언어정보가 있는 파일도 인식한다 ex)messages_en.properties )\nspring.messages.basename=messages /src/main/resources/messages.properties\nhello=안녕 hello.name=안녕 {0} /src/main/resources/messages_en.properties\nhello=hello hello.name=hello {0} 웹 애플리케이션에 메시지 적용하기 # messages.properties등록\nlabel.item=상품 label.item.id= 상품 ID label.item.itemName=상품명 label.item.price=가격 label.item.quantity=수량 page.items=상품 목록 page.item=상품 상세 page.addItem=상품 등록 page.updateItem=상품 수정 button.save=저장 button.cancel=취소 타임리프 메시지 표현식 #{...} ex) \u0026ldquo;상품\u0026quot;을 조회하려면 #{label.item} messages.properties에 설정된 값을 변경하면, 모든 메시지가 바뀐다!!\n참고 hello.name=안녕{0}\n\u0026lt;p th:text=\u0026#34;#{hello.name(${item.itemName})}\u0026#34;\u0026gt;\u0026lt;/p\u0026gt; 이런 식으로 파라미터받아서도 사용가능\n국제화 적용 # messages_en.properties에 영어로 지정해주면 끝!!\nRequest header의 Accept-Language:에 따라 변환될것임\nLocaleResolver 인터페이스 Locale선택 방식을 변경하려면, LocaleResolver 구현체를 변경해서 사용 ex) 사용자가 직접 언어를 선택하게 만들기\n쓸일이 없을것같지만, 생기면 구글검색\n","date":"24 January 2023","externalUrl":null,"permalink":"/posts/velog/057-%EB%A9%94%EC%8B%9C%EC%A7%80%EA%B5%AD%EC%A0%9C%ED%99%94/","section":"Posts","summary":"메시지, 국제화 소개 # 메시지 # 기획자가 “상품명” 이란 단어를 모두 “상품이름” 으로 고쳐달라고 하면? HTML 파일에 메시지가 하드코딩 되어 있어 모든 화면을 고쳐야 한다 ㅅㅂ\n이런 다양한 메시지를 한 곳(파일)에서 관리하는 기능을 메시지 기능이라 한다.\nEX) messages.properties라는 파일을 만들고\nitem=상품 item.itemName=상품명 ... HTML에서 해당 데이터를 key값으로 불러서 사용한다 addForm.html \u003clabel for=\"itemName\" th:text=\"#{item.itemName}\"\u003e\u003c/label\u003e\n","title":"메시지,국제화","type":"posts"},{"content":" 프로젝트 설정 # 저번에했던 프로젝트 조금 수정해서 진행\n타임리프 스프링 통합 # 공식문서 추가되는 기능들\nSpringEL 문법 통합 빈 호출 지원 폼 관리 추가 속성 폼 컴포넌트 기능 메시지, 국제화 \u0026hellip;. 스프링 부트에서는build.gradle의 implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'가 있으면, 자동으로 설정해준다.\n입력 폼 처리 # \u0026lt;form action=\u0026#34;item.html\u0026#34; th:action th:object=\u0026#34;${item}\u0026#34; method=\u0026#34;post\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;label for=\u0026#34;itemName\u0026#34;\u0026gt;상품명\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; th:field=\u0026#34;*{itemName}\u0026#34; class=\u0026#34;form-control\u0026#34; placeholder=\u0026#34;이름을 입력하세요\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; th:object=\u0026quot;{item}\u0026quot; : form에서 사용할 객체를 지정 th:field=\u0026quot;*{itemName}\u0026quot; 선택변수식 *{itemName} == ${item.itemName} th:field는 id, name, value 속성을 자동으로 생성 체크박스 # logging.level.org.apache.coyote.http11=debug 서버에서 http 요청 메시지를 볼 수 있다.\n체크 박스를 선택하면, Form에서 open=on 값이 넘어간다. 스프링이 이를 true로 반환한다.\n** 주의 ** 선택하지 않는다면 open 필드 자체가 전송되지 않는다 (null) 수정시 문제될수도있음\n이를 해결하기 위해, 히든 필드를 하나 만들어서 해결한다 _open처럼 기존 이름 앞에 _를 붙여서 전송하면 해제했다고 인식 (스프링지원기능) 만일 open이 들어온다면 _open은 무시\n타임리프 활용 # 위처럼 개발할 때마다 히든 필드를 추가하는건 상당히 번거로움 th:field 를 사용하면, 저절로 히든 필드를 추가해줌 또 조회시 checked 속성을 자동으로 넣어줌\n체크박스 멀티 # @ModelAttribute('이름') 메소드 레벨 해당 컨트롤러가 호출이 되면, 자동으로 모델에 리턴값 담아줌\n@ModelAttribute(\u0026#34;region\u0026#34;) public Map\u0026lt;String, String\u0026gt; regions(){ Map\u0026lt;String,String\u0026gt; regions = new LinkedHashMap\u0026lt;\u0026gt;(); regions.put(\u0026#34;SEOUL\u0026#34;,\u0026#34;서울\u0026#34;); regions.put(\u0026#34;BUSAN\u0026#34;,\u0026#34;부산\u0026#34;); regions.put(\u0026#34;JEJU\u0026#34;,\u0026#34;제주\u0026#34;); return regions ; } //이게 선언된 컨트롤러에선, 모델에 무조건 regions가 담기게 된다 //자동으로 model.addAttribute(\u0026#34;regions\u0026#34;,regions\u0026#34;) //*이걸통해 타임리프에서 반복으로 생성* th:for=\u0026quot;(#ids.prev('open')} 반복해서 멀티 체크박스를 만들 때, ID는 유일해야하므로 타임리프는 루푸 안에서 1,2,3등의 숫자를 붙여준다. 따라서 ids.prev(...),ids.next(...)를 사용해 동적으로 제공되는 ID를 사용한다. 조회시 타임리프는th:field에 지정한 값과 th:value에 지정된 값을 비교해서 자동으로 처리해준다\n\u0026lt;div\u0026gt;등록 지역\u0026lt;/div\u0026gt; \u0026lt;div th:each=\u0026#34;region : ${regions}\u0026#34; class=\u0026#34;form-check form-check-inline\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;checkbox\u0026#34; th:field=\u0026#34;${item.regions}\u0026#34; th:value=\u0026#34;${region.key}\u0026#34; class=\u0026#34;form-check-input\u0026#34;\u0026gt; \u0026lt;label th:for=\u0026#34;${#ids.prev(\u0026#39;regions\u0026#39;)}\u0026#34; th:text=\u0026#34;${region.value}\u0026#34; class=\u0026#34;form-check-label\u0026#34;\u0026gt;서울\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; radio도 비슷 (radio는 무조건값이들어감)\n셀렉트 박스도 비슷 (selected 알아서 넣어줌)\n","date":"23 January 2023","externalUrl":null,"permalink":"/posts/velog/058-%ED%83%80%EC%9E%84%EB%A6%AC%ED%94%84-%EC%8A%A4%ED%94%84%EB%A7%81-%ED%86%B5%ED%95%A9/","section":"Posts","summary":"프로젝트 설정 # 저번에했던 프로젝트 조금 수정해서 진행\n타임리프 스프링 통합 # 공식문서 추가되는 기능들\nSpringEL 문법 통합 빈 호출 지원 폼 관리 추가 속성 폼 컴포넌트 기능 메시지, 국제화 …. 스프링 부트에서는build.gradle의 implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'가 있으면, 자동으로 설정해준다.\n입력 폼 처리 # \u003cform action=\"item.html\" th:action th:object=\"${item}\" method=\"post\"\u003e \u003cdiv\u003e \u003clabel for=\"itemName\"\u003e상품명\u003c/label\u003e \u003cinput type=\"text\" th:field=\"*{itemName}\" class=\"form-control\" placeholder=\"이름을 입력하세요\"\u003e \u003c/div\u003e th:object=\"{item}\" : form에서 사용할 객체를 지정 th:field=\"*{itemName}\" 선택변수식 *{itemName} == ${item.itemName} th:field는 id, name, value 속성을 자동으로 생성 체크박스 # logging.level.org.apache.coyote.http11=debug 서버에서 http 요청 메시지를 볼 수 있다.\n","title":"타임리프 스프링 통합","type":"posts"},{"content":"스프링MVC1에서 이어지는 시리즈 마찬가지로 김영한님의 스프링MVC2 강의를 듣고 정리\n","date":"17 January 2023","externalUrl":null,"permalink":"/posts/velog/059-init/","section":"Posts","summary":"스프링MVC1에서 이어지는 시리즈 마찬가지로 김영한님의 스프링MVC2 강의를 듣고 정리\n","title":"Init","type":"posts"},{"content":" 프로젝트 생성 # 소개 # 공식문서\n타임리프 특징 # 서버사이드 렌더링 내추럴 템플릿 스프링 통합 지원 타임리프 사용 선언 \u0026lt;html xmlns:th\u0026quot;http://www.thymeleaf.org\u0026quot;\u0026gt;\n텍스트 - text,utext # 기본적으로 HTML 태그 속성에 기능을 정의해서 동작\nHTML의 콘텐츠에 데이터를 출력할 때 th:text 사용 ex) \u0026lt;span th:text=\u0026quot;{data}\u0026quot;\u0026gt;\n태그 안이 아닌 직접 데이터를 출력할려면 [[...]]사용 ex) \u0026lt;li\u0026gt;컨텐츠 안에서 직접 출력하기 = [[${data}]]\u0026lt;/li\u0026gt;\nEscape # \u0026lt;,\u0026gt;등의 특수 문자는 자동적으로 \u0026lt;,\u0026gt; 등의 Html 엔티티로 바뀌어서 인식됨 타임리프에서는 자동적으로 지원\nUnescape # 위 기능을 사용하지 않으려면 2가지 방법이 있음\nth:text=\u0026gt;th:utext [[...]] =\u0026gt; [(...)] escape가 기본, 꼭 필요할 때만 unescape\nSpringEL(변수) # 변수를 사용할 때 쓰는 표현식 ${...} 여기에 스프링에서 제공하는 방법까지 사용\nObject\nuser.username =\u0026gt; 실제로는 프로퍼티 접근법 사용 user['username'] =\u0026gt; 위와동 user.getUsername() =\u0026gt; getUsername() 직접호출 List Users[0]. ... 등으로 접근\nMap Usermap[0]. ...으로 접근\n지역변수 th:with=\u0026quot;...\u0026quot; 사용해서 변수 선언 선언한 태그 안에서만 사용 가능\n\u0026lt;div th:with=\u0026#34;first=${users[0]}\u0026#34;\u0026gt;\u0026lt;!--first변수에 user[0] 할당--\u0026gt; \u0026lt;p\u0026gt; 처음 사람의 이름은 \u0026lt;span th:text=\u0026#34;${first.username}\u0026#34;\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; 기본 객체들 # ** 스프링부트 3.0 이상버전부터는 모델에 직접 담아줘야된다. **\n${#request},${#response},${#session}, {#servletConext}, ${#locale}\n편의 객체 # Http 요청 파라미터 접근 : param ex)${param.paramdata} Http 세션 session ex) ${session.sessionData} 스프링 빈 @ ex) ${@helloBean.hello('Spring!')} 유틸리티 객체와 날짜 # 유틸리티 객체 예시 필요할때 찾아서 참고하자!\nURL 링크 # @{...} 문법으로 URL 생성\n단순 URL\n@{/hello} =\u0026gt; /hello ** 쿼리 파라미터 **\n`@{hello(param1=${param1}, param2=${param2})} =\u0026gt;/hello?param1=data¶m2=data2 ** () 에 있는 부분은 쿼리파라미터로 들어감 ** 경로 변수 **\n\u0026quot;@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})} =\u0026gt; /hello/data1/data2 URL 경로상의 변수가 있으면, () 부분은 경로변수로 처리 경로변수+쿼리파라미터도 가능\n@{/hello/{param1}(param1=${param1}, param2=${param2})} =\u0026gt; /hello/data1?param2=data2 리터럴 # 리터럴이란? 소스 코드상에 고정된 값 즉 문자, 숫자, 불린, null이 있음\n**원칙적으로 문자 리터럴은 ''로 감싸야 한다`` 공백없이 쭉 이어지면 생략 가능하지만, 공백이 있으면 오류가 남 ex) \u0026lt;span th:text=\u0026quot;hello World!\u0026quot;\u0026gt; \u0026lt;span\u0026gt; =\u0026gt; 오류 리터럴 대체 문법\n\u0026lt;li\u0026gt;\u0026#39;hello \u0026#39; + ${data} = \u0026lt;span th:text=\u0026#34;\u0026#39;hello \u0026#39; + ${data}\u0026#34;\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;/li\u0026gt; \u0026lt;!-- 리터럴 대체 문법으로 간단하게 사용 --\u0026gt; \u0026lt;li\u0026gt;리터럴 대체 |hello ${data}| = \u0026lt;span th:text=\u0026#34;|hello ${data}|\u0026#34;\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;/li\u0026gt; 연산 # HTML 엔티티 사용만 조심 \u0026lt;,\u0026gt;등\nElvis 연산자 데이터가 있으면 데이터 출력, 없으면 뒤문자 출력 ${data}?: '데이터가 없습니다.' = ``` No-Operation _ _인 경우 타임리프가 실행되지 않는것처럼 동작(HTML 내용 그대로) ${data}?: _ = 데이터가 없습니다. ``` 속성 값 설정 # 타임리프는 주로 태그에 th:* 속성을 지정해서 동작 기존에 있으면 대체, 없으면 생성\n속성 추가 대부분 클래스에 쓰일듯? th:attrappend : 속성 값에 추가(뒤) th:attrprepend:속성 값에 추가 (앞) th:classappend:클래스추가\nchecked HTML은 checked라는 속성이 존재하면 무조건 체크됨 그래서 th:checked는 값이 false면 checked속성 자체를 제거해줌\n반복 # th:each를 사용\n반복 기능 \u0026lt;tr th:each=\u0026quot;user : ${users}\u0026quot;\u0026gt;\nList 뿐만이 아니라 배열, Map 등 iterable한것들 사용 가능 ** 반복 상태 유지 ** \u0026lt;tr th:each=\u0026quot;user, userStat : ${users}\u0026quot;\u0026gt; 반복의 2번째 파라미터로 상태 확인 가능 파라미터를 생략하면 지정한 변수명user+Stat이 됨\nindex:0부터 시작하는 값 count:1부터 시작하는 값 size:전체사이즈 even,odd:홀,짝 여부 (boolean) first,last:시작,끝 여부 (boolean) current:현재 객체 조건 # 해당 조건이 맞지 않으면, 태그 자체를 지운다\n\u0026lt;span th:text=\u0026#34;\u0026#39;미성년자\u0026#39;\u0026#34; th:if=\u0026#34;${user.age lt 20}\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;!-- 해당 조건이 거짓이면 아예 사라짐 --\u0026gt; switch *==defalt\n\u0026lt;td th:switch=\u0026#34;${user.age}\u0026#34;\u0026gt; \u0026lt;span th:case=\u0026#34;10\u0026#34;\u0026gt;10살\u0026lt;/span\u0026gt; \u0026lt;span th:case=\u0026#34;20\u0026#34;\u0026gt;20살\u0026lt;/span\u0026gt; \u0026lt;span th:case=\u0026#34;*\u0026#34;\u0026gt;기타\u0026lt;/span\u0026gt; \u0026lt;/td\u0026gt; 주석 # 기본 주석 렌더링할때 제거됨\n\u0026lt;!--/* [[${data}]] */--\u0026gt; 프로토타입 주석 웹에서 열어보면 렌더링 X, 타임리프가렌더링하면 정상처리\n\u0026lt;!--/*/ [${data}] /*/--\u0026gt; 블록 # \u0026lt;th:block\u0026gt; = HTML 태그가 아닌 타임리프의 자체 태그 (유일) 특별한 경우에만 사용 (웬만하면 필요없을 듯?) 그냥 div안에감싸면됨 렌더링시 제거됨\n자바스크립트 인라인 # \u0026lt;script th:inline=\u0026quot;javascript\u0026quot;\u0026gt; 문자타입이면 \u0026quot;\u0026quot;를 너주고, 객체를 JSON으로 반환해주는 등 편함\n템플릿 조각 # footer에서 th:fragment=\u0026quot;이름\u0026quot;으로 조각의 이름을 지정하고\n\u0026lt;!--template/fragment/footer.html --\u0026gt; \u0026lt;footer th:fragment=\u0026#34;copy\u0026#34;\u0026gt; 푸터 자리 입니다. \u0026lt;/footer\u0026gt; \u0026lt;footer th:fragment=\u0026#34;copyParam (param1, param2)\u0026#34;\u0026gt; \u0026lt;p\u0026gt;파라미터 자리 입니다.\u0026lt;/p\u0026gt; \u0026lt;p th:text=\u0026#34;${param1}\u0026#34;\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;p th:text=\u0026#34;${param2}\u0026#34;\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/footer\u0026gt; 지정한 footer를 가져다 씀 insert는 안에 footer가 들어가고, replace는 아예 footer로 대체 (div=\u0026gt;footer)\n\u0026lt;!--template/fragment/fragmentMain.html --\u0026gt; \u0026lt;h2\u0026gt;부분 포함 insert\u0026lt;/h2\u0026gt; \u0026lt;div th:insert=\u0026#34;~{template/fragment/footer :: copy}\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;h2\u0026gt;부분 포함 replace\u0026lt;/h2\u0026gt; \u0026lt;div th:replace=\u0026#34;~{template/fragment/footer :: copy}\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;h2\u0026gt;부분 포함 단순 표현식\u0026lt;/h2\u0026gt; \u0026lt;div th:replace=\u0026#34;template/fragment/footer :: copy\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;h1\u0026gt;파라미터 사용\u0026lt;/h1\u0026gt; \u0026lt;div th:replace=\u0026#34;~{template/fragment/footer :: copyParam (\u0026#39;데이터1\u0026#39;, \u0026#39;데이터2\u0026#39;)}\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; 템플릿 레이아웃 # 위에서는 템플릿 조각을 가져와서 사용했다면, 여기서는 코드 조각을 레이아웃에 넘겨서 사용\nlayoutMain.html common_header(~{::title},~{::link})을 통해서, 밑의 \u0026lt;title\u0026gt;과 \u0026lt;link\u0026gt;를 변수로 넣어준다.\n\u0026lt;html xmlns:th=\u0026#34;http://www.thymeleaf.org\u0026#34;\u0026gt; \u0026lt;head th:replace=\u0026#34;template/layout/base :: common_header(~{::title},~{::link})\u0026#34;\u0026gt; \u0026lt;title\u0026gt;메인 타이틀\u0026lt;/title\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; th:href=\u0026#34;@{/css/bootstrap.min.css}\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; th:href=\u0026#34;@{/themes/smoothness/jquery-ui.css}\u0026#34;\u0026gt; \u0026lt;/head\u0026gt; ** base.html ** 위에서 받은 \u0026lt;title\u0026gt;과 \u0026lt;link\u0026gt;를 대체한다\n\u0026lt;head th:fragment=\u0026#34;common_header(title,links)\u0026#34;\u0026gt; \u0026lt;title th:replace=\u0026#34;${title}\u0026#34;\u0026gt;레이아웃 타이틀\u0026lt;/title\u0026gt; \u0026lt;!-- 공통 --\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; type=\u0026#34;text/css\u0026#34; media=\u0026#34;all\u0026#34; th:href=\u0026#34;@{/css/awesomeapp.css}\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;shortcut icon\u0026#34; th:href=\u0026#34;@{/images/favicon.ico}\u0026#34;\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; th:src=\u0026#34;@{/sh/scripts/codebase.js}\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 추가 --\u0026gt; \u0026lt;th:block th:replace=\u0026#34;${links}\u0026#34; /\u0026gt; \u0026lt;/head\u0026gt; html 자체를 넘기는 것도 가능\n","date":"17 January 2023","externalUrl":null,"permalink":"/posts/velog/060-thymeleaf/","section":"Posts","summary":"프로젝트 생성 # 소개 # 공식문서\n타임리프 특징 # 서버사이드 렌더링 내추럴 템플릿 스프링 통합 지원 타임리프 사용 선언 \u003chtml xmlns:th\"http://www.thymeleaf.org\"\u003e\n텍스트 - text,utext # 기본적으로 HTML 태그 속성에 기능을 정의해서 동작\n","title":"Thymeleaf","type":"posts"},{"content":"","date":"17 January 2023","externalUrl":null,"permalink":"/tags/error/","section":"Tags","summary":"","title":"Error","type":"tags"},{"content":"오늘 하도 서버를 켯다껐다해서 그런가 Port 8080 was already in use 에러가 뜨면서 실행이 안된다. 인터넷에서 해결방안을 찾아서 써논다.\n해결하는 방법 # application.properties에서 server.port=????로 포트 번호를 설정해준다. lsof -i tcp:8080 # 8080포트를 쓰고 있는 프로세스를 찾는다. sudo kill -9 [PID] #로 죽여버린다. *참고 lsof는 list of open file로, 열려있는 파일과 그에대한 정보를 알려준다 리눅스는 모든게 파일임 -i [프로토콜:포트번호]를 주면 특정 포트를 사용하는 프로세스 정보를 출력한다\nkill -9 는 sigkill(강제종료)이다.*\n","date":"17 January 2023","externalUrl":null,"permalink":"/posts/velog/061-port-8080-was-already-in-use/","section":"Posts","summary":"오늘 하도 서버를 켯다껐다해서 그런가 Port 8080 was already in use 에러가 뜨면서 실행이 안된다. 인터넷에서 해결방안을 찾아서 써논다.\n해결하는 방법 # application.properties에서 server.port=????로 포트 번호를 설정해준다. lsof -i tcp:8080 # 8080포트를 쓰고 있는 프로세스를 찾는다. sudo kill -9 [PID] #로 죽여버린다. *참고 lsof는 list of open file로, 열려있는 파일과 그에대한 정보를 알려준다 리눅스는 모든게 파일임 -i [프로토콜:포트번호]를 주면 특정 포트를 사용하는 프로세스 정보를 출력한다\n","title":"Port 8080 was already in use","type":"posts"},{"content":"","date":"15 January 2023","externalUrl":null,"permalink":"/series/springmvc1/","section":"Series","summary":"","title":"SpringMVC1","type":"series"},{"content":" 프로젝트 생성 # 요구사항 분석 # 상품 관리 서비스\n상품 도메인 모델 (ID, 명, 가격, 수량)\n** 서비스 흐름 ** 상품 관리 기능 = CRUD 타임리프 # 타임리프 사용 선언 \u0026lt;html xmlns:th=\u0026quot;http://www.thymeleaf.org\u0026quot;\u0026gt;\n속성 변경- th:href th:href=\u0026quot;@{/css/bootstrap.min.css}\u0026quot;\n원래 href의 값을, th:href의 값으로 변경 Html로 열면 원래값이, 렌더링되면 대체됨 핵심\nth:xxx가 붙으면 서버사이드에서 랜더링되고, 기존것 대체 그냥 열면 웹 브라우저는 th: 속성을 알지 못하므로 무시 URL 링크 표현식 @{...} th:href=\u0026quot;@{/css/bootstrap.min.css}\u0026quot;\nURL링크는 @{...} 사용 (URL링크 표현식) ** 리터럴 대체 |...| **\n타임리프에서 문자와 표현식 등은 더해서 사용해야됨\n결과를 다음과 같이 만들어야 하는데\nlocation.href='/basic/items/add' 그냥 사용하면 문자와 표현식을 각각 따로 더해서 사용해야 하므로 다음과 같이 복잡해진다.\nth:onclick=\u0026quot;'location.href=' + '\\'+@{/basic/items/add} + '\\''\u0026quot; 리터럴 대체 문법을 사용하면 다음과 같이 편리하게 사용할 수 있다.\nth:onclick=\u0026quot;|location.href='@{/basic/items/add}'|\u0026quot; \u0026lt;button class=\u0026#34;btn btn-primary float-end\u0026#34; onclick=\u0026#34;location.href=\u0026#39;addForm.html\u0026#39;\u0026#34; th:onclick=\u0026#34;|location.href=\u0026#39;@{/basic/items/add}\u0026#39;|\u0026#34; type=\u0026#34;button\u0026#34;\u0026gt;상품 등록\u0026lt;/button\u0026gt; 반복 출력 th:each items 컬랙션에서 item 변수를 하나씩 꺼냄\n변수 표현식 ${...} \u0026lt;td th:text=\u0026quot;${item.price}\u0026quot;\u0026gt;10000\u0026lt;/td\u0026gt;\n내용의 값을 th:text의 값으로 변경 프로퍼티 접근법 사용(item.getprice()호출) 링크 표현식2 @{...}\n경로 변수 뿐만 아니라 쿼리 파라미터도 생성\nex)th:href=\u0026quot;@{/basic/items/{itemId}(itemId=${item.id}, query='test')}\u0026quot;\n생성 링크: http://localhost:8080/basic/items/1?query=test 리터럴 대체 문법으로 간단히 사용할 수도\n@ModelAttribute # Item 객체를 생성, 파라미터값을 프로퍼티 접근법으로 입력 그리고 Model에 @ModelAttribute(\u0026quot;이름\u0026quot;)로 지정한 객체를 이름으로 자동으로 너줌\nPRG Post/Redirect/Get # 새로고침은 마지막에 서버에 전송한 데이터를 다시 전송 따라서, 새로고칠때마다 상품데이터를 계속 서버로 전송하게 됨\n리다이렉트는 웹브라우저가 새로운 요청을 하는거임 상품을 저장하고, 리다이렉트를 하면, 새로고침할 때 redirect요청이 들어가기 때문에 문제 해결 가능\n","date":"15 January 2023","externalUrl":null,"permalink":"/posts/velog/062-%EC%8A%A4%ED%94%84%EB%A7%81-mvc-%EC%9B%B9%ED%8E%98%EC%9D%B4%EC%A7%80%EB%A7%8C%EB%93%A4%EA%B8%B0/","section":"Posts","summary":"프로젝트 생성 # 요구사항 분석 # 상품 관리 서비스\n상품 도메인 모델 (ID, 명, 가격, 수량)\n** 서비스 흐름 ** 상품 관리 기능 = CRUD 타임리프 # 타임리프 사용 선언 \u003chtml xmlns:th=\"http://www.thymeleaf.org\"\u003e\n","title":"스프링 MVC-웹페이지만들기","type":"posts"},{"content":" 프로젝트 생성 # build.gradle 파일로 프로젝트 열기\nWelcome Page 스프링 부트에 Jardmf tkdydgkaus /resources/static/index.html/ 자동 처리\n로깅 # 로깅 라이브러리 스프링 부트를 사용하면, spring-boot-logging이 함께 포함됨 기본적으로 SL4J (인터페이스) 의 구현체 Logback을 사용\ntrace\u0026gt;debug\u0026gt;info\u0026gt;warn\u0026gt;error 5레벨 있으며 로깅을 사용해서 특정 수준 이상의 로그만 볼 수 있음 application.properties에서 설정 가능\n효율\nlog.debug(\u0026quot;data\u0026quot;+data) debug레벨을 실행하지 않을 때도, 문자열 합치는 연산 실행 =\u0026gt; 쓸모없는 자원낭비 log.debug(\u0026quot;data={}.data\u0026quot;) (파라미터로 넘기기) 실행하지 않으면 아무 리소스 소비 없음 =\u0026gt; 효율적 로그는 시간, 레벨, PID, 쓰레드명, 클래스명, 메세지 형식으로 출력됨\n요청 매핑 # @RestController\n뷰를 렌더링하지 않고, 반환값이 HTTP메세지 바디에 바로 입력됨 `@RequestMapping(\u0026ldquo;hello-basic\u0026rdquo;)\n/hello-basic URL이 호출되면, 이 메소드 실행 배열로 넣을수도 있음 {?,?} 메소드 속성을 지정하지 않으면, 전부 가능 (post,get \u0026hellip;.) @PathVariable(\u0026quot;userId\u0026quot;) URL경로를 템플릿화\n@GetMapping(\u0026#34;/mapping/{userId}\u0026#34;) public String mappingPath(@PathVariable(\u0026#34;userId\u0026#34;) String data){ log.info(\u0026#34;mappingPath userId={},\u0026#34;,data); return \u0026#34;ok\u0026#34;; /mapping/userA 경로가 호출되었을 때 \u0026quot;userA\u0026quot;를 data로 받음\nparams = \u0026quot;mode=debug\u0026quot;\n쿼리 파라미터에 mode=debug가 있어야 호출됨 headers=\u0026quot;mode=debug\n헤더에 mode=debug 가 있어야 호출 미디어 타입 조건 매핑 (consume=\u0026quot;application/json)\n컨텐츠가 json 타입이여만 호출 기본, 헤더 조회 # HttpServletRequest HttpServletResponse HttpMethod : Http메서드 조회 Locale 로케일 정보 조회 @RequestHeader MultiValueMap\u0026lt;String, String\u0026gt; headerMap 모든 헤더 정보를 맵으로 (Multivalue=중복허용) @RequestHeader(\u0026quot;host\u0026quot;) String host : 특정 헤더 조회 @CookieValue(value = \u0026quot;myCookie\u0026quot;, required = false) String cookie : 쿠키 @RequestParam # 요청 파라미터를 편리하게 받음\n//기본 사용 public String requestParamV2( @RequestParam(\u0026#34;username\u0026#34;) String memberName, @RequestParam(\u0026#34;age\u0026#34;) int memberAge) {... //파라미터 이름과 변수명이 같다면, 다음과 같이 생략 가능 (@RequestParam String username,@requestParam int age) // 단순 타입이면 어노테이션 생략도 가능하지만, 비추 required를 통해 필수값 설정 @ModelAttribute # 요청 파라미터 받기 =\u0026gt; 객체생성 =\u0026gt; 객체에 값 너주기\n를 자동화해주는 기능\npublic String modelAttributeV1(@ModelAttribute HelloData helloData) 동작방식 객체 생성 =\u0026gt; 객체의 프로퍼티 찾음 =\u0026gt; setter 호출 =\u0026gt; 바인딩 (값 입력)\n요청 메세지 # Httpmessage body에 직접 데이터를 담아서 요청\nHttp API에서 주로 사용 (json,text,xml) POST, PUT, PATCH 메시지 바디를 통해서 데이터가 직접 넘어오면@RequestParam,@modelAttribute 사용 불가 (HTML Form 제외)\n단순 텍스트 # InputStream으로 읽고, Writer.write()으로 써서 전송 HttpEntity\u0026lt;String\u0026gt; 사용 @PostMapping(\u0026#34;/request-body-string-v3\u0026#34;) public HttpEntity\u0026lt;String\u0026gt; requestBodyStringV3(HttpEntity\u0026lt;String\u0026gt; httpEntity) { String messageBody = httpEntity.getBody(); log.info(\u0026#34;messageBody={}\u0026#34;, messageBody); return new HttpEntity\u0026lt;\u0026gt;(\u0026#34;ok\u0026#34;); } HttpEntity : header, body 정보 편리하게 조회\n메시지 바디 정보를 직접 조회 파라미터 조회 기능과 관계 X 응답에도 사용 가능\n바디 정보 직접 반환 헤더 정보 포함 가능 view로 넘어가지 않음 HttpEntity를 상속받은 개체들\nRequestEntity:HttpMethod, url정보추가, 요청 ResponseEntity : Http상태 코드 설정, 응답 @RequestBody # 앞의 과정은 필요없이, 메세지 바디 직접 조회\npublic HttpEntity\u0026lt;String\u0026gt; requestBodyStringV4(@RequestBody String messageBody) @ResponseBody # 응답 결과를 Http 메시지 바디에 직접 담아서 전달 (view 사용 X)\nJSON # ObjectMapper를 통해서 Json=\u0026gt;객체변환하는 방법이 있고, (@RquestBody HelloData data)처럼 직접 만든 객체를 지정할 수도 있다.\nHttpEntity, @RequestBody등을 사용하면, Http 메시지 컨버터가 우리가 원하는 문자나 객체로 변환\n@ResponseBody @PostMapping(\u0026#34;/request-body-json-v3\u0026#34;) public String requestBodyJsonV3(@RequestBody HelloData helloData) throws IOException { log.info(\u0026#34;username={},age={}\u0026#34;,helloData.getUsername(),helloData.getAge()); return \u0026#34;ok\u0026#34;; } *생략하면 @ModelAttribute\t로 인식하기 때문에 생략 X HttpEntity\u0026lt;HelloData\u0026gt; 로도 꺼낼 수 있음\n응답 # 스프링에서 응답 데이터를 만드는 3가지 방법\n정적 리소스 (파일 그대로 전달) 뷰 템플릿 사용 (동적인 HTML) Http 메시지 사용 Http 메시지 바디에 Json등의 형식으로 데이터를 실어 보냄 정적 리소스 # src/main/resources/static에 리소스를 넣어두면, 정적 리소스 제공\n뷰 템플릿 # 뷰 템플릿을 거쳐서 HTML이 생성되고, 뷰가 응답을 만들어서 전달 기본 템플릿 경로 = src/main/resources/templates\nString: View | Http메시지 Void : 요청 URL을 논리 뷰 이름으로 Thymeleaf 설정 # build.gradle\nimplementation \u0026#39;org.springframework.boot:spring-boot-starter-thymeleaf\u0026#39; 기본설정\nspring.thymleaf.prefitx=classpath:/templates/ spring.thymeleaf.suffix=.html HTTP 메시지 컨버터 # ResponseBody 원리 스프링에서 Http메시지 컨버터 적용 경우\n요청 : @RequestBody,HttpEntity(RequestEntity) 응답 : @ResponseBody,\u0026rsquo;HttpEntity(ResponseEntity) 요청 매핑 핸들러 어댑터 구조 # RequestMappingHandlerAdapter 등작 방식 ArgumentResolver 어노테이션 기반의 컨트롤러가 Model,@RequestParam\u0026hellip;.등 여러 파라미터를 처리할 수 있는 이유가 ArugmentResolver덕분임 Http메세지 컨버터가 여기에 포함됨\nReturnValueHandler ArgumentResolver와 비슷한데, 응답값 반환,처리 ex) 뷰 이름을 String으로 반환해도 동작\n","date":"13 January 2023","externalUrl":null,"permalink":"/posts/velog/063-%EC%8A%A4%ED%94%84%EB%A7%81mvc-%EA%B8%B0%EB%B3%B8-%EA%B8%B0%EB%8A%A5/","section":"Posts","summary":"프로젝트 생성 # build.gradle 파일로 프로젝트 열기\nWelcome Page 스프링 부트에 Jardmf tkdydgkaus /resources/static/index.html/ 자동 처리\n로깅 # 로깅 라이브러리 스프링 부트를 사용하면, spring-boot-logging이 함께 포함됨 기본적으로 SL4J (인터페이스) 의 구현체 Logback을 사용\ntrace\u003edebug\u003einfo\u003ewarn\u003eerror 5레벨 있으며 로깅을 사용해서 특정 수준 이상의 로그만 볼 수 있음 application.properties에서 설정 가능\n","title":"스프링MVC-기본 기능","type":"posts"},{"content":" 스프링 MVC 전체 구조 # 이전에 만들었던 서블릿 프레임워크와 같은 구조이다!!\nDispatcherServlet # 스프링 MVC의 프론트 컨트롤러가 **DispatcherServlet**이다.\n부모 클래스에서 HttpServlet을 상속받아서 사용하고, 서블릿으로 동작 스프링은 DispatcherServlet을 서블릿으로 자동등록하며, 모들 경로에 대해서 매핑 요청 흐름\n서블릿이 호출되면, HttpServlet의 service()가 호출됨 스프링은 DispatcherServlet의 부모인 FrameworkServlet.service()가 오버라이딩 되있음 FrameworkServlet.service()를 시작으로, 여러 메서드가 호출되며 DispatcherServlet.doDispatch()가 호출 MVC 시작 # 현재 스프링 컨트롤러는 99,9% 어노테이션 기반으로 동작\n@RequestMapping\nRequestMappingHandlerMapping 핸들러 매핑 RequestMappingHandlerAdapter 핸들러 어뎁터 기본 설명\n@Controller:\n스프링이 자동으로 스프링 빈으로 등록 (내부에 @Component가 있어서 컴포넌트 스캔의 대상) 스프링 MVC에서 어노테이션 기반 컨트롤러로 인식 @RequestMapping:요청 정보를 매핑, 해당 URL이 호출되면 이 메서드 호출됨\nModelAndView: 모델과 뷰 정보를 담아서 반환\n컨트롤러 통합 # 클래스 레벨 @RequestMapping 과 메소드 레벨 @RequestMapping 을 조합해서, 하나의 컨트롤러로 사용 (클래스레벨+메소드레벨)\n실용적인 패턴 # 처음에는 @RequestMapping과 RequestMethod.[method]를 통해서 메소드 제약\n후에는 @GetMapping,@PostMapping을 통해 메소드 제약\n","date":"12 January 2023","externalUrl":null,"permalink":"/posts/velog/064-%EC%8A%A4%ED%94%84%EB%A7%81mvc-%EA%B5%AC%EC%A1%B0/","section":"Posts","summary":"스프링 MVC 전체 구조 # 이전에 만들었던 서블릿 프레임워크와 같은 구조이다!!\nDispatcherServlet # 스프링 MVC의 프론트 컨트롤러가 **DispatcherServlet**이다.\n부모 클래스에서 HttpServlet을 상속받아서 사용하고, 서블릿으로 동작 스프링은 DispatcherServlet을 서블릿으로 자동등록하며, 모들 경로에 대해서 매핑 요청 흐름\n서블릿이 호출되면, HttpServlet의 service()가 호출됨 스프링은 DispatcherServlet의 부모인 FrameworkServlet.service()가 오버라이딩 되있음 FrameworkServlet.service()를 시작으로, 여러 메서드가 호출되며 DispatcherServlet.doDispatch()가 호출 MVC 시작 # 현재 스프링 컨트롤러는 99,9% 어노테이션 기반으로 동작\n","title":"스프링MVC 구조","type":"posts"},{"content":" 프런트 컨트롤러 패턴 # 특징\n서블릿 하나로 요청을 받음 프런트 컨트롤러가 요청에 맞는 컨트롤러 호출 (입구 하나) 나머지 컨트롤러는 서블릿 사용 ❌ v1 # 기본 구조는 그대도되, Front Controller 추가 v2 # View 분리 V3 # 서블릿 종속성 제거\n요청 파라미터 정보는 자바 Map으로 대신 넘김 request 객체를 Model로 쓰지 않고 별도의 Model 객체 반환 View 이름 중복 제거\n컨트롤러는 논리이름만\t반환, 물리 위치는 프론트 컨트롤러에서 처리 V4 # 기본적인 구조는 V3과 같지만, 컨트롤러가 viewname만 반환 모델객체는 파라미터로 전달, 뷰 이름만 return 개발자 입장에서 군더더기 없는 코드\nV5 (유연한 컨트롤러) # 지금까지는 한가지 컨트롤러 인터페이스만 사용했는데 A는 v3, B는v4 방식으로 개발하고 싶다면?\n어뎁터 패턴을 사용\n핸들러 어댑터 : 중간에 어댑터 역활을 하는 어댑터\n핸들러 : 해당하는 어댑터만 있으면 다 처리할 수 있기 때문에 컨트롤러의 이름을 더 넓은 핸들러로 변경\nInstanceof를 통해 bool 반환\n스프링 MVC는 이 과정과 거의 같은 구조를 가지고 있다\n","date":"11 January 2023","externalUrl":null,"permalink":"/posts/velog/065-mvc-%EB%A7%8C%EB%93%A4%EA%B8%B0/","section":"Posts","summary":"프런트 컨트롤러 패턴 # 특징\n서블릿 하나로 요청을 받음 프런트 컨트롤러가 요청에 맞는 컨트롤러 호출 (입구 하나) 나머지 컨트롤러는 서블릿 사용 ❌ v1 # 기본 구조는 그대도되, Front Controller 추가 ","title":"MVC 만들기","type":"posts"},{"content":" 템플릿 엔진 # 서블릿과 자바 코드만으로 HTML을 동적으로 만들 수 있다. 하지만\nPrintWriter w = response.getWriter(); w.write(\u0026#34;\u0026lt;html\u0026gt;\u0026#34;); w.write(\u0026#34;\u0026lt;head\u0026gt;\u0026#34;); w.write(\u0026#34; \u0026lt;meta charset=\\\u0026#34;UTF-8\\\u0026#34;\u0026gt;\u0026#34;); w.write(\u0026#34; \u0026lt;title\u0026gt;Title\u0026lt;/title\u0026gt;\u0026#34;); w.write(\u0026#34;\u0026lt;/head\u0026gt;\u0026#34;); w.write(\u0026#34;\u0026lt;body\u0026gt;\u0026#34;); w.write(\u0026#34;\u0026lt;a href=\\\u0026#34;/index.html\\\u0026#34;\u0026gt;메인\u0026lt;/a\u0026gt;\u0026#34;); w.write(\u0026#34;\u0026lt;table\u0026gt;\u0026#34;); w.write(\u0026#34; \u0026lt;thead\u0026gt;\u0026#34;); w.write(\u0026#34; \u0026lt;th\u0026gt;id\u0026lt;/th\u0026gt;\u0026#34;); w.write(\u0026#34; \u0026lt;th\u0026gt;username\u0026lt;/th\u0026gt;\u0026#34;); 이런 짓은 사람이 할게 아니다.\n따라서, 자바 코드에서 HtTML을 만들지 않고 HTML 문서에 자바 코드를 넣어서 동적 HTML을 만들게 되었고, 이게 템플릿 엔진이다. (JSP,Thymeleaf등)\nJSP # 이전보다는 편하지만, HTML과 자바 코드가 섞여 지저분 따라서 MVC패턴이 등장\n비즈니스 로직은 다른 곳에서 처리 JSP는 화면만그리게 MVC 패턴 # 변경 주기 UI의 수정과 비즈니스 로직의 수정은 별개이므로, 분리해서 관리\nModel\n뷰에 출력할 데이터를 담아두는 역활 View\n모델에 담겨있는 데이터로 화면을 랜더링(HTML) Controller\nHttp 요청을 받아서 검증하고, 비즈니스 로직 실행 뷰에 전달할 데이터를 모델에 담음 Service\n컨트롤러에서 전부 다 담당하기 어려울경우에, 서비스에서 비즈니스 로직을 담당 request.setAttribute()로 값을 담고 request.setAttribute()로 값을 꺼낸다.\nMVC 적용 # dispatcher.forward() 통해서 제어권 넘김\n@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String viewPath = \u0026#34;/WEB_INF/views/new-form.jsp\u0026#34;; RequestDispatcher dispatcher = request.getRequestDispatcher(); dispatcher.forward(request,response); } WEB-INF 이 경로 안의 파일은, 외부에서 직접 호출 불가(무조건 컨트롤러를 거쳐서)\nRedirect VS Forward # Redirect는 실제 클라에 응답이 나갔다가, 클라가 redirect 경로로 다시 요청 Forward는 서버 내부에서 호출하기 때문에, 클라가 인지 못함\nMVC 한계 # 공통되는 부분이 많음 공통 처리가 어려움 이런 단점들을 해결하기 위해, 프론트 컨트롤러 패턴 등장\n","date":"10 January 2023","externalUrl":null,"permalink":"/posts/velog/066-jsp--mvc/","section":"Posts","summary":"템플릿 엔진 # 서블릿과 자바 코드만으로 HTML을 동적으로 만들 수 있다. 하지만\nPrintWriter w = response.getWriter(); w.write(\"\u003chtml\u003e\"); w.write(\"\u003chead\u003e\"); w.write(\" \u003cmeta charset=\\\"UTF-8\\\"\u003e\"); w.write(\" \u003ctitle\u003eTitle\u003c/title\u003e\"); w.write(\"\u003c/head\u003e\"); w.write(\"\u003cbody\u003e\"); w.write(\"\u003ca href=\\\"/index.html\\\"\u003e메인\u003c/a\u003e\"); w.write(\"\u003ctable\u003e\"); w.write(\" \u003cthead\u003e\"); w.write(\" \u003cth\u003eid\u003c/th\u003e\"); w.write(\" \u003cth\u003eusername\u003c/th\u003e\"); 이런 짓은 사람이 할게 아니다.\n따라서, 자바 코드에서 HtTML을 만들지 않고 HTML 문서에 자바 코드를 넣어서 동적 HTML을 만들게 되었고, 이게 템플릿 엔진이다. (JSP,Thymeleaf등)\n","title":"JSP \u0026 MVC","type":"posts"},{"content":" 프로젝트 생성 # 서블릿은 스프링이 필요없지만, 환경설정이 잘 되 있어 스프링 사용 spring boot starter 에서 생성 다운 후, build.gradle 선택해서 열기\nHello 서블릿 # @ServeltComponentScan을 통하여 스프링에서 서블릿 자동 등록\n모든 서블렛은 javax.servlet.http.HttpServlet를 상속받음 @Webservlet으로 url mapping 요칭이 오면, 오버라이딩된 service메소드 실행\nwelcome page # /main/webapp경로에 있는 index.html\nHttpServletRequest # 서벌릿은 개발자 대신 Http 요청메세지를 파싱 =\u0026gt;HttpServletRequest 객체로 제공\nHttp요청 데이터 # GET url에 데이터를 포함해서 전달 (검색,페이징 등) POST content-type:application/x-www-form-urlencoded 메시지 바디에 쿼리 파라미터 형식으로 전달 Http message body body에 직접 데이터를 담아서 요청(json, POST,PUT,PATCH GET # 전체 param 조회 request.getParameterNames().asIterator() .forEachRemaining(paramName -\u0026gt; System.out.println(paramName + \u0026#34;=\u0026#34; + request.getParameter(paramName))) 단일 param 조회 request.getParameter([값]) 복수 param 조회 request.getParameterValues([값]) //enumeration 반환(리스트) POST # request.getParameter()는 GET,POST 둘다 지원 폼만들기 귀찮을때 POSTMAN 어플 사용 API Message 방식 # ttp message body에 데이터를 직접 담아서 요청 단순 텍스트 # ServletInputStream inputStream = request.getInputStream(); //바이트로 꺼내기 String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); //인코딩 JSON # json도 결국 문자열임 부트에선ObjectMapper를 통해 객체로 변경 HttpServletResponse # Http응답 메세지 생성 응답코드, header, body 편의기능(Content-type, cookie, Rediret)\n헤더 세팅 예제\nprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //상태코드 response.setStatus(HttpServletResponse.SC_OK); //헤더 response.setHeader(\u0026#34;Content-Type\u0026#34;,\u0026#34;text/plain;charset=utf-8\u0026#34;); response.setHeader(\u0026#34;Cache-Control\u0026#34;, \u0026#34;no-cache, no-store, must- revalidate\u0026#34;); response.setHeader(\u0026#34;pragma\u0026#34;,\u0026#34;no-cache\u0026#34;); response.setHeader(\u0026#34;my-header\u0026#34;,\u0026#34;hello\u0026#34;); //나만의 헤더 response.getWriter().println(\u0026#34;Ok\u0026#34;); } 이외에도 편의기능 세팅 가능\n응답 데이터 # 단순텍스트 HTML MessageBody Json API Json # response.setContentType(\u0026quot;application/json\u0026quot;); ObjectMapper를 통하여 객체를 json으로 ","date":"4 January 2023","externalUrl":null,"permalink":"/posts/velog/067-%EC%84%9C%EB%B8%94%EB%A6%BF/","section":"Posts","summary":"프로젝트 생성 # 서블릿은 스프링이 필요없지만, 환경설정이 잘 되 있어 스프링 사용 spring boot starter 에서 생성 다운 후, build.gradle 선택해서 열기\nHello 서블릿 # @ServeltComponentScan을 통하여 스프링에서 서블릿 자동 등록\n모든 서블렛은 javax.servlet.http.HttpServlet를 상속받음 @Webservlet으로 url mapping 요칭이 오면, 오버라이딩된 service메소드 실행\n","title":"서블릿","type":"posts"},{"content":" 웹서버, WAS # 웹 서버 # Http 기반으로 정적 리소스들을 주고받는 서버\nWAS (web application server) # 웹 서버의 기능을 포함하며, 프로그램 코드를 실행해 애플리케이션 로직 수행 자바는 서벌릿 컨테이너 기능을 제공하면 WAS (ex.tomcat)\n정적 리소느는 웹 서버가 처리하고, 동적인 처리가 필요하면 WAS에 위임 서블릿 # Http 요청이 들어오면\nWAS가 Request,Response 객체를 만들어 서블릿 호출 개발자는 Request 객체에서 정보를 꺼내서 사용 \u0026amp; Response 객체에 응답 정보 입력 WAS는 Response객체로 Http 응답 생성 서블릿 컨테이너 # 서블릿을 지원하는 WAS (ex.tomcat) 서블릿 생명주기 관리 싱글톤으로 관리 (오직 1개의 객체) 최초 로딩 시점에 객체를 만들어놓고 재활용 모든 요청은 동일한 서블릿 객체 인스턴스에 접근 멀티 쓰레드 지원 다중요청 (쓰레드) # 쓰레드 풀을 이용해서 사용 쓰레드는 풀에 이미 생성되어 있으며, 필요할 때 꺼내서 쓰고 반납 만일 모든 쓰레드가 사용중이여면, 요청을 거절하거나 대기 쓰레드 생성 비용 절약, 빠름, 안전\nmax thread # 높으면? =\u0026gt; 서버는 여유롭지만, 클라이언트 응답 지연 낮으면? =\u0026gt; 서버 리소스 죽어감 WAS의 지원 # 멀티 쓰레드는 WAS가 처리 (내가 신경 X) 싱글톤 객체는 주의해서 사용 ","date":"4 January 2023","externalUrl":null,"permalink":"/posts/velog/068-%EC%9B%B9-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98/","section":"Posts","summary":"웹서버, WAS # 웹 서버 # Http 기반으로 정적 리소스들을 주고받는 서버\nWAS (web application server) # 웹 서버의 기능을 포함하며, 프로그램 코드를 실행해 애플리케이션 로직 수행 자바는 서벌릿 컨테이너 기능을 제공하면 WAS (ex.tomcat)\n","title":"웹 어플리케이션","type":"posts"},{"content":"인프런 할인 이벤트를 보고, 눈여겨보고 있었던 김영한님의 스프링 MVC강의 1~2편을 다 샀다.\n열심히 공부해서 다른 사람의 도움을 받지 않고, 나만의 포트폴리오를 만들 생각이다\n앞으로 이 시리즈에 강의내용을 정리해서 올릴 예정이다. 화이팅!\n","date":"4 January 2023","externalUrl":null,"permalink":"/posts/velog/069-init/","section":"Posts","summary":"인프런 할인 이벤트를 보고, 눈여겨보고 있었던 김영한님의 스프링 MVC강의 1~2편을 다 샀다.\n열심히 공부해서 다른 사람의 도움을 받지 않고, 나만의 포트폴리오를 만들 생각이다\n앞으로 이 시리즈에 강의내용을 정리해서 올릴 예정이다. 화이팅!\n","title":"Init","type":"posts"},{"content":" 부족한 부분 # 파일 데이터를 다루는 코드가 잘 이해가 안됨 jquery 내부에서의 오류 (이건 못고침) 설정 관련 헷갈리는게 너무나 많음 느낀점 # 그래도 어케 돌아가는지는 알거같다. 생각보다 웹 프레임워크가 거기서 거기인듯? 분명 엄청 방대한 (나한테는) 분량임에도 불구하고 하나하나 짚어가면 그렇게 어렵지 않다. 앞으로 # 인프런 세일 광고를 보고 김영한스프링 강의를 큰맘먹고 등록했다. 이 강의는 스프링부트를 쓰는거긴 하지만, 이것까지 들으면서 좀더 이해력을 키워나갈 예정이다\n","date":"3 January 2023","externalUrl":null,"permalink":"/posts/velog/070-5.-%EB%A7%88%EC%B9%98%EB%A9%B0/","section":"Posts","summary":"부족한 부분 # 파일 데이터를 다루는 코드가 잘 이해가 안됨 jquery 내부에서의 오류 (이건 못고침) 설정 관련 헷갈리는게 너무나 많음 느낀점 # 그래도 어케 돌아가는지는 알거같다. 생각보다 웹 프레임워크가 거기서 거기인듯? 분명 엄청 방대한 (나한테는) 분량임에도 불구하고 하나하나 짚어가면 그렇게 어렵지 않다. 앞으로 # 인프런 세일 광고를 보고 김영한스프링 강의를 큰맘먹고 등록했다. 이 강의는 스프링부트를 쓰는거긴 하지만, 이것까지 들으면서 좀더 이해력을 키워나갈 예정이다\n","title":"5. 마치며","type":"posts"},{"content":"","date":"3 January 2023","externalUrl":null,"permalink":"/series/firstspring/","section":"Series","summary":"","title":"FIrstSPring","type":"series"},{"content":" 공지사항과, 공지사항 첨부 파일 (1:n) # 테이블 생성 # CREATE TABLE TBL_NOTICE ( NOTICE_NO NUMBER NOT NULL , TITLE VARCHAR2(200) NOT NULL , CONTENT VARCHAR2(4000) NOT NULL , REGISTER VARCHAR2(90) NOT NULL , REG_DATE DATE NOT NULL , PRIMARY KEY (NOTICE_NO) ) ; CREATE TABLE TBL_NOTICE_FILE ( NOTICE_FILE_NO NUMBER NOT NULL , NOTICE_FILE_NAME VARCHAR2(300) NOT NULL , NOTICE_NO NUMBER NOT NULL , PRIMARY KEY (NOTICE_FILE_NO) ); CREATE SEQUENCE SEQ_NOTICE INCREMENT BY 1 START WITH 1; CREATE SEQUENCE SEQ_NOTICE_FILE INCREMENT BY 1 START WITH 1; //FK설정 ALTER TABLE TBL_NOTICE_FILE ADD CONSTRAINT NOTICE_FILE_NOTICE_NO FOREGIN KEY (NOTICE_NO) REFERENCE TBL_NOTICE(NOTICE_NO) pom.xml 설정 # \u0026lt;!-- START multi file upload --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.imgscalr\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;imgscalr-lib\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;4.2\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;commons-fileupload\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;commons-fileupload\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.3.2\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- END multi file upload --\u0026gt; \u0026lt;!-- https://mvnrepository.com/artifact/org.webjars/jquery --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.webjars\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jquery\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;3.3.1\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; root-context.xml 설정 (파일 최대크기) # \u0026lt;!-- file upload로 들어오는 data를 처리하는 객체 --\u0026gt; \u0026lt;bean id=\u0026#34;multipartResolver\u0026#34; class=\u0026#34;org.springframework.web.multipart.commons.CommonsMultipartResolver\u0026#34;\u0026gt; \u0026lt;property name=\u0026#34;maxUploadSize\u0026#34; value=\u0026#34;10485760\u0026#34;\u0026gt;\u0026lt;/property\u0026gt; \u0026lt;/bean\u0026gt; servlet-context.xml bean 등록 설정 # \u0026lt;!--파일 업로드 관련 Beans 설정 --\u0026gt; \u0026lt;beans:bean id=\u0026#34;multipartResolver\u0026#34; class=\u0026#34;org.springframework.web.multipart.commons.CommonsMultipartResolver\u0026#34;\u0026gt; \u0026lt;beans:property name=\u0026#34;maxUploadSize\u0026#34; value=\u0026#34;10485760\u0026#34;\u0026gt;\u0026lt;/beans:property\u0026gt; \u0026lt;/beans:bean\u0026gt; \u0026lt;!-- 서버의 파일 저장 경로 설정 --\u0026gt; \u0026lt;beans:bean id=\u0026#34;uploadPath\u0026#34; class=\u0026#34;java.lang.String\u0026#34;\u0026gt; \u0026lt;beans:constructor-arg value=\u0026#34;C:\\\\resources\\\\upload\u0026#34;\u0026gt;\u0026lt;/beans:constructor-arg\u0026gt; \u0026lt;/beans:bean\u0026gt; 이후 # 이전과 같이, NoticeDAO (persistence) =\u0026gt; noticeMapper =\u0026gt; NoticeEDAOImpl =\u0026gt; Service 순으로 코딩해 나간다\n","date":"3 January 2023","externalUrl":null,"permalink":"/posts/velog/071-4.-%EC%B2%A8%EB%B6%80%ED%8C%8C%EC%9D%BC-%EB%B0%8F-%EB%8C%93%EA%B8%80-%EA%B8%B0%EB%8A%A5/","section":"Posts","summary":"공지사항과, 공지사항 첨부 파일 (1:n) # 테이블 생성 # CREATE TABLE TBL_NOTICE ( NOTICE_NO NUMBER NOT NULL , TITLE VARCHAR2(200) NOT NULL , CONTENT VARCHAR2(4000) NOT NULL , REGISTER VARCHAR2(90) NOT NULL , REG_DATE DATE NOT NULL , PRIMARY KEY (NOTICE_NO) ) ; CREATE TABLE TBL_NOTICE_FILE ( NOTICE_FILE_NO NUMBER NOT NULL , NOTICE_FILE_NAME VARCHAR2(300) NOT NULL , NOTICE_NO NUMBER NOT NULL , PRIMARY KEY (NOTICE_FILE_NO) ); CREATE SEQUENCE SEQ_NOTICE INCREMENT BY 1 START WITH 1; CREATE SEQUENCE SEQ_NOTICE_FILE INCREMENT BY 1 START WITH 1; //FK설정 ALTER TABLE TBL_NOTICE_FILE ADD CONSTRAINT NOTICE_FILE_NOTICE_NO FOREGIN KEY (NOTICE_NO) REFERENCE TBL_NOTICE(NOTICE_NO) pom.xml 설정 # \u003c!-- START multi file upload --\u003e \u003cdependency\u003e \u003cgroupId\u003eorg.imgscalr\u003c/groupId\u003e \u003cartifactId\u003eimgscalr-lib\u003c/artifactId\u003e \u003cversion\u003e4.2\u003c/version\u003e \u003c/dependency\u003e \u003cdependency\u003e \u003cgroupId\u003ecommons-fileupload\u003c/groupId\u003e \u003cartifactId\u003ecommons-fileupload\u003c/artifactId\u003e \u003cversion\u003e1.3.2\u003c/version\u003e \u003c/dependency\u003e \u003c!-- END multi file upload --\u003e \u003c!-- https://mvnrepository.com/artifact/org.webjars/jquery --\u003e \u003cdependency\u003e \u003cgroupId\u003eorg.webjars\u003c/groupId\u003e \u003cartifactId\u003ejquery\u003c/artifactId\u003e \u003cversion\u003e3.3.1\u003c/version\u003e \u003c/dependency\u003e root-context.xml 설정 (파일 최대크기) # \u003c!-- file upload로 들어오는 data를 처리하는 객체 --\u003e \u003cbean id=\"multipartResolver\" class=\"org.springframework.web.multipart.commons.CommonsMultipartResolver\"\u003e \u003cproperty name=\"maxUploadSize\" value=\"10485760\"\u003e\u003c/property\u003e \u003c/bean\u003e servlet-context.xml bean 등록 설정 # \u003c!--파일 업로드 관련 Beans 설정 --\u003e \u003cbeans:bean id=\"multipartResolver\" class=\"org.springframework.web.multipart.commons.CommonsMultipartResolver\"\u003e \u003cbeans:property name=\"maxUploadSize\" value=\"10485760\"\u003e\u003c/beans:property\u003e \u003c/beans:bean\u003e \u003c!-- 서버의 파일 저장 경로 설정 --\u003e \u003cbeans:bean id=\"uploadPath\" class=\"java.lang.String\"\u003e \u003cbeans:constructor-arg value=\"C:\\\\resources\\\\upload\"\u003e\u003c/beans:constructor-arg\u003e \u003c/beans:bean\u003e 이후 # 이전과 같이, NoticeDAO (persistence) =\u003e noticeMapper =\u003e NoticeEDAOImpl =\u003e Service 순으로 코딩해 나간다\n","title":"4. 첨부파일 및 댓글 기능","type":"posts"},{"content":" 로그인 기능 구현 # 유저 테이블 생성 # CREATE TABLE TBL_USER ( USID VARCHAR2(50) NOT NULL, UPW VARCHAR2(50) NOT NULL, UNAME VARCHAR2(100) NOT NULL, UPOINT NUMBER DEFAULT 0 NOT NULL, PRIMARY KEY (USID) ); UserVO, LoginDTO 처리 # VO와 DTO의 차이점?\nDTO는 화면에 이용, VO는 테이블 구조를 이용하는 경우\nUserDAO 생성 및 SQL # persistence 패키지에 UserDAO 생성 sql을 담당할 userMapper 생성 userMapper을 사용해 UserDAO 구현체 (Impl)생성 Interceptor # Interceptor란?\n말 그대로 스프링에서 목적지를 가로채는 것 HandlerInterceptorAdapter 클래스를 상속받아 구현\ntrue를 리턴하면 되돌아가고, false를 리턴하면 다음단계로 이동\n/WEB-INFO/Spring/appServlet/servlet-context.xml 에서 인터프리터가 작동할 url 설정\nLoginInterceptor # 로그인 페이지로 이동하면 작동\npreHandle에서 session에 저장되있는 로그인 데이터가 있다면 삭제 이후 postHandle에서로그인 정보를 확인하고 세션 영역에 로그인 정보 저장 3.만일 세션 영역에 이전 페이지의 정보가 있을 경우, 거기로 이동 AuthAuthInterceptor # 인증이 필요할 때 작동 1.preHandle에서 로그인 정보를 읽으려고 시도, 있으면 리턴 아니면 다음단계로 2. request.getURI(),request,getQueryString()을 이용해 현재 위치 세션에 저장=\u0026gt; LoginInterceptor로 이동\n","date":"3 January 2023","externalUrl":null,"permalink":"/posts/velog/072-3.1-%EB%A1%9C%EA%B7%B8%EC%9D%B8/","section":"Posts","summary":"로그인 기능 구현 # 유저 테이블 생성 # CREATE TABLE TBL_USER ( USID VARCHAR2(50) NOT NULL, UPW VARCHAR2(50) NOT NULL, UNAME VARCHAR2(100) NOT NULL, UPOINT NUMBER DEFAULT 0 NOT NULL, PRIMARY KEY (USID) ); UserVO, LoginDTO 처리 # VO와 DTO의 차이점?\n","title":"3.1 로그인","type":"posts"},{"content":"간단한 계시판 만들기\n개발 목표 # 등록 화면 구현, DB에 등록 등록 결과 확인, 목록 이동, 전체 목록 상세보기 수정페이지 이동, DB 수정 삭제 페이징, 검색 DB생성 # CREATE TABLE TBL_BOARD ( BNO NUMBER NOT NULL, TITLE VARCHAR2(200) NOT NULL, CONTENT VARCHAR2(1000) NOT NULL, WRITER VARCHAR2(50) NOT NULL, REGDATE DATE DEFAULT SYSDATE, VIEWCNT NUMBER DEFAULT 0, PRIMARY KEY (BNO) ); CREATE SEQUENCE SEQ_BOARD INCREMENT BY 1 START WITH 1; 작업 순서 # 테이블을 담을 BoardVO 클래스 작성 (domain) BoardDAO인터페이스 작성 (persistence) BoardMapper.xml 작성 (mapper) BoardDAOImpl 구현체 작성 (persistence) BoardService, 구현체 작성 (서비스는 생략가능) Boardcontroller에서, jsp와 서비스를 이어줌 페이징, 검색 # 페이징 # 오라클이면 rownum, mysql이면 limit을 이용해 구현\n검색 # AND TITLE LIKE \u0026#39;%\u0026#39; || #{keyword} || \u0026#39;%\u0026#39; 이런 색으로 like문법을 이용해서 구현\n","date":"3 January 2023","externalUrl":null,"permalink":"/posts/velog/073-3-crud/","section":"Posts","summary":"간단한 계시판 만들기\n개발 목표 # 등록 화면 구현, DB에 등록 등록 결과 확인, 목록 이동, 전체 목록 상세보기 수정페이지 이동, DB 수정 삭제 페이징, 검색 DB생성 # CREATE TABLE TBL_BOARD ( BNO NUMBER NOT NULL, TITLE VARCHAR2(200) NOT NULL, CONTENT VARCHAR2(1000) NOT NULL, WRITER VARCHAR2(50) NOT NULL, REGDATE DATE DEFAULT SYSDATE, VIEWCNT NUMBER DEFAULT 0, PRIMARY KEY (BNO) ); CREATE SEQUENCE SEQ_BOARD INCREMENT BY 1 START WITH 1; 작업 순서 # 테이블을 담을 BoardVO 클래스 작성 (domain) BoardDAO인터페이스 작성 (persistence) BoardMapper.xml 작성 (mapper) BoardDAOImpl 구현체 작성 (persistence) BoardService, 구현체 작성 (서비스는 생략가능) Boardcontroller에서, jsp와 서비스를 이어줌 페이징, 검색 # 페이징 # 오라클이면 rownum, mysql이면 limit을 이용해 구현\n","title":"3 CRUD","type":"posts"},{"content":" 코딩 순서 # 테이블 생성 및 개발 준비 도메인 객체의 설계 및 클래스 작성 DAO 인터페이스 작성 XML Mapper의 생성과 SQL문 작성 MyBatis에서 작성된 Mapper 인식하도록 설정 DAO 구현 (DAOImpl) XML Mapper # Mybatis에서 SQL문을 저장하는 존재 src/main/java/resources/mappers폴더에 저장\nroot-context.xml 추가 # XML Mapper 인식 위해 설정 추가\n\u0026lt;bean id=\u0026#34;sqlSessionFactory\u0026#34; class=\u0026#34;org.mybatis.spring.SqlSessionFactoryBean\u0026#34;\u0026gt; \u0026lt;property name=\u0026#34;dataSource\u0026#34; ref=\u0026#34;dataSource\u0026#34; /\u0026gt; \u0026lt;property name=\u0026#34;configLocation\u0026#34; value=\u0026#34;classpath:/mybatisconfig.xml\u0026#34;\u0026gt;\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;mapperLocations\u0026#34; value=\u0026#34;classpath:mappers/**/*Mapper.xml\u0026#34;\u0026gt;\u0026lt;/property\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;bean id=\u0026#34;sqlSession\u0026#34; class=\u0026#34;org.mybatis.spring.SqlSessionTemplate\u0026#34; destroy-method=\u0026#34;clearCache\u0026#34;\u0026gt; \u0026lt;constructor-arg name=\u0026#34;sqlSessionFactory” ref=\u0026#34;sqlSessionFactory\u0026#34;\u0026gt; \u0026lt;/constructor-arg\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;context:component-scan base-package=\u0026#34;com.mis.persistence\u0026#34;\u0026gt; \u0026lt;/context:component-scan\u0026gt; ","date":"2 January 2023","externalUrl":null,"permalink":"/posts/velog/074-2.1-spring-+-mybatis/","section":"Posts","summary":"코딩 순서 # 테이블 생성 및 개발 준비 도메인 객체의 설계 및 클래스 작성 DAO 인터페이스 작성 XML Mapper의 생성과 SQL문 작성 MyBatis에서 작성된 Mapper 인식하도록 설정 DAO 구현 (DAOImpl) XML Mapper # Mybatis에서 SQL문을 저장하는 존재 src/main/java/resources/mappers폴더에 저장\nroot-context.xml 추가 # XML Mapper 인식 위해 설정 추가\n","title":"2.1 Spring + MyBatis","type":"posts"},{"content":" MVC란? # MODEL - VIEW - Controller\nModel # 데이터를 가진 객체 컨트롤러와 뷰에 통보 View # HTML/CSS/JS Controller # 사용자의 접근에 따라 Model을 만들고, 이를 View에 반영시켜 사용자에게 전달 Spring MVC # servlet-context.xml # appServlet폴더 내의, MVC 설정만 분리하기 위한 파일\nannotation # annotation desc using @controller 스프링 컨트롤러 객체임 명시 클래스 @RequestMapping 특정 URL에 매핑 클래스,메소드 @RequestParam req에서 특정한 파라미터 추출 파라미터 @RequestHeader req에서 특정 헤더 추출 파라미터 @PathVariabel 현재 URL에서 정보 추출 파라미터 @CookieValue 쿠키값 추출 파라미터 @ModelAttribute 자동으로 해당 객체의 뷰까지 전달 메소드,파라미터 @SessionAttribute 세션상에서 모델 정보 유지 클래스 @Responsebody 리턴값 그대로 응답 메소드,리턴타입 @Repository DAO(Data access object) 클래스 @Service 서비스객체 클래스 return type # void # 매핑된 이름.jsp 반환\nString # 문자열+.jsp 반환 return redirect: + 문자열 을 통해 redirect 가능\nObject # 객체를 json으로 변환\n","date":"2 January 2023","externalUrl":null,"permalink":"/posts/velog/075-2.spring-mvc/","section":"Posts","summary":"MVC란? # MODEL - VIEW - Controller\nModel # 데이터를 가진 객체 컨트롤러와 뷰에 통보 View # HTML/CSS/JS Controller # 사용자의 접근에 따라 Model을 만들고, 이를 View에 반영시켜 사용자에게 전달 Spring MVC # servlet-context.xml # appServlet폴더 내의, MVC 설정만 분리하기 위한 파일\n","title":"2.Spring MVC","type":"posts"},{"content":" MyBatis란? # 스프링과 DB를 이어주는 중간다리 역활\npom.xml 라이브러리 추가 # mybatis # \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.mybatis\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;mybatis\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;3.4.1\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; mybatis-spring # \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.mybatis\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;mybatis-spring\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.3.0\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; spring-jdbc # \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-jdbc\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${org.springframework-version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; spring-test # \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-test\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${org.springframework-version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; ","date":"2 January 2023","externalUrl":null,"permalink":"/posts/velog/076-1.1-mybatis+oracle-%EC%84%A4%EC%A0%95/","section":"Posts","summary":"MyBatis란? # 스프링과 DB를 이어주는 중간다리 역활\npom.xml 라이브러리 추가 # mybatis # \u003cdependency\u003e \u003cgroupId\u003eorg.mybatis\u003c/groupId\u003e \u003cartifactId\u003emybatis\u003c/artifactId\u003e \u003cversion\u003e3.4.1\u003c/version\u003e \u003c/dependency\u003e mybatis-spring # \u003cdependency\u003e \u003cgroupId\u003eorg.mybatis\u003c/groupId\u003e \u003cartifactId\u003emybatis-spring\u003c/artifactId\u003e \u003cversion\u003e1.3.0\u003c/version\u003e \u003c/dependency\u003e spring-jdbc # \u003cdependency\u003e \u003cgroupId\u003eorg.springframework\u003c/groupId\u003e \u003cartifactId\u003espring-jdbc\u003c/artifactId\u003e \u003cversion\u003e${org.springframework-version}\u003c/version\u003e \u003c/dependency\u003e spring-test # \u003cdependency\u003e \u003cgroupId\u003eorg.springframework\u003c/groupId\u003e \u003cartifactId\u003espring-test\u003c/artifactId\u003e \u003cversion\u003e${org.springframework-version}\u003c/version\u003e \u003c/dependency\u003e","title":"1.1 MyBatis+Oracle 설정","type":"posts"},{"content":"STS3사용\nSpring MVC PROJECT 기본 구조 # /src/main/java =\u0026gt; 개발되는 Java 코드의 경로\nsrc/main/resource =\u0026gt; 서버실행에 필요한 파일 경로\nsrc/main/webapp/WEB-INF\n/spring=\u0026gt;스프링 설정 파일 경로 /views =\u0026gt;JSP파일 경로 src/test/* =\u0026gt; 테스트 전용 경로\npom.xml =\u0026gt;Maven 설정 파일\n추가 설정 # Properites-\u0026gt;Maven-\u0026gt;Project facts Java 버전 변경 Tomcat 설치 후 Windows→ Preferences → Server → Runtime Environments → Add 3.Servers→ Tomcat서버 폴더 → server.xml Connector 포트 8181 변경(안하면 충돌) 4.스프링+MyBatis+Oracle 설정 ","date":"2 January 2023","externalUrl":null,"permalink":"/posts/velog/077-1.-init/","section":"Posts","summary":"STS3사용\nSpring MVC PROJECT 기본 구조 # /src/main/java =\u003e 개발되는 Java 코드의 경로\nsrc/main/resource =\u003e 서버실행에 필요한 파일 경로\nsrc/main/webapp/WEB-INF\n/spring=\u003e스프링 설정 파일 경로 /views =\u003eJSP파일 경로 src/test/* =\u003e 테스트 전용 경로\npom.xml =\u003eMaven 설정 파일\n추가 설정 # Properites-\u003eMaven-\u003eProject facts Java 버전 변경 Tomcat 설치 후 Windows→ Preferences → Server → Runtime Environments → Add 3.Servers→ Tomcat서버 폴더 → server.xml Connector 포트 8181 변경(안하면 충돌) 4.스프링+MyBatis+Oracle 설정 ","title":"1. INIT","type":"posts"},{"content":" 처음으로 해보는 스프링 # 지금까지 python Django를 사용한 웹 개발만 해봤는데, 취업을 위해 스프링 프레임워크를 공부하고자 한다.\n환경 # name desc OS Window10 JAVA JDK 1.8 DB Oracle Spring Spring Legacy Server Tomcat9.0 IDE STS3 SQL Mybatis ","date":"2 January 2023","externalUrl":null,"permalink":"/posts/velog/078-firstspring/","section":"Posts","summary":"처음으로 해보는 스프링 # 지금까지 python Django를 사용한 웹 개발만 해봤는데, 취업을 위해 스프링 프레임워크를 공부하고자 한다.\n환경 # name desc OS Window10 JAVA JDK 1.8 DB Oracle Spring Spring Legacy Server Tomcat9.0 IDE STS3 SQL Mybatis ","title":"FirstSpring","type":"posts"},{"content":"","date":"7 December 2022","externalUrl":null,"permalink":"/series/bandit/","section":"Series","summary":"","title":"Bandit","type":"series"},{"content":"ssh bandit33@bandit.labs.overthewire.org -p 2220 # odHo63fHiFqcWWJG9rLiLDtPm45KzUKy At this moment, level 34 does not exist yet.\n아직 만들어지 않은듯? 나중에 추가되면 풀어봄\n\u0026ndash;끝\u0026ndash;\n","date":"7 December 2022","externalUrl":null,"permalink":"/posts/velog/079-bandit33%EB%AF%B8%EC%99%84/","section":"Posts","summary":"ssh bandit33@bandit.labs.overthewire.org -p 2220 # odHo63fHiFqcWWJG9rLiLDtPm45KzUKy At this moment, level 34 does not exist yet.\n아직 만들어지 않은듯? 나중에 추가되면 풀어봄\n–끝–\n","title":"bandit33(미완)","type":"posts"},{"content":"","date":"7 December 2022","externalUrl":null,"permalink":"/tags/bandit/","section":"Tags","summary":"","title":"Bandit","type":"tags"},{"content":"ssh bandit32@bandit.labs.overthewire.org -p 2220 #rmCBvG56y58BXzv98yZGdO7ATVL5dW8y After all this git stuff its time for another escape. Good luck!\n??\n풀이 # 시작하자마자 WELCOME TO THE UPPERCASE SHELL 메세지가 나온다 모든 글자가 대문자로 처리되는듯 하다\n$0'을 입력하면 쉘로 정상 실행된다 ?\n$ cat /etc/bandit_pass/bandit33 odHo63fHiFqcWWJG9rLiLDtPm45KzUKy ","date":"7 December 2022","externalUrl":null,"permalink":"/posts/velog/080-bandit32/","section":"Posts","summary":"ssh bandit32@bandit.labs.overthewire.org -p 2220 #rmCBvG56y58BXzv98yZGdO7ATVL5dW8y After all this git stuff its time for another escape. Good luck!\n??\n풀이 # 시작하자마자 WELCOME TO THE UPPERCASE SHELL 메세지가 나온다 모든 글자가 대문자로 처리되는듯 하다\n$0'을 입력하면 쉘로 정상 실행된다 ?\n$ cat /etc/bandit_pass/bandit33 odHo63fHiFqcWWJG9rLiLDtPm45KzUKy","title":"bandit32","type":"posts"},{"content":"ssh bandit31@bandit.labs.overthewire.org -p 2220 #OoffzGDlzhAlerFJ2cAiz1D41JW1Mhmt There is a git repository at ssh://bandit31-git@localhost/home/bandit31-git/repo. The password for the user bandit31-git is the same as for the user bandit31.\n이전과 같dl git에서 찾으면 된다\n풀이 # git clone ssh://bandit31-git@localhost:2220/home/bandit31-git/repo Reame.md bandit31@bandit:/tmp/tmp.cYOLY9Uwqg/repo$ cat README.md This time your task is to push a file to the remote repository. Details: File name: key.txt Content: \u0026#39;May I come in?\u0026#39; Branch: master 이번에는 remote repository에 파일을 푸시하라고 나온다 May I come in? 이라는 내용의 key.txt를 만들고 add한다\nbandit31@bandit:/tmp/tmp.cYOLY9Uwqg/repo$ git add key.txt The following paths are ignored by one of your .gitignore files: key.txt gitignore이 설정되있단다 rm으로 지워버리자\nbandit31@bandit:/tmp/tmp.cYOLY9Uwqg/repo$ rm .gitignore 이제 정상적으로 된다\ngit add key.txt git commit -m \u0026#39;test\u0026#39; git push origin master ... remote: Well done! Here is the password for the next level: remote: rmCBvG56y58BXzv98yZGdO7ATVL5dW8y ","date":"7 December 2022","externalUrl":null,"permalink":"/posts/velog/081-bandit31/","section":"Posts","summary":"ssh bandit31@bandit.labs.overthewire.org -p 2220 #OoffzGDlzhAlerFJ2cAiz1D41JW1Mhmt There is a git repository at ssh://bandit31-git@localhost/home/bandit31-git/repo. The password for the user bandit31-git is the same as for the user bandit31.\n이전과 같dl git에서 찾으면 된다\n풀이 # git clone ssh://bandit31-git@localhost:2220/home/bandit31-git/repo Reame.md bandit31@bandit:/tmp/tmp.cYOLY9Uwqg/repo$ cat README.md This time your task is to push a file to the remote repository. Details: File name: key.txt Content: 'May I come in?' Branch: master 이번에는 remote repository에 파일을 푸시하라고 나온다 May I come in? 이라는 내용의 key.txt를 만들고 add한다\n","title":"bandit31","type":"posts"},{"content":"ssh bandit30@bandit.labs.overthewire.org -p 2220 #xbhV3HpNGlTIdnjUrdAlPzc2L6y9EOnS There is a git repository at ssh://bandit30-git@localhost/home/bandit30-git/repo. The password for the user bandit30-git is the same as for the user bandit30.\n이전과 같음\n풀이 # #/tmp안에서 진행 git clone ssh://bandit30-git@localhost:2220/home/bandit30-git/repo #xbhV3HpNGlTIdnjUrdAlPzc2L6y9EOnS 이번엔 로그도, 브랜치도 따로 없다\ngit show-ref명령어가 있는데, 이는 커밋을 간접적으로 참조하는?? 방식이라고 한다 정확히는 모르겠다\nbandit30@bandit:/tmp/tmp.1hIj1KUVPA/repo$ git show-ref 0019ee8c6d6fd1dba2d73666b9e6339ad3314ddb refs/heads/master 0019ee8c6d6fd1dba2d73666b9e6339ad3314ddb refs/remotes/origin/HEAD 0019ee8c6d6fd1dba2d73666b9e6339ad3314ddb refs/remotes/origin/master 831aac2e2341f009e40e46392a4f5dd318483019 refs/tags/secret git show refs/tags/secret #OoffzGDlzhAlerFJ2cAiz1D41JW1Mhmt ","date":"7 December 2022","externalUrl":null,"permalink":"/posts/velog/082-bandit30/","section":"Posts","summary":"ssh bandit30@bandit.labs.overthewire.org -p 2220 #xbhV3HpNGlTIdnjUrdAlPzc2L6y9EOnS There is a git repository at ssh://bandit30-git@localhost/home/bandit30-git/repo. The password for the user bandit30-git is the same as for the user bandit30.\n이전과 같음\n풀이 # #/tmp안에서 진행 git clone ssh://bandit30-git@localhost:2220/home/bandit30-git/repo #xbhV3HpNGlTIdnjUrdAlPzc2L6y9EOnS 이번엔 로그도, 브랜치도 따로 없다\ngit show-ref명령어가 있는데, 이는 커밋을 간접적으로 참조하는?? 방식이라고 한다 정확히는 모르겠다\nbandit30@bandit:/tmp/tmp.1hIj1KUVPA/repo$ git show-ref 0019ee8c6d6fd1dba2d73666b9e6339ad3314ddb refs/heads/master 0019ee8c6d6fd1dba2d73666b9e6339ad3314ddb refs/remotes/origin/HEAD 0019ee8c6d6fd1dba2d73666b9e6339ad3314ddb refs/remotes/origin/master 831aac2e2341f009e40e46392a4f5dd318483019 refs/tags/secret git show refs/tags/secret #OoffzGDlzhAlerFJ2cAiz1D41JW1Mhmt","title":"bandit30","type":"posts"},{"content":"ssh bandit29@bandit.labs.overthewire.org -p 2220 #tQKvmcwNYcFS6vmPHIUSI3ShmsrQZK8S There is a git repository at ssh://bandit29-git@localhost/home/bandit29-git/repo. The password for the user bandit29-git is the same as for the user bandit29.\nssh://bandit29-git@localhost/home/bandit29-git/repo에 git 저장소가 있습니다. 사용자 밴디트 29-git의 암호는 사용자 밴디트 29의 암호와 동일합니다.\n풀이 # 정보 # git checkout [branch]를 통해서 branch를 바꿀 수 있다 git log -p로 수정된것만 볼 수 있다 #mktemp -d로 임시폴더만들고 이동해서 진행 ssh://bandit29-git@localhost:2220/home/bandit29-git/repo #tQKvmcwNYcFS6vmPHIUSI3ShmsrQZK8S git checkout dev # dev 브랜치로 변경 bandit29@bandit:/tmp/tmp.pTgWuyrMjx/repo$ git log -p commit 4177db8d2e8f795a23b911550ee864880ae643ff (HEAD -\u0026gt; dev, origin/dev) Author: Morla Porla \u0026lt;morla@overthewire.org\u0026gt; Date: Sat Dec 3 08:14:07 2022 +0000 add data needed for development diff --git a/README.md b/README.md index 1af21d3..a4b1cf1 100644 --- a/README.md +++ b/README.md @@ -4,5 +4,5 @@ Some notes for bandit30 of bandit. ## credentials - username: bandit30 -- password: \u0026lt;no passwords in production!\u0026gt; +- password: xbhV3HpNGlTIdnjUrdAlPzc2L6y9EOnS ","date":"7 December 2022","externalUrl":null,"permalink":"/posts/velog/083-bandit29/","section":"Posts","summary":"ssh bandit29@bandit.labs.overthewire.org -p 2220 #tQKvmcwNYcFS6vmPHIUSI3ShmsrQZK8S There is a git repository at ssh://bandit29-git@localhost/home/bandit29-git/repo. The password for the user bandit29-git is the same as for the user bandit29.\nssh://bandit29-git@localhost/home/bandit29-git/repo에 git 저장소가 있습니다. 사용자 밴디트 29-git의 암호는 사용자 밴디트 29의 암호와 동일합니다.\n풀이 # 정보 # git checkout [branch]를 통해서 branch를 바꿀 수 있다 git log -p로 수정된것만 볼 수 있다 #mktemp -d로 임시폴더만들고 이동해서 진행 ssh://bandit29-git@localhost:2220/home/bandit29-git/repo #tQKvmcwNYcFS6vmPHIUSI3ShmsrQZK8S git checkout dev # dev 브랜치로 변경 bandit29@bandit:/tmp/tmp.pTgWuyrMjx/repo$ git log -p commit 4177db8d2e8f795a23b911550ee864880ae643ff (HEAD -\u003e dev, origin/dev) Author: Morla Porla \u003cmorla@overthewire.org\u003e Date: Sat Dec 3 08:14:07 2022 +0000 add data needed for development diff --git a/README.md b/README.md index 1af21d3..a4b1cf1 100644 --- a/README.md +++ b/README.md @@ -4,5 +4,5 @@ Some notes for bandit30 of bandit. ## credentials - username: bandit30 -- password: \u003cno passwords in production!\u003e +- password: xbhV3HpNGlTIdnjUrdAlPzc2L6y9EOnS","title":"bandit29","type":"posts"},{"content":"ssh bandit28@bandit.labs.overthewire.org -p 2220 #AVanL161y9rsbcJIsFHuw35rjaOM19nR There is a git repository at ssh://bandit28-git@localhost/home/bandit28-git/repo. The password for the user bandit28-git is the same as for the user bandit28. Clone the repository and find the password for the next level.\nssh://bandit28-git@localhost/home/bandit28-git/repo에 git 저장소가 있습니다. 사용자 밴디트 28-git의 암호는 사용자 밴디트 28의 암호와 동일합니다. 리포지토리를 복제하고 다음 수준의 암호를 찾습니다\n풀이 # 정보 # mktmp는 /tmp/에, temp는 지정한 위치에 임시 파일을 만든다 -d 옵션으로 디렉토리를 임시로 만들 수 있다 # mktemp -d로 임시폴더를 만들고 거기서 진행 git clone ssh://bandit28-git@localhost:2220/home/bandit28-git/repo md파일을 찍어보면\n#README.md # Bandit Notes Some notes for level29 of bandit. ## credentials - username: bandit29 - password: xxxxxxxxxx password가 xxxxxxx로 나온다 순진한 나는 진짜 비번인줄 알고 해봤는데 당연히 아니다.\ngit log로 커밋을 보면, 총 3개의 커밋이 나온다 여기서 가장 최신의 커밋을 보면 (git show[commit])\nbandit28@bandit:/tmp/tmp.0VgASmd1lU/repo$ git show 5f45cfaca938393c7706fec16ab1ca627e947f64 commit 5f45cfaca938393c7706fec16ab1ca627e947f64 (HEAD -\u0026gt; master, origin/master, origin/HEAD) Author: Morla Porla \u0026lt;morla@overthewire.org\u0026gt; Date: Sat Dec 3 08:14:06 2022 +0000 fix info leak diff --git a/README.md b/README.md index b302105..5c6457b 100644 --- a/README.md +++ b/README.md @@ -4,5 +4,5 @@ Some notes for level29 of bandit. ## credentials - username: bandit29 -- password: tQKvmcwNYcFS6vmPHIUSI3ShmsrQZK8S +- password: xxxxxxxxxx 이렇게 변경되기 전 파일을 볼 수 있다.\n?! # 솔직히 이건 하면서 좀 소름돋았다 지금까지 장고등을 하면서 계속 커밋하다가 마지막에만 비밀번호나 secret key를 지웠는데\u0026hellip; 물론 누가 내걸 가져가봤자 도움되는건 없겠지만 ㅋㅋㅋㅋ\ntQKvmcwNYcFS6vmPHIUSI3ShmsrQZK8S\n","date":"6 December 2022","externalUrl":null,"permalink":"/posts/velog/084-bandit28-%EC%86%8C%EB%A6%84/","section":"Posts","summary":"ssh bandit28@bandit.labs.overthewire.org -p 2220 #AVanL161y9rsbcJIsFHuw35rjaOM19nR There is a git repository at ssh://bandit28-git@localhost/home/bandit28-git/repo. The password for the user bandit28-git is the same as for the user bandit28. Clone the repository and find the password for the next level.\nssh://bandit28-git@localhost/home/bandit28-git/repo에 git 저장소가 있습니다. 사용자 밴디트 28-git의 암호는 사용자 밴디트 28의 암호와 동일합니다. 리포지토리를 복제하고 다음 수준의 암호를 찾습니다\n풀이 # 정보 # mktmp는 /tmp/에, temp는 지정한 위치에 임시 파일을 만든다 -d 옵션으로 디렉토리를 임시로 만들 수 있다 # mktemp -d로 임시폴더를 만들고 거기서 진행 git clone ssh://bandit28-git@localhost:2220/home/bandit28-git/repo md파일을 찍어보면\n","title":"bandit28 (소름)","type":"posts"},{"content":"ssh bandit27@bandit.labs.overthewire.org -p 2220 #YnQpBuifNMas1hcUFk70ZmqkhUU2EuaS There is a git repository at ssh://bandit27-git@localhost/home/bandit27-git/repo. The password for the user bandit27-git is the same as for the user bandit27. Clone the repository and find the password for the next level.\nssh://bandit27-git@localhost/home/bandit27-git/repo에 git 저장소가 있습니다. 사용자 밴디트 27-git의 암호는 사용자 밴디트 27의 암호와 동일합니다. 리포지토리를 복제하고 다음 수준의 암호를 찾습니다.\n풀이 # 자꾸 에러나서 인터넷을 찾아보았더니 서버정책이 변경됬단다 포트를 꼭 추가해줘야한다\n#/tmp에서 진행 bandit27@bandit:/tmp/tmp.8KQzjF22CP$ git clone ssh://bandit27-git@localhost:2220/home/bandit27-git/repo cat으로 읽으면 된다 The password to the next level is: AVanL161y9rsbcJIsFHuw35rjaOM19nR\n","date":"6 December 2022","externalUrl":null,"permalink":"/posts/velog/085-bandit27/","section":"Posts","summary":"ssh bandit27@bandit.labs.overthewire.org -p 2220 #YnQpBuifNMas1hcUFk70ZmqkhUU2EuaS There is a git repository at ssh://bandit27-git@localhost/home/bandit27-git/repo. The password for the user bandit27-git is the same as for the user bandit27. Clone the repository and find the password for the next level.\nssh://bandit27-git@localhost/home/bandit27-git/repo에 git 저장소가 있습니다. 사용자 밴디트 27-git의 암호는 사용자 밴디트 27의 암호와 동일합니다. 리포지토리를 복제하고 다음 수준의 암호를 찾습니다.\n풀이 # 자꾸 에러나서 인터넷을 찾아보았더니 서버정책이 변경됬단다 포트를 꼭 추가해줘야한다\n","title":"bandit27","type":"posts"},{"content":"bandit25에서 얻은 쉘에서 접속해야한다\nGood job getting a shell! Now hurry and grab the password for bandit27! Commands you may need to solve this level\n칭찬받았다 :)\n풀이 # ls를 쳐보면, bandit27-do라는 파일이 보인다 권한이 다음과 같다\n-rwsr-x--- 1 bandit27 bandit26 14876 Dec 3 08:14 bandit27-do x자리에 s가 보이는데, 이는 setuid를 뜻한다 다음과 같이, 파일 소유자의 권한을 빌릴 수 있다\nbandit26@bandit:~$ ./bandit27-do whoami bandit27 비번찾기\nbandit26@bandit:~$ ./bandit27-do cat /etc/bandit_pass/bandit27 #YnQpBuifNMas1hcUFk70ZmqkhUU2EuaS ","date":"6 December 2022","externalUrl":null,"permalink":"/posts/velog/086-bandit26/","section":"Posts","summary":"bandit25에서 얻은 쉘에서 접속해야한다\nGood job getting a shell! Now hurry and grab the password for bandit27! Commands you may need to solve this level\n칭찬받았다 :)\n풀이 # ls를 쳐보면, bandit27-do라는 파일이 보인다 권한이 다음과 같다\n-rwsr-x--- 1 bandit27 bandit26 14876 Dec 3 08:14 bandit27-do x자리에 s가 보이는데, 이는 setuid를 뜻한다 다음과 같이, 파일 소유자의 권한을 빌릴 수 있다\n","title":"bandit26","type":"posts"},{"content":"ssh bandit25@bandit.labs.overthewire.org -p 2220 #p7TaowMYrmu23Ol8hiZh9UvD0O9hpx8d Logging in to bandit26 from bandit25 should be fairly easy… The shell for user bandit26 is not /bin/bash, but something else. Find out what it is, how it works and how to break out of it.\n밴디트 25에서 밴디트 26에 로그인하는 것은 꽤 쉬울 것입니다. 사용자 밴디트 26의 셸은 /bin/bash가 아니라 다른 것이다. 그것이 무엇인지, 그것이 어떻게 작동하는지, 그리고 그것을 탈출하는 방법을 알아보세요.\n풀이 # 먼져 홈에있는 sshkey를 통해 접속해보자\nbandit25@bandit:~$ ssh bandit26@localhost -i bandit26.sshkey -p 2220 _ _ _ _ ___ __ | | | (_) | |__ \\ / / | |__ __ _ _ __ __| |_| |_ ) / /_ | \u0026#39;_ \\ / _` | \u0026#39;_ \\ / _` | | __| / / \u0026#39;_ \\ | |_) | (_| | | | | (_| | | |_ / /| (_) | |_.__/ \\__,_|_| |_|\\__,_|_|\\__|____\\___/ Connection to localhost closed. 바로 끊긴다. 쉘문제라고 알려줬으니 /etc/passwd를 확인하자\nbandit25@bandit:~$ cat /etc/passwd | grep bandit26 bandit26:x:11026:11026:bandit level 26:/home/bandit26:/usr/bin/showtext 설명\n내용 설명 bandit26: 서버로 로그인하는 아이디 x: 비밀번호(진짜는 /etc/shadow에있음) 11026: 2개있는데 유저번호,그룹번호임 bandit level 26: 사용자의 이름 /home/bandit26: 사용자가 로그인하면 위치하는 디렉토리 /usr/bin/showtext 사용자가 처음 로그인했을때 실행되는 프로그램 이때, 마지막 로그인했을때 실행되는 프로그램이, 나머지는 bin/bash인데 얘만 /usr/bin/showtext이다 따라서 이를 열어보자\nbandit25@bandit:~$ cat /usr/bin/showtext #!/bin/sh export TERM=linux exec more ~/text.txt exit 0 test.txt에 more 실행하고 종료하는 명령어이다 따라서 more 명령이 끝나지 않기 위해 창을 줄여버리면, 대기상태가 된다\n여기서 shell을 설정해주면, 끝나지 않은 채로 접속이 가능하다 (현재 bandit26)\n#창을 줄인채로 bandit25@bandit:~$ ssh bandit26@localhost -i bandit26.sshkey -p 2220 #v를 눌러 vim으로 진행 :set shell=/bin/bash #쉘세팅 :sh # 쉘실행 bandit26@bandit:~$ bandit26@bandit:~$ cat /etc/bandit_pass/bandit26 #c7GvcKlw9mC7aUQaPx7nwFstuAIBw1o1 또다른방법 vim 에디터에서 :r 로 파일의 내용을 읽어서 쓸수도 있다. 비번은 알 수 있는다 접속을 못한다 ㅠㅠ\n","date":"6 December 2022","externalUrl":null,"permalink":"/posts/velog/087-bandit25/","section":"Posts","summary":"ssh bandit25@bandit.labs.overthewire.org -p 2220 #p7TaowMYrmu23Ol8hiZh9UvD0O9hpx8d Logging in to bandit26 from bandit25 should be fairly easy… The shell for user bandit26 is not /bin/bash, but something else. Find out what it is, how it works and how to break out of it.\n밴디트 25에서 밴디트 26에 로그인하는 것은 꽤 쉬울 것입니다. 사용자 밴디트 26의 셸은 /bin/bash가 아니라 다른 것이다. 그것이 무엇인지, 그것이 어떻게 작동하는지, 그리고 그것을 탈출하는 방법을 알아보세요.\n풀이 # 먼져 홈에있는 sshkey를 통해 접속해보자\n","title":"bandit25","type":"posts"},{"content":"ssh bandit24@bandit.labs.overthewire.org -p 2220 #VAfGXJ1PBSsPSnvsjI8p759leLZ9GGar A daemon is listening on port 30002 and will give you the password for bandit25 if given the password for bandit24 and a secret numeric 4-digit pincode. There is no way to retrieve the pincode except by going through all of the 10000 combinations, called brute-forcing. You do not need to create new connections each time\n이전암호 + 0000~9999 의 모든 조합을 30002번포트에 보내라 맞으면 비번을 준다\n풀이 # 2가지 방법이 있다\nsh이용 # 나는 실패했는데 인터넷에선 이렇게 했다고 한다\n#!/bin/bash for i in {0000..9999} do echo UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ $i \u0026gt;\u0026gt; possibilities.txt done cat possibilities.txt | nc localhost 30002 \u0026gt; result.txt cat result.txt grep -v \u0026#39;Wrong!\u0026#39; 내가 하면 한 1000개하면 출력이 멈춰서 더이상 진행되지 않는다 왜인지 모르겠다\n파이썬 소켓 프로그래밍 # #!/usr/bin/env python3 # coding: utf-8 import socket def find_pass(password, port): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((\u0026#34;localhost\u0026#34;, port)) msg = s.recv(2048).decode() # 처음나오는 메세지 print(msg) for i in range(10000): # 브루트포스시작 pincode = \u0026#34; \u0026#34; + str(i).zfill(4) + \u0026#34;\\n\u0026#34; answer = password + pincode # 보낼메세지 s.sendall(answer.encode()) res = s.recv(1024).decode() #받을메서지 if res[0] == \u0026#34;W\u0026#34;: print(pincode, \u0026#34;is wrong\u0026#34;) else: print(pincode) print(res) exit() if __name__ == \u0026#34;__main__\u0026#34;: password = \u0026#34;VAfGXJ1PBSsPSnvsjI8p759leLZ9GGar\u0026#34; port = 30002 find_pass(password, port) 핀코드는 할때마다 랜덤으로 바뀐다 시간이 상당히 오래 걸린다\n비밀번호 = p7TaowMYrmu23Ol8hiZh9UvD0O9hpx8d\n","date":"6 December 2022","externalUrl":null,"permalink":"/posts/velog/088-bandit24/","section":"Posts","summary":"ssh bandit24@bandit.labs.overthewire.org -p 2220 #VAfGXJ1PBSsPSnvsjI8p759leLZ9GGar A daemon is listening on port 30002 and will give you the password for bandit25 if given the password for bandit24 and a secret numeric 4-digit pincode. There is no way to retrieve the pincode except by going through all of the 10000 combinations, called brute-forcing. You do not need to create new connections each time\n이전암호 + 0000~9999 의 모든 조합을 30002번포트에 보내라 맞으면 비번을 준다\n","title":"bandit24","type":"posts"},{"content":"ssh bandit23@bandit.labs.overthewire.org -p 2220 #QYw0Y2aiA672PsMmh9puTQuhoz8SyR2G A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed. NOTE: This level requires you to create your own first shell-script. This is a very big step and you should be proud of yourself when you beat this level! NOTE 2: Keep in mind that your shell script is removed once executed, so you may want to keep a copy around…\n프로그램은 시간 기반 작업 스케줄러인 cron에서 일정한 간격으로 자동으로 실행됩니다. /etc/cron.d/에서 구성을 확인하고 실행 중인 명령을 확인합니다. 참고: 이 수준에서는 자신의 첫 번째 셸 스크립트를 만들어야 합니다. 이것은 매우 큰 단계이고 여러분이 이 수준을 이길 때 여러분은 스스로를 자랑스러워해야 합니다! 참고 2: 셸 스크립트가 실행되면 제거되므로 복사본을 보관할 수 있습니다\n풀이 # cron을 확인한다\nbandit23@bandit:/etc/cron.d$ cat cronjob_bandit24 @reboot bandit24 /usr/bin/cronjob_bandit24.sh \u0026amp;\u0026gt; /dev/null * * * * * bandit24 /usr/bin/cronjob_bandit24.sh \u0026amp;\u0026gt; /dev/null #얘를공략 bandit23@bandit:/etc/cron.d$ cat /usr/bin/cronjob_bandit24.sh #!/bin/bash myname=$(whoami)\t#bandit24 cd /var/spool/$myname/foo\techo \u0026#34;Executing and deleting all scripts in /var/spool/$myname/foo:\u0026#34; #경로 for i in * .*;\t#경로의 모든 파일을 돌면서 do if [ \u0026#34;$i\u0026#34; != \u0026#34;.\u0026#34; -a \u0026#34;$i\u0026#34; != \u0026#34;..\u0026#34; ];\t# 현재폴더와 상위 폴더가 아니면 then\t#해라 echo \u0026#34;Handling $i\u0026#34;\towner=\u0026#34;$(stat --format \u0026#34;%U\u0026#34; ./$i)\u0026#34;\t#owner변수는 파일의 소유자 if [ \u0026#34;${owner}\u0026#34; = \u0026#34;bandit23\u0026#34; ]; then\t#만약 owner가 bandit23이라면 timeout -s 9 60 ./$i\t(60초 후 kill -9) fi\t# #if문 끝 rm -f ./$i\t#파일지우기 fi\t#then문 끝 done\t#아예끝 비밀번호를 읽어서 내 폴더에 쓰는 역활의 파일 test.sh만들기 cat /etc/bandit_pass/bandit24 \u0026gt; /tmp/jiheon/passwd\n#bandit23@bandit:/tmp/jiheon 에서 작업 chmod 666 /tmp/jiheon\t# 현재 폴더에 누구나 쓸 수 있게 (비번을써야됨) cp ./test.sh /var/spool/bandit24/foo/test.sh\t#파일을 옮김 # cron에 의해 1분마다 작업이 실행되고, 내 폴더의 passwd에 비번이 써짐 # VAfGXJ1PBSsPSnvsjI8p759leLZ9GGar ","date":"1 December 2022","externalUrl":null,"permalink":"/posts/velog/089-bandit23/","section":"Posts","summary":"ssh bandit23@bandit.labs.overthewire.org -p 2220 #QYw0Y2aiA672PsMmh9puTQuhoz8SyR2G A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed. NOTE: This level requires you to create your own first shell-script. This is a very big step and you should be proud of yourself when you beat this level! NOTE 2: Keep in mind that your shell script is removed once executed, so you may want to keep a copy around…\n","title":"bandit23","type":"posts"},{"content":"ssh bandit22@bandit.labs.overthewire.org -p 2220 #WdDozAdTM2z9DiFEQ2mGlwngMfj4EZff A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed. NOTE: Looking at shell scripts written by other people is a very useful skill. The script for this level is intentionally made easy to read. If you are having problems understanding what it does, try executing it to see the debug information it prints.\n프로그램은 시간 기반 작업 스케줄러인 cron에서 일정한 간격으로 자동으로 실행됩니다. /etc/cron.d/에서 구성을 확인하고 실행 중인 명령을 확인합니다.\n풀이 # 정보 # md5sum이란?\n입력된 파일을 해시값으로 출력해준다. 파일이 한 글자라도 손상되었다면, 해시값이 달라진다 cut이란?\n특정 파일의 열을 출력, 제거하는 명령어 /etc/cron.d/cronjob_bandit22를 확인한다\nbandit22@bandit:/etc/cron.d$ cat cronjob_bandit23 @reboot bandit23 /usr/bin/cronjob_bandit23.sh \u0026amp;\u0026gt; /dev/null * * * * * bandit23 /usr/bin/cronjob_bandit23.sh \u0026amp;\u0026gt; /dev/null /usr/bin/cronjob_bandit23.sh를 읽어본다\nbandit22@bandit:/etc/cron.d$ cat /usr/bin/cronjob_bandit23.sh #!/bin/bash myname=$(whoami) mytarget=$(echo I am user $myname | md5sum | cut -d \u0026#39; \u0026#39; -f 1) echo \u0026#34;Copying passwordfile /etc/bandit_pass/$myname to /tmp/$mytarget\u0026#34; # 대충 i am user 현유저이름 =\u0026gt; 이를 해시로, 공백을 기준으로 자르고 첫번째거 반환 # 이대로 해보면 bandit22@bandit:/tmp$ echo I am user bandit23 | md5sum | cut -d \u0026#39; \u0026#39; -f 1 8ca319486bfbbc3663ea0fbe81326349 마지막으로, /tmp/8ca319486bfbbc3663ea0fbe81326349를 읽으면 된다\nbandit22@bandit:/tmp$ cat /tmp/8ca319486bfbbc3663ea0fbe81326349 QYw0Y2aiA672PsMmh9puTQuhoz8SyR2G ","date":"1 December 2022","externalUrl":null,"permalink":"/posts/velog/090-bandit22/","section":"Posts","summary":"ssh bandit22@bandit.labs.overthewire.org -p 2220 #WdDozAdTM2z9DiFEQ2mGlwngMfj4EZff A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed. NOTE: Looking at shell scripts written by other people is a very useful skill. The script for this level is intentionally made easy to read. If you are having problems understanding what it does, try executing it to see the debug information it prints.\n","title":"bandit22","type":"posts"},{"content":"ssh bandit21@bandit.labs.overthewire.org -p 2220 #NvEJF7oVjkddltPSrdKEFOllh9V1IBcq A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed.\n프로그램은 시간 기반 작업 스케줄러인 cron에서 일정한 간격으로 자동으로 실행됩니다. /etc/cron.d/에서 구성을 확인하고 실행 중인 명령을 확인합니다.\n풀이 # 정보 # cron이란?\n정기적으로 같은 작업을 할때, 일정시간마다 자동 실행시키는 프로그램 crontab으로 시간, 실행할거를 관리하고, cron으로 crontab을 실행시킴 etc/cron.d 폴더에서 진행 bandit21@bandit:/etc/cron.d$ cat cronjob_bandit22 @reboot bandit22 /usr/bin/cronjob_bandit22.sh \u0026amp;\u0026gt; /dev/null * * * * * bandit22 /usr/bin/cronjob_bandit22.sh \u0026amp;\u0026gt; /dev/null # 대충 1분마다 /usr/bin/cronjob_bandit22.sh 프로그램 출력을 휴지통?에 보내고 있다 bandit21@bandit:/etc/cron.d$ cat /usr/bin/cronjob_bandit22.sh #!/bin/bash chmod 644 /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv cat /etc/bandit_pass/bandit22 \u0026gt; /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv # 대충 비밀번호를 /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv에 덮어쓰기하고있다는뜻 bandit21@bandit:/etc/cron.d$ cat /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv #WdDozAdTM2z9DiFEQ2mGlwngMfj4EZff #이를 확인해보면 비번이 나온다 ","date":"1 December 2022","externalUrl":null,"permalink":"/posts/velog/091-bandit21/","section":"Posts","summary":"ssh bandit21@bandit.labs.overthewire.org -p 2220 #NvEJF7oVjkddltPSrdKEFOllh9V1IBcq A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed.\n프로그램은 시간 기반 작업 스케줄러인 cron에서 일정한 간격으로 자동으로 실행됩니다. /etc/cron.d/에서 구성을 확인하고 실행 중인 명령을 확인합니다.\n풀이 # 정보 # cron이란?\n","title":"bandit21","type":"posts"},{"content":"ssh bandit20@bandit.labs.overthewire.org -p 2220 #VxCazJaVykI6W36BkBU0mJTCM8rR95XT There is a setuid binary in the homedirectory that does the following: it makes a connection to localhost on the port you specify as a commandline argument. It then reads a line of text from the connection and compares it to the password in the previous level (bandit20). If the password is correct, it will transmit the password for the next level\n홈 디렉토리에는 setuid 바이너리가 있으며, 이 바이너리는 명령줄 인수로 지정한 포트의 localhost에 연결합니다. 그런 다음 연결에서 텍스트 한 줄을 읽고 이전 수준의 암호(bandit20)와 비교합니다. 암호가 올바르면 다음 단계의 암호를 전송합니다.\n풀이 # 대충 포트로 이전 비밀번호를 보내면, 반대쪽으로 다음 비밀번호를 준다는 뜻이다 2개의 터미널로 접속해서 푼다\n정보 # nc -l [포트번호] 로 포트를 열 수 있다 터미널1에서 nc -l 1234로 1234번 포트를 열어놓는다 터미널2에서 ./suconnect 1234 로 포트에 연결한다 터미널1에서 이전비밀번호를 보내면, 터미널2에서 이를 읽고 다시 1로 현재비밀번호를 보낸다 #NvEJF7oVjkddltPSrdKEFOllh9V1IBcq\n","date":"30 November 2022","externalUrl":null,"permalink":"/posts/velog/092-bandit20/","section":"Posts","summary":"ssh bandit20@bandit.labs.overthewire.org -p 2220 #VxCazJaVykI6W36BkBU0mJTCM8rR95XT There is a setuid binary in the homedirectory that does the following: it makes a connection to localhost on the port you specify as a commandline argument. It then reads a line of text from the connection and compares it to the password in the previous level (bandit20). If the password is correct, it will transmit the password for the next level\n홈 디렉토리에는 setuid 바이너리가 있으며, 이 바이너리는 명령줄 인수로 지정한 포트의 localhost에 연결합니다. 그런 다음 연결에서 텍스트 한 줄을 읽고 이전 수준의 암호(bandit20)와 비교합니다. 암호가 올바르면 다음 단계의 암호를 전송합니다.\n","title":"bandit20","type":"posts"},{"content":"ssh bandit19@bandit.labs.overthewire.org -p 2220 #awhqfNnAbc1naukrpqDYcF95h7HoMTrC To gain access to the next level, you should use the setuid binary in the homedirectory. Execute it without arguments to find out how to use it. The password for this level can be found in the usual place (/etc/bandit_pass), after you have used the setuid binary.\nsetuid를 사용한 후, /etc/bandit_pass 에서 비밀번호를 찾아라\n풀이 # 정보 # setuid란?\n사실 별 생각없이 사용하는 chmod 777은, chmod 0777에서 0이 생략된것이다. 맨 앞의 0은, 리눅스의 특수권한을 뜻한다. (setuid, setgid, stickybit) setuid이란, 파일이 (명령어가) 실행되는 동안 일시적으로 파일 소유주의 권한을 얻게 된다. bandit19@bandit:~$ ls -al bandit20-do -rwsr-x--- 1 bandit20 bandit19 14872 Sep 1 06:30 bandit20-do # 여기서처음보는 s가 등장 #example ./bandit20-do whoami bandit20 whoami bandit19 ./bandit20-do cat /etc/bandit_pass/bandit20 #VxCazJaVykI6W36BkBU0mJTCM8rR95XT ","date":"30 November 2022","externalUrl":null,"permalink":"/posts/velog/093-bandit19/","section":"Posts","summary":"ssh bandit19@bandit.labs.overthewire.org -p 2220 #awhqfNnAbc1naukrpqDYcF95h7HoMTrC To gain access to the next level, you should use the setuid binary in the homedirectory. Execute it without arguments to find out how to use it. The password for this level can be found in the usual place (/etc/bandit_pass), after you have used the setuid binary.\nsetuid를 사용한 후, /etc/bandit_pass 에서 비밀번호를 찾아라\n풀이 # 정보 # setuid란?\n","title":"bandit19","type":"posts"},{"content":"ssh bandit18@bandit.labs.overthewire.org -p 2220 #hga5tuuCLF6fFzUpnagiMN8ssu9LFrdg The password for the next level is stored in a file readme in the homedirectory. Unfortunately, someone has modified .bashrc to log you out when you log in with SSH.\nreadme 파일을 읽으면 된다. 근데 누가 bashrc를 만져서, 접속하자마자 로그아웃된다\n풀이 # 시작하자마자 연결이 끊긴다 ssh로 접속할때, 실행할 명령어를 같이 보내주면 된다.\nssh bandit18@bandit.labs.overthewire.org -p 2220 \u0026#39;cat readme\u0026#39; #hga5tuuCLF6fFzUpnagiMN8ssu9LFrdg #awhqfNnAbc1naukrpqDYcF95h7HoMTrC ","date":"30 November 2022","externalUrl":null,"permalink":"/posts/velog/094-bandit18/","section":"Posts","summary":"ssh bandit18@bandit.labs.overthewire.org -p 2220 #hga5tuuCLF6fFzUpnagiMN8ssu9LFrdg The password for the next level is stored in a file readme in the homedirectory. Unfortunately, someone has modified .bashrc to log you out when you log in with SSH.\nreadme 파일을 읽으면 된다. 근데 누가 bashrc를 만져서, 접속하자마자 로그아웃된다\n풀이 # 시작하자마자 연결이 끊긴다 ssh로 접속할때, 실행할 명령어를 같이 보내주면 된다.\nssh bandit18@bandit.labs.overthewire.org -p 2220 'cat readme' #hga5tuuCLF6fFzUpnagiMN8ssu9LFrdg #awhqfNnAbc1naukrpqDYcF95h7HoMTrC","title":"bandit18","type":"posts"},{"content":"ssh bandit17@bandit.labs.overthewire.org -p 2220 #VwOSWtCA7lRKkTfbr2IDh6awj9RNZM5e There are 2 files in the homedirectory: passwords.old and passwords.new. The password for the next level is in passwords.new and is the only line that has been changed between passwords.old and passwords.new NOTE: if you have solved this level and see ‘Byebye!’ when trying to log into bandit18, this is related to the next level, bandit19\npassword.old와 password.new 파일에서 다른 줄을 찾아라\n풀이 # 정보 # diff [file][file]\n두 (최대3) 파일간의 다른점 출력 u 옵션을 쓰면 근처의 내용도 보임 bandit17@bandit:~$ diff passwords.old passwords.new -d -w #hga5tuuCLF6fFzUpnagiMN8ssu9LFrdg 이 비번으로 로그인하면 바로 종료된다\n","date":"29 November 2022","externalUrl":null,"permalink":"/posts/velog/095-bandit17/","section":"Posts","summary":"ssh bandit17@bandit.labs.overthewire.org -p 2220 #VwOSWtCA7lRKkTfbr2IDh6awj9RNZM5e There are 2 files in the homedirectory: passwords.old and passwords.new. The password for the next level is in passwords.new and is the only line that has been changed between passwords.old and passwords.new NOTE: if you have solved this level and see ‘Byebye!’ when trying to log into bandit18, this is related to the next level, bandit19\npassword.old와 password.new 파일에서 다른 줄을 찾아라\n풀이 # 정보 # diff [file][file]\n","title":"bandit17","type":"posts"},{"content":"ssh bandit16@bandit.labs.overthewire.org -p 2220 ##JQttfApK4SeyHwDlI9SXGR50qclOAil1 The credentials for the next level can be retrieved by submitting the password of the current level to a port on localhost in the range 31000 to 32000. First find out which of these ports have a server listening on them. Then find out which of those speak SSL and which don’t. There is only 1 server that will give the next credentials, the others will simply send back to you whatever you send to it.\n현재 수준의 암호를 31000 - 32000 범위의 localhost 포트에 제출하여 다음 수준에 대한 자격 증명을 검색할 수 있습니다. 먼저 서버에서 수신 대기 중인 포트를 확인하십시오. 그런 다음 SSL을 사용하는 사람과 사용하지 않는 사람을 찾아보십시오. 다음 자격 증명을 제공하는 서버는 하나뿐이며, 다른 서버는 사용자가 보내는 모든 것을 다시 사용자에게 보냅니다.\n풀이 # 정보 # nmap을 통하여 열려있는 포트 스캔 가능 ssh private key는 오직 나한테만 rwx가 가능해야됨 (그룹도안됨) bandit16@bandit:~$ nmap -p 31000-32000 localhost 31046/tcp open unknown 31518/tcp open unknown 31691/tcp open unknown 31790/tcp open unknown 31960/tcp open unknown #/tmp 폴더에서 내이름으로 된 폴더 만들고 실행 bandit16@bandit:/tmp/jiheon$ openssl s_client -connect 127.0.0.1:31790 #JQttfApK4SeyHwDlI9SXGR50qclOAil1 #복사해서 key만들어줌 bandit16@bandit:/tmp/jiheon$ssh -i sshkey.private bandit17@localhost -p 2220 0@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ # 이후 에러메세지들을 보면, 너무 열려있다고 나온다. chmod로 권한변경해주면 bandit16계정으로 접속됨 bandit17@bandit:~$ cat /etc/bandit_pass/bandit17 #VwOSWtCA7lRKkTfbr2IDh6awj9RNZM5e ","date":"29 November 2022","externalUrl":null,"permalink":"/posts/velog/096-bandit16/","section":"Posts","summary":"ssh bandit16@bandit.labs.overthewire.org -p 2220 ##JQttfApK4SeyHwDlI9SXGR50qclOAil1 The credentials for the next level can be retrieved by submitting the password of the current level to a port on localhost in the range 31000 to 32000. First find out which of these ports have a server listening on them. Then find out which of those speak SSL and which don’t. There is only 1 server that will give the next credentials, the others will simply send back to you whatever you send to it.\n","title":"bandit16","type":"posts"},{"content":"ssh bandit15@bandit.labs.overthewire.org -p 2220 #jN2kgmIXJ6fShzhT2avhotn4Zcka6tnt The password for the next level can be retrieved by submitting the password of the current level to port 30001 on localhost using SSL encryption. Helpful note: Getting “HEARTBEATING” and “Read R BLOCK”? Use -ign_eof and read the “CONNECTED COMMANDS” section in the manpage. Next to ‘R’ and ‘Q’, the ‘B’ command also works in this version of that command…\n다음 수준의 암호는 SSL 암호화를 사용하여 로컬 호스트의 포트 30001에 현재 수준의 암호를 제출하여 검색할 수 있습니다.\n풀이 # 정보 # SSL이란? 보안 프로토콜을 통한 통신 (https가 ssl을 이용한거) openssl 명령어를 사용 openssl s_client -connect 도메인:포트 openssl s_client -connect localhost:30001 #jN2kgmIXJ6fShzhT2avhotn4Zcka6tnt #Correct! #JQttfApK4SeyHwDlI9SXGR50qclOAil1 ","date":"29 November 2022","externalUrl":null,"permalink":"/posts/velog/097-bandit15/","section":"Posts","summary":"ssh bandit15@bandit.labs.overthewire.org -p 2220 #jN2kgmIXJ6fShzhT2avhotn4Zcka6tnt The password for the next level can be retrieved by submitting the password of the current level to port 30001 on localhost using SSL encryption. Helpful note: Getting “HEARTBEATING” and “Read R BLOCK”? Use -ign_eof and read the “CONNECTED COMMANDS” section in the manpage. Next to ‘R’ and ‘Q’, the ‘B’ command also works in this version of that command…\n다음 수준의 암호는 SSL 암호화를 사용하여 로컬 호스트의 포트 30001에 현재 수준의 암호를 제출하여 검색할 수 있습니다.\n","title":"bandit15","type":"posts"},{"content":"링크\nssh bandit14@bandit.labs.overthewire.org -p 2220 #fGrHPx402xGC7U7rXKDaxiWFTOiF0ENq The password for the next level can be retrieved by submitting the password of the current level to port 30000 on localhost.\n현재 레벨의 비밀번호를 localhost의 포트 30000에 제출해라?\n풀이 # 정보 # nc(netcat) 명령어를 통하여 네트워크에 데이터를 읽고 쓸 수 있다. nc localhost 3000 #3000번 포트에 접속 ls -alths #임의의 명령어 Wrong! Please enter the correct current password nc localhost 30000 # fGrHPx402xGC7U7rXKDaxiWFTOiF0ENq # 현재비밀번호 Correct! jN2kgmIXJ6fShzhT2avhotn4Zcka6tnt ","date":"28 November 2022","externalUrl":null,"permalink":"/posts/velog/098-bandit14/","section":"Posts","summary":"링크\nssh bandit14@bandit.labs.overthewire.org -p 2220 #fGrHPx402xGC7U7rXKDaxiWFTOiF0ENq The password for the next level can be retrieved by submitting the password of the current level to port 30000 on localhost.\n현재 레벨의 비밀번호를 localhost의 포트 30000에 제출해라?\n풀이 # 정보 # nc(netcat) 명령어를 통하여 네트워크에 데이터를 읽고 쓸 수 있다. nc localhost 3000 #3000번 포트에 접속 ls -alths #임의의 명령어 Wrong! Please enter the correct current password nc localhost 30000 # fGrHPx402xGC7U7rXKDaxiWFTOiF0ENq # 현재비밀번호 Correct! jN2kgmIXJ6fShzhT2avhotn4Zcka6tnt","title":"bandit14","type":"posts"},{"content":"링크\nssh bandit13@bandit.labs.overthewire.org -p 2220 #wbWdlBxEir4CaE8LaPhauuOo6pwRmrDw The password for the next level is stored in /etc/bandit_pass/bandit14 and can only be read by user bandit14. For this level, you don’t get the next password, but you get a private SSH key that can be used to log into the next level. Note: localhost is a hostname that refers to the machine you are working on\n다음 레벨의 암호는 /etc/bandit_pass/bandit14에 저장되며 사용자 밴디트14만 읽을 수 있습니다. 이 수준에서는 다음 암호가 아니라 다음 수준에 로그인하는 데 사용할 수 있는 개인 SSH 키가 제공됩니다. 참고: localhost는 작업 중인 시스템을 나타내는 호스트 이름입니다.\n풀이 # 정보 # ssh key는 (리모트머신)공개키와 (로컬머신)비공개키로 이루어진다. ssh 접속을 시도하면, 이 둘을 비교하여 확인단다. -i 옵션을 이용해서 파일을 보낼수있다 ssh -i ssh.private bandit14@localhost -p 2220 #접속성공 #whoami cat /etc/bandit_pass/bandit14 #fGrHPx402xGC7U7rXKDaxiWFTOiF0ENq ","date":"28 November 2022","externalUrl":null,"permalink":"/posts/velog/099-bandit13/","section":"Posts","summary":"링크\nssh bandit13@bandit.labs.overthewire.org -p 2220 #wbWdlBxEir4CaE8LaPhauuOo6pwRmrDw The password for the next level is stored in /etc/bandit_pass/bandit14 and can only be read by user bandit14. For this level, you don’t get the next password, but you get a private SSH key that can be used to log into the next level. Note: localhost is a hostname that refers to the machine you are working on\n다음 레벨의 암호는 /etc/bandit_pass/bandit14에 저장되며 사용자 밴디트14만 읽을 수 있습니다. 이 수준에서는 다음 암호가 아니라 다음 수준에 로그인하는 데 사용할 수 있는 개인 SSH 키가 제공됩니다. 참고: localhost는 작업 중인 시스템을 나타내는 호스트 이름입니다.\n","title":"bandit13","type":"posts"},{"content":" lv 12 # 링크\nssh bandit12@bandit.labs.overthewire.org -p 2220 #JVNBBFSmZwKKOP0XbFXOoW8chDz5yVRv The password for the next level is stored in the file data.txt, which is a hexdump of a file that has been repeatedly compressed. For this level it may be useful to create a directory under /tmp in which you can work using mkdir. For example: mkdir /tmp/myname123. Then copy the datafile using cp, and rename it using mv (read the manpages!)\n여러번 압축된 data.txt를 풀어라 (권한문제로 /tmp 에 폴더를 만들어서 작업할것)\n풀이 # 하나도 모르겠어서 인터넷을 참고했다.\n1. 작업할 폴더에 data.txt를 복사한다. # bandit12@bandit:~$ cp data.txt /tmp/jiheon ; cd /tmp/jiheon 2. xxd(16진수와 2진수간의 변환) 을 통해 이진파일로 바꿔준다 # file 명령어를 통해서, gzip으로 압축된 파일임을 알 수 있다. -r 옵션은 16=\u0026gt;2 일때 사용 cat data.txt | xxd -r \u0026gt;data file data # data: gzip compressed data, was \u0026#34;data2.bin\u0026#34;, last modified: Thu Sep 1 06:30:09 2022, max compression, from Unix, original size modulo 2^32 575 3. gzip 압축풀기 # 압축을 푸는 -d 옵션을 쓴다 확장자가 gz인 경우에만 gzip을 쓸 수 있으므로 이름을 변경한다 gzip -d data file data #data: bzip2 compressed data, block size = 900k 이번엔 bzip2로 압축되었다고 한다.\n4.bzip2 압축풀기 +@ # -d 확장자 bz로 변경하고 진행 mv data data.bz bzip2 -d data.bz file data #data: gzip compressed data, was \u0026#34;data4.bin\u0026#34;, last modified: Thu Sep 1 06:30:09 2022, max compression, from Unix, original size modulo 2^32 20480 또 gzip이다\nmv data data.gz gzip -d data.gz file data #data: POSIX tar archive (GNU) tar\n5. tar 압축풀기 # -x옵션 =\u0026gt; 압축풀기 -f옵션 =\u0026gt;dlfmawlwjd\nmv data data.tar tar -xf data.tar file data6.bin 이 과정을 계속 반복한다 # The password is wbWdlBxEir4CaE8LaPhauuOo6pwRmrDw 와\n","date":"23 November 2022","externalUrl":null,"permalink":"/posts/velog/100-bandit12/","section":"Posts","summary":"lv 12 # 링크\nssh bandit12@bandit.labs.overthewire.org -p 2220 #JVNBBFSmZwKKOP0XbFXOoW8chDz5yVRv The password for the next level is stored in the file data.txt, which is a hexdump of a file that has been repeatedly compressed. For this level it may be useful to create a directory under /tmp in which you can work using mkdir. For example: mkdir /tmp/myname123. Then copy the datafile using cp, and rename it using mv (read the manpages!)\n","title":"bandit12","type":"posts"},{"content":" lv 11 # 링크\nThe password for the next level is stored in the file data.txt, where all lowercase (a-z) and uppercase (A-Z) letters have been rotated by 13 positions\n대소문자가 13자리씩 밀려서 나타나있다 (카이사르암호)\n풀이 # 파이썬으로 ord()를 가지고 풀면 풀 수 있을듯? 근데 그럼 의미가 없는것같아서 인터넷을 참조했다.\nbandit11@bandit:~$ cat data.txt | tr \u0026#39;[A-Za-z]\u0026#39; \u0026#39;[N-ZA-Mn-za-m]\u0026#39; #The password is JVNBBFSmZwKKOP0XbFXOoW8chDz5yVRv tr 명령어 # 대부분 파이프와 사용되는 명령어이며, 치환이다 tr \u0026lsquo;변경전글자\u0026rsquo; \u0026lsquo;변경할 글자\u0026rsquo; 로 바꿀 수 있다. ex tr \u0026lsquo;ab\u0026rsquo; \u0026lsquo;6\u0026rsquo; =\u0026gt; 모든 a,b 를 6으로 바꿔라 ","date":"23 November 2022","externalUrl":null,"permalink":"/posts/velog/101-bandit-11/","section":"Posts","summary":"lv 11 # 링크\nThe password for the next level is stored in the file data.txt, where all lowercase (a-z) and uppercase (A-Z) letters have been rotated by 13 positions\n대소문자가 13자리씩 밀려서 나타나있다 (카이사르암호)\n풀이 # 파이썬으로 ord()를 가지고 풀면 풀 수 있을듯? 근데 그럼 의미가 없는것같아서 인터넷을 참조했다.\n","title":"bandit 11","type":"posts"},{"content":" lv10 # 링크\nssh bandit10@bandit.labs.overthewire.org -p 2220 #EN632PlfYiZbn3PhVK3XOGSlNInNE00t The password for the next level is stored in the file data.txt, which contains base64 encoded data\ndata.txt가 base64로 인코딩되어있다.\n가볍게 디코딩해주면 된다. bandit10@bandit:~$ cat data.txt | base64 --decode #The password is 6zPeziLdR2RKNdNYFNb6nVCKzphlXHBM 인터넷을 찾아보니깐 더 좋은 방법이 있었다.\nbandit10@bandit:~$ base64 --decode data.txt ","date":"23 November 2022","externalUrl":null,"permalink":"/posts/velog/102-bandit10/","section":"Posts","summary":"lv10 # 링크\nssh bandit10@bandit.labs.overthewire.org -p 2220 #EN632PlfYiZbn3PhVK3XOGSlNInNE00t The password for the next level is stored in the file data.txt, which contains base64 encoded data\ndata.txt가 base64로 인코딩되어있다.\n가볍게 디코딩해주면 된다. bandit10@bandit:~$ cat data.txt | base64 --decode #The password is 6zPeziLdR2RKNdNYFNb6nVCKzphlXHBM 인터넷을 찾아보니깐 더 좋은 방법이 있었다.\nbandit10@bandit:~$ base64 --decode data.txt","title":"bandit10","type":"posts"},{"content":"링크\nssh bandit9@bandit.labs.overthewire.org -p 2220 #EN632PlfYiZbn3PhVK3XOGSlNInNE00t The password for the next level is stored in the file data.txt in one of the few human-readable strings, preceded by several ‘=’ characters.\n여러\u0026rsquo;=\u0026rsquo; 앞에있는 사람이 읽을수 있는 문자가 비밀번호임\nstrings를 통해, 사람이 읽을 수 있는 문자열만 출력 grep- E 를 통해(regex) =이 여러번 반복되는 문자열 출력 bandit9@bandit:~$ strings data.txt | grep -E ={3} #========== the #bu========== password #4iu========== is ㅋㅋㅋ #========== G7w8LIi6J3kTb8A7j9LgrywtEUlyyp6s ","date":"22 November 2022","externalUrl":null,"permalink":"/posts/velog/103-bandit-9/","section":"Posts","summary":"링크\nssh bandit9@bandit.labs.overthewire.org -p 2220 #EN632PlfYiZbn3PhVK3XOGSlNInNE00t The password for the next level is stored in the file data.txt in one of the few human-readable strings, preceded by several ‘=’ characters.\n여러’=’ 앞에있는 사람이 읽을수 있는 문자가 비밀번호임\nstrings를 통해, 사람이 읽을 수 있는 문자열만 출력 grep- E 를 통해(regex) =이 여러번 반복되는 문자열 출력 bandit9@bandit:~$ strings data.txt | grep -E ={3} #========== the #bu========== password #4iu========== is ㅋㅋㅋ #========== G7w8LIi6J3kTb8A7j9LgrywtEUlyyp6s","title":"bandit 9","type":"posts"},{"content":" lv8 # 링크\nssh bandit8@bandit.labs.overthewire.org -p 2220 #TESKZC0XvTetK0S9xNwm25STk5iWrBvP The password for the next level is stored in the file data.txt and is the only line of text that occurs only once\n단 한번만 등장하는 텍스트를 찾아라\nsort로 정렬 uniq -u로 중복제거 bandit8@bandit:~$ sort data.txt | uniq -u #EN632PlfYiZbn3PhVK3XOGSlNInNE00t uniq # 기본적으로 중복되는건 하나만 표시함 (sort랑 같이쓰임) -u 중복되지 않는것만 표시함 -d 중복된것만 표시 -c 중복된개수 세기 -i 대소문자 무시 ","date":"22 November 2022","externalUrl":null,"permalink":"/posts/velog/104-bandit8/","section":"Posts","summary":"lv8 # 링크\nssh bandit8@bandit.labs.overthewire.org -p 2220 #TESKZC0XvTetK0S9xNwm25STk5iWrBvP The password for the next level is stored in the file data.txt and is the only line of text that occurs only once\n단 한번만 등장하는 텍스트를 찾아라\nsort로 정렬 uniq -u로 중복제거 bandit8@bandit:~$ sort data.txt | uniq -u #EN632PlfYiZbn3PhVK3XOGSlNInNE00t uniq # 기본적으로 중복되는건 하나만 표시함 (sort랑 같이쓰임) -u 중복되지 않는것만 표시함 -d 중복된것만 표시 -c 중복된개수 세기 -i 대소문자 무시 ","title":"bandit8","type":"posts"},{"content":"","date":"22 November 2022","externalUrl":null,"permalink":"/tags/%EB%A6%AC%EB%88%85%EC%8A%A4/","section":"Tags","summary":"","title":"리눅스","type":"tags"},{"content":" 초기 sudo 비밀번호 설정하는 방법 # sudo passwd #\\n \u0026#39;비밀번호\u0026#39; mysql 초기 설정 # mysql_secure_installation #으로 root비밀번호 설정 create user 아이디@localhost identified by \u0026#39;비밀번호\u0026#39; # 로컬 create user \u0026#39;아이디\u0026#39;@\u0026#39;%\u0026#39; identified by \u0026#39;비밀번호\u0026#39; # 로컬빼고다 grant all privileges on *.* to user@localhost identified by \u0026#39;비밀번호\u0026#39; with grant option;``` #모든 데이터베이스에 대한 접근을 허용한다는 의미로 절대 하면 안됨(나는연습이니까 ㅎㅎ) ","date":"22 November 2022","externalUrl":null,"permalink":"/posts/velog/105-%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B8%B0%EC%B4%88-%EC%84%A4%EC%A0%951/","section":"Posts","summary":"초기 sudo 비밀번호 설정하는 방법 # sudo passwd #\\n '비밀번호' mysql 초기 설정 # mysql_secure_installation #으로 root비밀번호 설정 create user 아이디@localhost identified by '비밀번호' # 로컬 create user '아이디'@'%' identified by '비밀번호' # 로컬빼고다 grant all privileges on *.* to user@localhost identified by '비밀번호' with grant option;``` #모든 데이터베이스에 대한 접근을 허용한다는 의미로 절대 하면 안됨(나는연습이니까 ㅎㅎ)","title":"리눅스 기초 설정1","type":"posts"},{"content":"어려워진다\nlv6 # 조건 # owned by user bandit7 owned by group bandit6 33 bytes in size 지금까지는 find를 이름으로만 찾았는데, 옵션이 너무 많다. -size 33c -user bandit7 -group bandit6 -type f 에서 에러를 없앤다.\nssh bandit6@bandit.labs.overthewire.org -p 2220 #P4L4vucdmLnm8I7Vl7jG1ApGSfjYKqJU bandit6@bandit:~$ find / -size 33c -user bandit7 -type f -group bandit6 2\u0026gt;\u0026gt; /dev/null #var/lib/dpkg/info/bandit7.password cat /var/lib/dpkg/info/bandit7.password # z7WtoNQU2XfjmMtWA8u5rN4vzqu4v99S lv7 # # The password for the next level is stored in the file data.txt next to the word millionth\ngrep사용\nssh bandit7@bandit.labs.overthewire.org -p 2220 #z7WtoNQU2XfjmMtWA8u5rN4vzqu4v99S cat data.txt | grep millionth #TESKZC0XvTetK0S9xNwm25STk5iWrBvP ","date":"21 November 2022","externalUrl":null,"permalink":"/posts/velog/106-bandit-lv-67/","section":"Posts","summary":"어려워진다\nlv6 # 조건 # owned by user bandit7 owned by group bandit6 33 bytes in size 지금까지는 find를 이름으로만 찾았는데, 옵션이 너무 많다. -size 33c -user bandit7 -group bandit6 -type f 에서 에러를 없앤다.\nssh bandit6@bandit.labs.overthewire.org -p 2220 #P4L4vucdmLnm8I7Vl7jG1ApGSfjYKqJU bandit6@bandit:~$ find / -size 33c -user bandit7 -type f -group bandit6 2\u003e\u003e /dev/null #var/lib/dpkg/info/bandit7.password cat /var/lib/dpkg/info/bandit7.password # z7WtoNQU2XfjmMtWA8u5rN4vzqu4v99S lv7 # # The password for the next level is stored in the file data.txt next to the word millionth\n","title":"bandit lv 6,7","type":"posts"},{"content":" lv1 # -라는 파일을 읽어야 된다 그냥읽으면 안읽히므로 ./-로 읽어야한다.\nssh bandit1@bandit.labs.overthewire.org -p 2220 #lv0 비번 # cat ./- #rRGizSaX8Mk1RTb1CNQoXTcYZWU6lgzi lv2 # space in this filename이라는 파일을 읽어야한다. 이스케이프 문자를 활용한다 (또는 \u0026lsquo;\u0026lsquo;사용)\nssh bandit2@bandit.labs.overthewire.org -p 2220 cat spaces\\ in\\ this\\ filename #aBZ0W5EmUfAf7kHTQeOwd8bauFJ2lAiG lv3 # ₩inher₩e이라는 폴더가 있는데 안에 아무것도 없다 ls -al로 숨김파일 .hidden을 찾을 수 있다.\nssh bandit3@bandit.labs.overthewire.org -p 2220 cat inhere/.hidden # 2EW7BBsr6aMMoJ2HjW067dm8EgX26xNe lv4 # 파일이 엄청 많다. file 명령어로 ascii타입을 찾으면 된다.\nssh bandit4@bandit.labs.overthewire.org -p 2220 file inhere/* #lrIWWI6bB37kxfiCQZqUdOIYfr6eEeqR lv5 # inhere 폴더의\n사람이 읽을 수 있고, 1033 바이트의 실행 불가능한 파일을 찾아야한다. ssh bandit5@bandit.labs.overthewire.org -p 2220 bandit5@bandit:~/inhere$ find -type f -size 1033c #P4L4vucdmLnm8I7Vl7jG1ApGSfjYKqJU size 옵션 -b block -c byte -k kbyte\n","date":"21 November 2022","externalUrl":null,"permalink":"/posts/velog/107-bandit-lv-1~5/","section":"Posts","summary":"lv1 # -라는 파일을 읽어야 된다 그냥읽으면 안읽히므로 ./-로 읽어야한다.\nssh bandit1@bandit.labs.overthewire.org -p 2220 #lv0 비번 # cat ./- #rRGizSaX8Mk1RTb1CNQoXTcYZWU6lgzi lv2 # space in this filename이라는 파일을 읽어야한다. 이스케이프 문자를 활용한다 (또는 ‘‘사용)\nssh bandit2@bandit.labs.overthewire.org -p 2220 cat spaces\\ in\\ this\\ filename #aBZ0W5EmUfAf7kHTQeOwd8bauFJ2lAiG lv3 # ₩inher₩e이라는 폴더가 있는데 안에 아무것도 없다 ls -al로 숨김파일 .hidden을 찾을 수 있다.\n","title":"bandit lv 1~5","type":"posts"},{"content":"재미있는걸 찾아서 해볼려고한다. overthewire 라는 곳에서 제공하는 게임인데\n0단계를 풀면 1단계에 접근할 수 있는 게임으로 총 34단계이다.\n0단계는 그냥 접속만 하면 되긴 하는데, 예전에 로그인해서 host key verification failed 문제가 생겼다.\n이럴경우엔 ~./ssh의 known_host 파일에서 접속하려는 서버의 IP를 지워주면 재생성된다.\nssh bandit0@bandit.labs.overthewire.org -p 2220 #bandit0 \u0026lt;=비번 cat readme #NH2SXQwcBdpmTEzi3bvBHMM9H66vVXjL ","date":"21 November 2022","externalUrl":null,"permalink":"/posts/velog/108-bandit-lv0/","section":"Posts","summary":"재미있는걸 찾아서 해볼려고한다. overthewire 라는 곳에서 제공하는 게임인데\n0단계를 풀면 1단계에 접근할 수 있는 게임으로 총 34단계이다.\n0단계는 그냥 접속만 하면 되긴 하는데, 예전에 로그인해서 host key verification failed 문제가 생겼다.\n이럴경우엔 ~./ssh의 known_host 파일에서 접속하려는 서버의 IP를 지워주면 재생성된다.\nssh bandit0@bandit.labs.overthewire.org -p 2220 #bandit0 \u003c=비번 cat readme #NH2SXQwcBdpmTEzi3bvBHMM9H66vVXjL","title":"bandit lv0","type":"posts"},{"content":"","date":"18 November 2022","externalUrl":null,"permalink":"/tags/pip/","section":"Tags","summary":"","title":"Pip","type":"tags"},{"content":" pip경로 꼬였다면 # 주피터노트북 경로가 꼬여서, pip이 엉뚱한 곳에 설치된다면\nimport sys sys.executable 로 현재 파이썬 경로를 알 수 있다.\n!{sys.executable} -m pip install pymysql 을 통해서 현재경로에 설치 가능하다.\n인터넷에서 참고했는데 어서했는진 모르겠다. 제일좋은건 경로 다시잡아주는건데 귀찮다.\n","date":"18 November 2022","externalUrl":null,"permalink":"/posts/velog/109-pip%EA%B2%BD%EB%A1%9C%EA%BC%AC%EC%98%80%EC%9D%84%EB%95%8C/","section":"Posts","summary":"pip경로 꼬였다면 # 주피터노트북 경로가 꼬여서, pip이 엉뚱한 곳에 설치된다면\nimport sys sys.executable 로 현재 파이썬 경로를 알 수 있다.\n!{sys.executable} -m pip install pymysql 을 통해서 현재경로에 설치 가능하다.\n인터넷에서 참고했는데 어서했는진 모르겠다. 제일좋은건 경로 다시잡아주는건데 귀찮다.\n","title":"pip경로꼬였을때","type":"posts"},{"content":"","date":"18 November 2022","externalUrl":null,"permalink":"/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"https://www.acmicpc.net/problem/12015\n12015번: 가장 긴 증가하는 부분 수열 2첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000,000)www.acmicpc.net\n예전에 풀어봤던 문제인데, 풀어야 하는 방식이 다르다\n예전에는 모든 배열을 돌면서 n^2의 시간복잡도로 풀었는데, 여기서는 시간초과가 난다.\n따라서 2분탐색을 통해서 시간복잡도를 nlogn으로 줄여야 한다.\n먼져, 원 수열을 반복문에 넣어 기본적으로 부분 수열을 구해나가는데,\n현재 보고있는 원소가\n만일 현재 구한 부분수열의 마지막원소(젤큼) 보다 크다면, 그냥 부분수열에 append시켜주면 된다.\n만일 작다면, (부분 수열 중 원소보다 큰 수 중 가장 작은 수) 에 원소를 넣는다.\n이를 반복하면서 lis의 총 길이를 구하면 된다.\n직접 이분탐색 구현 코드\nn = int(input())a = list(map(int, input().split()))lis = [0]for i in a: if lis[-1] \u0026lt; i: # 만일 lis의 마지막 숫자가 i보다 작을 경우 lis.append(i) # 추가 else: # 이분탐색해서 가장 잘 어울리는 위치에 넣기 start, end = 0, len(lis) while start \u0026lt; end: mid = (start + end) // 2 if lis[mid] \u0026lt; i: start = mid + 1 else: end = mid lis[start] = iprint(len(lis) - 1) bisect 모듈 이용 코드\nfrom bisect import bisect_leftn = int(input()) a = list(map(int, input().split()))res = [0]for i in a: if res[-1] \u0026lt; i: res.append(i) else: res[bisect_left(res, i)] = i print(len(res)-1) ","date":"7 November 2022","externalUrl":null,"permalink":"/posts/2022-tistory/185-12015-%EA%B0%80%EC%9E%A5%EA%B8%B4%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94%EB%B6%80%EB%B6%84%EC%88%98%EC%97%B42/","section":"Posts","summary":"https://www.acmicpc.net/problem/12015\n12015번: 가장 긴 증가하는 부분 수열 2첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000,000)www.acmicpc.net\n예전에 풀어봤던 문제인데, 풀어야 하는 방식이 다르다\n예전에는 모든 배열을 돌면서 n^2의 시간복잡도로 풀었는데, 여기서는 시간초과가 난다.\n따라서 2분탐색을 통해서 시간복잡도를 nlogn으로 줄여야 한다.\n먼져, 원 수열을 반복문에 넣어 기본적으로 부분 수열을 구해나가는데,\n현재 보고있는 원소가\n만일 현재 구한 부분수열의 마지막원소(젤큼) 보다 크다면, 그냥 부분수열에 append시켜주면 된다.\n","title":"12015 가장긴증가하는부분수열2","type":"posts"},{"content":"https://www.acmicpc.net/problem/13904\n13904번: 과제예제에서 다섯 번째, 네 번째, 두 번째, 첫 번째, 일곱 번째 과제 순으로 수행하고, 세 번째, 여섯 번째 과제를 포기하면 185점을 얻을 수 있다.www.acmicpc.net\n2가지 풀이가 있음\n힙으로 푸는건 순회공연때 해봤으므로, 그리디로 풀어봄(정답코드참고함)\n먼져, 숙제를 무조건 w가 큰 순서로 정렬한다\n그리고, 가장 좋은경우\n즉 숙제를 할 수 있는 마지막 날에 숙제를 한다고 생각한다.\n만일, 숙제를 할 수 있는 마지막날에 이미 숙제를 했다면, day를 1씩 줄여나가면서 가능한 날짜를 찾는다\n(w기준으로 최대값부터 탐색을 하면서 가능)\n만일 숙제를 할 수 없다면 어쩔 수 없고, 할 수 있다면 정답값에 더해준다.\nimport sysinput = sys.stdin.readlinen = int(input())hw = [] visit = [False] * 1001for _ in range(n): d, w = map(int, input().split()) hw.append((d, w)) hw.sort(key=lambda x: x[1], reverse=True) # 무조건 weight가 크게 정렬함answer = 0 for d, w in hw: while d \u0026gt; 0 and visit[d]: # 과제를 할 수 있는 최대한의 날짜를 찾는다 d -= 1 # 처음d값은 날짜이고, 이후 d날에 이미 과제를 했다면 그전 과제를 할 수 있는 날짜를 구한다.. if d: # 만일 과제를 할 수 있을경우 (w기준 최댓값순으로 정렬했으므로) visit[d] = True # 그날에 과제를 한다. answer += wprint(answer) 힙\nimport sysfrom heapq import heappush, heappopinput = sys.stdin.readlineh = [] hw = sorted([[*map(int, input().split())] for _ in range(int(input()))]) for d, w in hw: heappush(h, w) if d \u0026lt; len(h): heappop(h) print(sum(h)) ","date":"1 November 2022","externalUrl":null,"permalink":"/posts/2022-tistory/184-13904-%EA%B3%BC%EC%A0%9C/","section":"Posts","summary":"https://www.acmicpc.net/problem/13904\n13904번: 과제예제에서 다섯 번째, 네 번째, 두 번째, 첫 번째, 일곱 번째 과제 순으로 수행하고, 세 번째, 여섯 번째 과제를 포기하면 185점을 얻을 수 있다.www.acmicpc.net\n2가지 풀이가 있음\n힙으로 푸는건 순회공연때 해봤으므로, 그리디로 풀어봄(정답코드참고함)\n먼져, 숙제를 무조건 w가 큰 순서로 정렬한다\n그리고, 가장 좋은경우\n즉 숙제를 할 수 있는 마지막 날에 숙제를 한다고 생각한다.\n만일, 숙제를 할 수 있는 마지막날에 이미 숙제를 했다면, day를 1씩 줄여나가면서 가능한 날짜를 찾는다\n","title":"13904 과제","type":"posts"},{"content":"https://www.acmicpc.net/problem/1654\n1654번: 랜선 자르기첫째 줄에는 오영식이 이미 가지고 있는 랜선의 개수 K, 그리고 필요한 랜선의 개수 N이 입력된다. K는 1이상 10,000이하의 정수이고, N은 1이상 1,000,000이하의 정수이다. 그리고 항상 K ≦ N 이다. 그www.acmicpc.net\n실버문제지만, 이분탐색 개념이 잘 안잡혀있어서 헷갈렸다.\n시간초과를 피하기 위해, 이분탐색을 써야한다.\n1 과 가장 긴 랜선의 값을 start, end로 놓는다. 2.(start+end)//2 , 즉 시작과 끝의 중간점을 랜선의 길이 (mid)라고 설정한다.\nmid 길이로 만들 때, 조건을 검사하여 n개 이상의 수가 나온다면,최소 길이(start)를 mid+1 로 설정한다.\n반대로 n개 미만이 나온다면, 최대 길이(end)를 mid-1 로 설정한다.\n3.start를 mid로 놓고, 위 과정을 다시 반복한다.\n이를 진행하다 보면, start가 end 보다 커지게 될 것이다. 그러면 그 시점에서 end의 값이 만들 수 있는 최댓값이다.\nimport sysinput = sys.stdin.readlinek, n = map(int, input().split()) lan = [int(input()) for _ in range(k)]start, end = 1, max(lan) while start \u0026lt;= end: # 범위가 벗어나지 않을때까지 반복하면서 mid = (start + end) // 2 # 이분탐색을 위한 중간값 cnt = 0 for l in lan: cnt += l // mid # 만들 수 있는 총 갯수 if cnt \u0026gt;= n: # 만들수 있는 갯수보다 크다면, 랜선길이의 최솟값을 mid로 잡고 다시 반복 (같아도 최댓값을 구하기 때문에 계속) start = mid + 1 # 무한반복을 피하기 위함 else: # 갯수만큼 만들 수 없다면, 랜선길이의 최댓값을 mid로 잡고 다시 반복 end = mid - 1 print(end) ","date":"18 October 2022","externalUrl":null,"permalink":"/posts/2022-tistory/183-1654%EB%9E%9C%EC%84%A0%EC%9E%90%EB%A5%B4%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/1654\n1654번: 랜선 자르기첫째 줄에는 오영식이 이미 가지고 있는 랜선의 개수 K, 그리고 필요한 랜선의 개수 N이 입력된다. K는 1이상 10,000이하의 정수이고, N은 1이상 1,000,000이하의 정수이다. 그리고 항상 K ≦ N 이다. 그www.acmicpc.net\n실버문제지만, 이분탐색 개념이 잘 안잡혀있어서 헷갈렸다.\n시간초과를 피하기 위해, 이분탐색을 써야한다.\n1 과 가장 긴 랜선의 값을 start, end로 놓는다. 2.(start+end)//2 , 즉 시작과 끝의 중간점을 랜선의 길이 (mid)라고 설정한다.\nmid 길이로 만들 때, 조건을 검사하여 n개 이상의 수가 나온다면,최소 길이(start)를 mid+1 로 설정한다.\n","title":"1654랜선자르기","type":"posts"},{"content":"https://www.acmicpc.net/problem/14267\n14267번: 회사 문화 1영선회사에는 매우 좋은 문화가 있는데, 바로 상사가 직속 부하를 칭찬하면 그 부하가 부하의 직속 부하를 연쇄적으로 칭찬하는 내리 칭찬이 있다. 즉, 상사가 한 직속 부하를 칭찬하면 그 부하www.acmicpc.net\ndp와 트리구조를 이용해서 풀음\n리스트를 돌아가면서, 자기값에 자기 직속선배값을 더하면 됨\n오랫만에알고리즘푸는거라 시간이 많이 걸렸음\nimport sysinput = sys.stdin.readlinen, m = map(int, input().split()) tmp = [*map(int, input().split())]arr = [0 for _ in range(n)]for _ in range(m): a, b = map(int, input().split()) arr[a - 1] += bfor i in range(1, n): arr[i] += arr[tmp[i] - 1]print(*arr) ","date":"17 October 2022","externalUrl":null,"permalink":"/posts/2022-tistory/182-14267-%ED%9A%8C%EC%82%AC%EB%AC%B8%ED%99%941/","section":"Posts","summary":"https://www.acmicpc.net/problem/14267\n14267번: 회사 문화 1영선회사에는 매우 좋은 문화가 있는데, 바로 상사가 직속 부하를 칭찬하면 그 부하가 부하의 직속 부하를 연쇄적으로 칭찬하는 내리 칭찬이 있다. 즉, 상사가 한 직속 부하를 칭찬하면 그 부하www.acmicpc.net\ndp와 트리구조를 이용해서 풀음\n리스트를 돌아가면서, 자기값에 자기 직속선배값을 더하면 됨\n오랫만에알고리즘푸는거라 시간이 많이 걸렸음\nimport sysinput = sys.stdin.readlinen, m = map(int, input().split()) tmp = [*map(int, input().split())]arr = [0 for _ in range(n)]for _ in range(m): a, b = map(int, input().split()) arr[a - 1] += bfor i in range(1, n): arr[i] += arr[tmp[i] - 1]print(*arr)","title":"14267 회사문화1","type":"posts"},{"content":"https://www.acmicpc.net/source/49072104\n로그인www.acmicpc.net\n투 포인터가 뭔지 궁금해서 풀이를 봤다.\n생각보다 간단했다.\n먼저 정렬된 리스트를가지고 한다.\n처음에는 리스트[0],[-1]을 가지고 한다. (a,b)\n우리의 목적은 0에 가장 가까운 값을 찾는것이다.\n만일 a+b가 이전의 정답보다 작다면\n정답을 교체해준다.\n그리고 a+b가 0보다 크다면, 더 작은 수가 필요할 것이므로 end에서 1을 빼서 다시 계산,\n반대라면 더 큰 수가 필요하므로 start+1해서 다시 계산한다.\nstart와 end의 위치가 바뀔때까지 탐색해서, 현 탐색 중 가장 a+b가 0에 가까웠던 a,b를 찾아 출력한다.\nimport sysinput = sys.stdin.readlineanswer = 1e10answer1 = [None, None] n = int(input())arr = sorted([*map(int, input().split())])start, end = 0, n - 1 while end \u0026gt; start: tmp = arr[start] + arr[end] if abs(tmp) \u0026lt; answer: answer = abs(tmp) answer1 = [arr[start], arr[end]] if tmp \u0026lt; 0: # 0보다작다면 start += 1 # 더큰방향으로 elif tmp \u0026gt; 0: # 9보다크다면 end -= 1 # 더작아지는방향으로 else: # 0이면 볼거없음 answer = [arr[start], arr[end]] print(min(answer1), max(answer1)) exit()print(min(answer1), max(answer1)) ","date":"13 September 2022","externalUrl":null,"permalink":"/posts/2022-tistory/181-2407%EB%91%90%EC%9A%A9%EC%95%A1/","section":"Posts","summary":"https://www.acmicpc.net/source/49072104\n로그인www.acmicpc.net\n투 포인터가 뭔지 궁금해서 풀이를 봤다.\n생각보다 간단했다.\n먼저 정렬된 리스트를가지고 한다.\n처음에는 리스트[0],[-1]을 가지고 한다. (a,b)\n우리의 목적은 0에 가장 가까운 값을 찾는것이다.\n만일 a+b가 이전의 정답보다 작다면\n정답을 교체해준다.\n그리고 a+b가 0보다 크다면, 더 작은 수가 필요할 것이므로 end에서 1을 빼서 다시 계산,\n반대라면 더 큰 수가 필요하므로 start+1해서 다시 계산한다.\nstart와 end의 위치가 바뀔때까지 탐색해서, 현 탐색 중 가장 a+b가 0에 가까웠던 a,b를 찾아 출력한다.\n","title":"2407두용액","type":"posts"},{"content":"https://www.acmicpc.net/problem/7662\n7662번: 이중 우선순위 큐입력 데이터는 표준입력을 사용한다. 입력은 T개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 입력 데이터의 수를 나타내는 정수 T가 주어진다. 각 테스트 데이터의 첫째 줄에는 Q에 적www.acmicpc.net\n\b그냥 정렬해서 풀면 간단한데, 시간 때문에 개고생을 해야된다.\n힙과 defaultdict 를 이용한다.\nminheap과 maxheap을 만들어서, 원소를 각각 추가해 준다 이때 defaultdict로 수가 몇개 들어있는지 확인한다.\n그리고 최소값은 minheap에서, 최댓값은 maxheap에서 뺀다.\n이때, 만일 다른 힙에서 삭제된 값일수도 있으니, defaultdict에 현재 pop한 값이 1개 이상일 때까지 (유효한 수가 존재)\npop해준다.\n만일 유효한 수를 찾았다면, defaultdict[유효한수] 에서 1을 빼준다.\nimport heapqimport sysfrom heapq import *from collections import defaultdict input = sys.stdin.readlinet = int(input())for _ in range(t): k = int(input()) minh, maxh, dd = [], [], defaultdict(int) for _ in range(k): a, b = input().rstrip().split() b = int(b) if a == \u0026#34;I\u0026#34;: heappush(minh, b) heappush(maxh, -b) dd[b] += 1 else: if b == 1: # 최댓값을 삭제하는 연산이라면 while True: try: tmp = -heappop(maxh) if dd[tmp] \u0026gt; 0: dd[tmp] -= 1 break except: break elif b == -1: # 최소값을 선택하면 while True: # 삭제된 값이 아닐때까지 try: tmp = heappop(minh) if dd[tmp] \u0026gt; 0: dd[tmp] -= 1 break except: break try: while True: minval = heappop(minh) if dd[minval]: break except: print(\u0026#34;EMPTY\u0026#34;) continue while True: maxval = -heappop(maxh) if dd[maxval]: break print(maxval, minval) ","date":"8 September 2022","externalUrl":null,"permalink":"/posts/2022-tistory/180-7662%EC%9D%B4%EC%A4%91%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84%ED%81%90/","section":"Posts","summary":"https://www.acmicpc.net/problem/7662\n7662번: 이중 우선순위 큐입력 데이터는 표준입력을 사용한다. 입력은 T개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 입력 데이터의 수를 나타내는 정수 T가 주어진다. 각 테스트 데이터의 첫째 줄에는 Q에 적www.acmicpc.net\n\b그냥 정렬해서 풀면 간단한데, 시간 때문에 개고생을 해야된다.\n힙과 defaultdict 를 이용한다.\nminheap과 maxheap을 만들어서, 원소를 각각 추가해 준다 이때 defaultdict로 수가 몇개 들어있는지 확인한다.\n그리고 최소값은 minheap에서, 최댓값은 maxheap에서 뺀다.\n이때, 만일 다른 힙에서 삭제된 값일수도 있으니, defaultdict에 현재 pop한 값이 1개 이상일 때까지 (유효한 수가 존재)\n","title":"7662이중우선순위큐","type":"posts"},{"content":"https://www.acmicpc.net/problem/2109\n2109번: 순회강연한 저명한 학자에게 n(0 ≤ n ≤ 10,000)개의 대학에서 강연 요청을 해 왔다. 각 대학에서는 d(1 ≤ d ≤ 10,000)일 안에 와서 강연을 해 주면 p(1 ≤ p ≤ 10,000)만큼의 강연료를 지불하겠다고 알려왔다.www.acmicpc.net\n이전 \u0026lsquo;보석도둑\u0026rsquo; 문제를 참고해서 풀었다.\n먼저 날짜가 높은 순서대로 정렬하고,\n강의를 마지막 날짜부터 1씩 감소시키면서,\n현 날짜와 똑같은 날짜의 강의를 힙에 넣는다.\n그후, 날짜가 하나씩 감소할 때마다, 힙에 남아있는 가장 큰 값을 넣는다.\nimport sysimport heapqinput = sys.stdin.readlinelect = []n = int(input()) if n==0: print(n) exit()for _ in range(n): p, d = map(int, input().split()) heapq.heappush(lect, (-d, p)) # 날짜, 강연료 lect.sort() # 높은 날짜순서로 정렬h = [] # 최대힙answer = 0day = -lect[0][0] # 마지막 나ㅣㄹ짜 while day \u0026gt; 0: # 강의가 존재할 때까지 while lect and -lect[0][0] == day: # 강의가 존재하고, 강의의 제한시간과 같을 때 heapq.heappush(h, -heapq.heappop(lect)[1]) if h: answer -= heapq.heappop(h) day -= 1print(answer) 그런데 다 풀고 나서 다른풀이를 보니 더 간단한 방법이 존재했다.\n강의를 날짜기 낮은 순으로 정렬하고,\n반복을 돌면서, 무조건 heap에 pay를 넣는다. (여기서의 힙은 최소힙이다)\n그 후, 만일 현재 날짜가 heap의 길이보다 크다면 (즉 비어있는 스케줄이 없다면)\nheap의 가장 작은 날짜를 pop해줘서 날짜를 맞춰준다.\nimport sysfrom heapq import *input =sys.stdin.readlinen=int(input()) lect=sorted([[* map(int,input().split())]for _ in range(n)], key=lambda x: x[1]) #날짜기준으로 정렬 h=[] #최소힙for p,d in lect: heappush(h,p) # 일단 무조건 힙에 pay를 넣는다. if len(h)\u0026gt;d: #만약, 강의가 모두 잡혀있는 상태라면 heappop(h) # 가장 pay가 적은 강의를 뺀다 print(sum(h)) ","date":"4 September 2022","externalUrl":null,"permalink":"/posts/2022-tistory/179-2109%EC%88%9C%ED%9A%8C%EA%B0%95%EC%97%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/2109\n2109번: 순회강연한 저명한 학자에게 n(0 ≤ n ≤ 10,000)개의 대학에서 강연 요청을 해 왔다. 각 대학에서는 d(1 ≤ d ≤ 10,000)일 안에 와서 강연을 해 주면 p(1 ≤ p ≤ 10,000)만큼의 강연료를 지불하겠다고 알려왔다.www.acmicpc.net\n이전 ‘보석도둑’ 문제를 참고해서 풀었다.\n먼저 날짜가 높은 순서대로 정렬하고,\n강의를 마지막 날짜부터 1씩 감소시키면서,\n현 날짜와 똑같은 날짜의 강의를 힙에 넣는다.\n그후, 날짜가 하나씩 감소할 때마다, 힙에 남아있는 가장 큰 값을 넣는다.\nimport sysimport heapqinput = sys.stdin.readlinelect = []n = int(input()) if n==0: print(n) exit()for _ in range(n): p, d = map(int, input().split()) heapq.heappush(lect, (-d, p)) # 날짜, 강연료 lect.sort() # 높은 날짜순서로 정렬h = [] # 최대힙answer = 0day = -lect[0][0] # 마지막 나ㅣㄹ짜 while day \u003e 0: # 강의가 존재할 때까지 while lect and -lect[0][0] == day: # 강의가 존재하고, 강의의 제한시간과 같을 때 heapq.heappush(h, -heapq.heappop(lect)[1]) if h: answer -= heapq.heappop(h) day -= 1print(answer) 그런데 다 풀고 나서 다른풀이를 보니 더 간단한 방법이 존재했다.\n","title":"2109순회강연","type":"posts"},{"content":"import sysimport heapqinput = sys.stdin.readlinejul = [] n, k = map(int, input().split())for _ in range(n): heapq.heappush(jul, [*map(int, input().split())]) bag = [int(input()) for _ in range(k)] # 작은순서대로 정렬bag.sort()h = []answer = 0 for b in bag: while jul and jul[0][0] \u0026lt;= b: # 가방에 담을수 있는 모든 보석 heapq.heappush(h, -heapq.heappop(jul)[1]) # 무게만 담기 if h: # 만일 담을수 있는 보석이 있다면 answer -= heapq.heappop(h) # 현재 가방에 담을 수 있는 보석 중 가장 가치높은 보석만 꺼내기 print(answer) https://www.acmicpc.net/problem/1202\n1202번: 보석 도둑첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000) 다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000) 다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ciwww.acmicpc.net\n다른사람들 풀이를 참고했다.\n맨 처음에는 그냥 다 탐색해서 풀었는데, 당연히 시간초과가 났다.\n우리가 구하려는 것은 \u0026lsquo;가장작은 가방에 들어갈 수 있는 최대 가치의 보석\u0026rsquo;이다.\n따라서 가장 작은 가방부터 시작한다.\n임시 최대힙인 h를 만들어 놓고, (최대힙)\n보석은 무계순으로 정렬해 놓는다.\n보석의 첫번째 원소가 (가장 가벼운 원소가) 가방보다 무거울 때까지,\n보석의 첫번째 원소를 하나씩 pop하고, 임시 최대힙에 push한다(가치만).\n이 과정이 끝나면, h에는 \u0026lsquo;현재 가방에 들어갈 수 있는 모든 보석\u0026rsquo; 이 가치순으로 역정렬되어있을 것이다.\n그중 가장 큰 보석을 pop해준다.\n이 과정을 반복하다 보면, 결국 각 가방에 가치가 가장 큰 보석만 들어가게 되므로, 이를 더해준다.\n","date":"4 September 2022","externalUrl":null,"permalink":"/posts/2022-tistory/178-1202%EB%B3%B4%EC%84%9D%EB%8F%84%EB%91%91/","section":"Posts","summary":"import sysimport heapqinput = sys.stdin.readlinejul = [] n, k = map(int, input().split())for _ in range(n): heapq.heappush(jul, [*map(int, input().split())]) bag = [int(input()) for _ in range(k)] # 작은순서대로 정렬bag.sort()h = []answer = 0 for b in bag: while jul and jul[0][0] \u003c= b: # 가방에 담을수 있는 모든 보석 heapq.heappush(h, -heapq.heappop(jul)[1]) # 무게만 담기 if h: # 만일 담을수 있는 보석이 있다면 answer -= heapq.heappop(h) # 현재 가방에 담을 수 있는 보석 중 가장 가치높은 보석만 꺼내기 print(answer) https://www.acmicpc.net/problem/1202\n1202번: 보석 도둑첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000) 다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000) 다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ciwww.acmicpc.net\n","title":"1202보석도둑","type":"posts"},{"content":"h2 데이터베이스 사용\n~/study/h2/bin의 h2.sh\n순수 jdbc(무려 15년전) 참고만하고 넘어가자\n2.jdbcTemplate\n3.JPA\nJPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행\n개발 생산성 up!!\nJPA는 표준 인터베이스 (ORM) Object Relational Mapping\n구현은 각 업체마다 다르다\ngradle에\nimplementation \u0026#39;org.springframework.boot:spring-boot-starter-data-jpa\u0026#39; 추가\napplication.properties\nspring.datasource.url=jdbc:h2:tcp://localhost/~/test spring.datasource.driver-class-name=org.h2.Driverspring.datasource.username=sa spring.jpa.show-sql=truespring.jpa.hibernate.ddl-auto=none entity 매핑 (@Entity) @ID ==PK , @GeneratedValue == db에서 자동생성\n@Column(name=\u0026ldquo;username\u0026rdquo;) == 컬럼명\njpa는 EntityManager로 모든게 동작함\n스프링 데이터 JPA (JPA 업그레이드 버전)****\n","date":"2 September 2022","externalUrl":null,"permalink":"/posts/2022-tistory/177-%EC%8A%A4%ED%94%84%EB%A7%81db%EC%A0%91%EA%B7%BC%EA%B8%B0%EC%88%A0/","section":"Posts","summary":"h2 데이터베이스 사용\n~/study/h2/bin의 h2.sh\n순수 jdbc(무려 15년전) 참고만하고 넘어가자\n2.jdbcTemplate\n3.JPA\nJPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행\n개발 생산성 up!!\nJPA는 표준 인터베이스 (ORM) Object Relational Mapping\n구현은 각 업체마다 다르다\ngradle에\nimplementation 'org.springframework.boot:spring-boot-starter-data-jpa' 추가\napplication.properties\nspring.datasource.url=jdbc:h2:tcp://localhost/~/test spring.datasource.driver-class-name=org.h2.Driverspring.datasource.username=sa spring.jpa.show-sql=truespring.jpa.hibernate.ddl-auto=none entity 매핑 (@Entity) @ID ==PK , @GeneratedValue == db에서 자동생성\n@Column(name=“username”) == 컬럼명\njpa는 EntityManager로 모든게 동작함\n스프링 데이터 JPA (JPA 업그레이드 버전)****\n","title":"스프링DB접근기술","type":"posts"},{"content":"acmicpc.net/problem/13265\n이분그래프 문제같다.\n나는 dfs가 좋다.\ndfs를 진행하면서, 색을 계속 바꿔가며 (not c)칠한다.\n만일 이미 색칠되어있는 칸을 만났는데, 현재색과 같으면 불가능한것이므로 바로 종료한다\n아니면 계속dfs를 진행한다\n이전에 이분그래프문제를 풀어봤더니 쉽게 풀었다.\nimport sysfrom collections import dequeinput = sys.stdin.readlinedef bfs(i): q = deque() q.append((i, True)) while q: now, c = q.popleft() for next in arr[now]: if visited[next] == c: return False elif visited[next] == -1: q.append((next, not c)) visited[next] = not c return Truet = int(input())for _ in range(t): n, m = map(int, input().split()) arr = [[] for _ in range(n + 1)] visited = [-1 for _ in range(n + 1)] for __ in range(m): a, b = map(int, input().split()) arr[a].append(b) arr[b].append(a) for i in range(1, n + 1): if visited[i] == -1: visited[i] = True tmp = bfs(i) if not tmp: break print(\u0026#34;possible\u0026#34; if tmp else \u0026#34;impossible\u0026#34;) ","date":"31 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/176-13265%EC%83%89%EC%B9%A0%ED%95%98%EA%B8%B0/","section":"Posts","summary":"acmicpc.net/problem/13265\n이분그래프 문제같다.\n나는 dfs가 좋다.\ndfs를 진행하면서, 색을 계속 바꿔가며 (not c)칠한다.\n만일 이미 색칠되어있는 칸을 만났는데, 현재색과 같으면 불가능한것이므로 바로 종료한다\n아니면 계속dfs를 진행한다\n이전에 이분그래프문제를 풀어봤더니 쉽게 풀었다.\nimport sysfrom collections import dequeinput = sys.stdin.readlinedef bfs(i): q = deque() q.append((i, True)) while q: now, c = q.popleft() for next in arr[now]: if visited[next] == c: return False elif visited[next] == -1: q.append((next, not c)) visited[next] = not c return Truet = int(input())for _ in range(t): n, m = map(int, input().split()) arr = [[] for _ in range(n + 1)] visited = [-1 for _ in range(n + 1)] for __ in range(m): a, b = map(int, input().split()) arr[a].append(b) arr[b].append(a) for i in range(1, n + 1): if visited[i] == -1: visited[i] = True tmp = bfs(i) if not tmp: break print(\"possible\" if tmp else \"impossible\")","title":"13265색칠하기","type":"posts"},{"content":"홈화면 추가\n/ 가 등록되지않았으면 index.html호출되고 아니면 무시됨\nform 안의 input태그의 name 속성이 키가 됨\n웹프강의가 조금은 도움이 되는것 같기도 하고\u0026hellip;.\n도데체 interface(MemberRepository) 를 호출하는데\nMemoryMemberRepository가 등록되는건지 모르겠다.\n","date":"31 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/175-%EC%9B%B9-mvc-%EA%B0%9C%EB%B0%9C/","section":"Posts","summary":"홈화면 추가\n/ 가 등록되지않았으면 index.html호출되고 아니면 무시됨\nform 안의 input태그의 name 속성이 키가 됨\n웹프강의가 조금은 도움이 되는것 같기도 하고….\n도데체 interface(MemberRepository) 를 호출하는데\nMemoryMemberRepository가 등록되는건지 모르겠다.\n","title":"웹 MVC 개발","type":"posts"},{"content":"https://www.acmicpc.net/problem/1374\n1374번: 강의실첫째 줄에 강의의 개수 N(1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 줄마다 세 개의 정수가 주어지는데, 순서대로 강의 번호, 강의 시작 시간, 강의 종료 시간을 의미한다. 강의www.acmicpc.net\n처음에는 걍 싹다비교해봤는데, 당연히 시간초과가 났다.\n이런류의 문제는 거의 처음 접해봐서, 힌트를 얻고 풀었다.\n먼져, 강의를 시작시간 순으로 정렬해준다.\n이후 끝나는 시간 순으로 힙을 만들어준다\n정렬해 놓은 강의들을 하나씩 비교하는데\n만일 강의의 시작시간이 힙의 첫번째원소, 즉 가장 빨리 끝나는 강의보다 크다면\n어쩔 수 없이 강의실을 하나 더 만들어야된다\n그게 아니라면, 가장 빨리 끝나는 강의를 pop해주고\n지금 보고 있는 강의를 힙에 넣어준다면,\n힙의 성질에 의해서 가장 빨리 끝나는 강의가 루트로 올 것이다.\n이후 힙의 갯수를 세면 된다.\nimport sysimport heapqinput = sys.stdin.readline lect, h = [], [] #강의들(시작시간기준) , 강의실(끝나는시간 기준)answer = []n = int(input()) for _ in range(n): # 강의를 시작하는순서대로 정렬 a, b, c = map(int, input().split()) heapq.heappush(lect, (b, c)) heapq.heappush(h, heapq.heappop(lect)[1]) #가장 빨리 시작하는 강의를 강의실에 배정while lect: start, end = heapq.heappop(lect) if start \u0026gt;= h[0]: #만일 가장 빨리끝나는강의실보다 같거나 늦게 시작한다면, heapq.heappop(h) #가장빠른강의실에 heapq.heappush(h, end) #새로운강의를 배치함 heap성질에 의해서 정렬될것임 else: #만일 가장 빨리끝나는강의실보다 빨리 시작한다면 heapq.heappush(h, end) #새 강의실을 배정해야함 print(len(h)) ","date":"31 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/174-1374%EA%B0%95%EC%9D%98%EC%8B%A4/","section":"Posts","summary":"https://www.acmicpc.net/problem/1374\n1374번: 강의실첫째 줄에 강의의 개수 N(1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 줄마다 세 개의 정수가 주어지는데, 순서대로 강의 번호, 강의 시작 시간, 강의 종료 시간을 의미한다. 강의www.acmicpc.net\n처음에는 걍 싹다비교해봤는데, 당연히 시간초과가 났다.\n이런류의 문제는 거의 처음 접해봐서, 힌트를 얻고 풀었다.\n먼져, 강의를 시작시간 순으로 정렬해준다.\n이후 끝나는 시간 순으로 힙을 만들어준다\n정렬해 놓은 강의들을 하나씩 비교하는데\n만일 강의의 시작시간이 힙의 첫번째원소, 즉 가장 빨리 끝나는 강의보다 크다면\n","title":"1374강의실","type":"posts"},{"content":"@Controller\n스프링이 시작될때, 객체를 생성\n스프링 컨테이너에서 빈을 관리한다고 부름\n@Autowired\n스프링 컨테이너의 memberService를 가져와서 연동한다\n@Service\n스프링이 올라올때 컨테이너에 등록해줌\n@Repository\n스프링에 repository 등록\n전부 @Component 가능\n스프링 빈을 등록하는 2가지 방법\n어노테이션 사용\n@component 어노테이션이 있으면 스프링 빈으로 자동 등록한다\n@controller 컨트롤러가 스프링 빈으로 자동 등록된 이유도 컴포넌트 스캔 때문이다\n@controoler, @Service, @Repository 모두 @component 하위임\nhello.hellospring 하위 패키지가 아니면, 등록안해줌\n스프링은 기본적으로 싱글톤으로 동작(유일하게 하나만 등록해서 공유)\n따라서, 같은 스프링 빈이면 모두 같은 인스턴스이다.\n자바코드사용\n\bSpringconfig.java라는 새 파일을 만든다 이름상관없음\n@Configuration, @Bean 이용\npackage hello.hellospring;import hello.hellospring.repository.MemberRepository; import hello.hellospring.repository.MemoryMemberRepository; import hello.hellospring.service.MemberService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class SpringConfig { @Bean public MemberService memberService(){ return new MemberService(memberRepository()); } @Bean public MemberRepository memberRepository(){ return new MemoryMemberRepository(); }} 뜰때 memberService와 memberRepository를 둘 다 스프링 빈에 등록하고,\nmemberService의 생성자에 memberRepository를 넣어준다.\nDI (dependency injection) 의 3가지 방식\n생성자 주입 - 생성자를 통해서 들어오는거 **권장됨\n필드주입 - 그냥 필드에다가 들어오는거\nsetter주입 -setter을 통해서 주입\n%주의% Autowired는 Bean에 등록된 개체가 아니면 동작하지 않음\n","date":"30 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/173-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84/","section":"Posts","summary":"@Controller\n스프링이 시작될때, 객체를 생성\n스프링 컨테이너에서 빈을 관리한다고 부름\n@Autowired\n스프링 컨테이너의 memberService를 가져와서 연동한다\n@Service\n스프링이 올라올때 컨테이너에 등록해줌\n@Repository\n스프링에 repository 등록\n전부 @Component 가능\n스프링 빈을 등록하는 2가지 방법\n어노테이션 사용\n@component 어노테이션이 있으면 스프링 빈으로 자동 등록한다\n@controller 컨트롤러가 스프링 빈으로 자동 등록된 이유도 컴포넌트 스캔 때문이다\n@controoler, @Service, @Repository 모두 @component 하위임\nhello.hellospring 하위 패키지가 아니면, 등록안해줌\n스프링은 기본적으로 싱글톤으로 동작(유일하게 하나만 등록해서 공유)\n","title":"스프링 빈과 의존관계","type":"posts"},{"content":"Optional : null도 반환가능하게해줌\ninterface에 메소드의 몸통만 구현하면 \u0026ndash; 상속받는 개체에서 해당 메소드를 실행하면 오류가 날것\n따라서 상속받는 개체에서 메소드를 재정의해야함\ntestcase를 작성하는법\n자바의 min메소드를 통해서 실행하면, 오래걸리고, 반복실행하기어렵고,\n한번에 실행하기 어려움\n따라서 Junit이라는 프레임워크를 실행하여 이러한 문제를 해결\n@AfterEach\n테스트가 끝날때마다 밑함수 호출\ncommand option v 그대로 반환\nifPresent()객체가 있는지 확인\ngiven, when, then\n주어졌는데, 실행했을때, 결과가나와야됨\n","date":"29 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/172-%ED%9A%8C%EC%9B%90-%EB%8F%84%EB%A9%94%EC%9D%B8%EA%B3%BC-%EC%A0%80%EC%9E%A5%EC%86%8C-%EB%A7%8C%EB%93%A4%EA%B8%B0/","section":"Posts","summary":"Optional : null도 반환가능하게해줌\ninterface에 메소드의 몸통만 구현하면 – 상속받는 개체에서 해당 메소드를 실행하면 오류가 날것\n따라서 상속받는 개체에서 메소드를 재정의해야함\ntestcase를 작성하는법\n자바의 min메소드를 통해서 실행하면, 오래걸리고, 반복실행하기어렵고,\n한번에 실행하기 어려움\n따라서 Junit이라는 프레임워크를 실행하여 이러한 문제를 해결\n@AfterEach\n테스트가 끝날때마다 밑함수 호출\ncommand option v 그대로 반환\nifPresent()객체가 있는지 확인\ngiven, when, then\n주어졌는데, 실행했을때, 결과가나와야됨\n","title":"회원 도메인과 저장소 만들기","type":"posts"},{"content":"https://www.acmicpc.net/problem/1655\n1655번: 가운데를 말해요첫째 줄에는 백준이가 외치는 정수의 개수 N이 주어진다. N은 1보다 크거나 같고, 100,000보다 작거나 같은 자연수이다. 그 다음 N줄에 걸쳐서 백준이가 외치는 정수가 차례대로 주어진다. 정수는 -1www.acmicpc.net\nheap을 두개쓴다\n[최대힙] (중간값) [최소힙]\n중간값도 결국엔 힙에들어가야 하므로\n최대힙의 루트를 중간값으로 정한다.\nex 1,2,3,\n[2,1],[3]\n왼쪽은 l , 오른쪽을 r 이라 하겠다.\n새로운 수가 들어오면,\nl이 r 보다 원소가 많으면 r에, 아니면 기준인 l 에 heappush 시킨다.\n이때, r의 루트가 (r에서 가장 작은 수가) l의루트(l에서 가장 큰 수) 보다 작다면\n서로의 루트를 바꿔준다. (l의 루트가 다시 중간값이 된다)\n어렵다\nimport sysimport heapqinput=sys.stdin.readline left,right=[],[] #최대힙, 최소힙 중간값을 left의 root로 할거임n=int(input().rstrip()) for _ in range(n): num=int(input().rstrip()) if len(left)==len(right): heapq.heappush(left,-num) else: heapq.heappush(right,num) if right and -left[0] \u0026gt; right[0]: #만일 left의 루트, 즉 중간값이 right의 루트, right의 최솟값보다 크다면 lr=-heapq.heappop(left) rr=-heapq.heappop(right) ##각각의 루트를 바꿔준다. (중간값을 바꾸기 위해서) heapq.heappush(right,lr) heapq.heappush(left,rr) print(-left[0]) ","date":"29 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/171-1655%EA%B0%80%EC%9A%B4%EB%8D%B0%EB%A5%BC-%EB%A7%90%ED%95%B4%EC%9A%94/","section":"Posts","summary":"https://www.acmicpc.net/problem/1655\n1655번: 가운데를 말해요첫째 줄에는 백준이가 외치는 정수의 개수 N이 주어진다. N은 1보다 크거나 같고, 100,000보다 작거나 같은 자연수이다. 그 다음 N줄에 걸쳐서 백준이가 외치는 정수가 차례대로 주어진다. 정수는 -1www.acmicpc.net\nheap을 두개쓴다\n[최대힙] (중간값) [최소힙]\n중간값도 결국엔 힙에들어가야 하므로\n최대힙의 루트를 중간값으로 정한다.\nex 1,2,3,\n[2,1],[3]\n왼쪽은 l , 오른쪽을 r 이라 하겠다.\n새로운 수가 들어오면,\nl이 r 보다 원소가 많으면 r에, 아니면 기준인 l 에 heappush 시킨다.\n이때, r의 루트가 (r에서 가장 작은 수가) l의루트(l에서 가장 큰 수) 보다 작다면\n","title":"1655가운데를 말해요","type":"posts"},{"content":"정적 컨텐츠 - /static\nmvc와 템플릿 엔진 (서버사이드)\nmodel 은 비지니스 로직 및 데이터 처리\nview는 화면을 그리는거\ncontroller는 request를 받는거\n@requestParam으로 get 파라미터를 받을 수 있음\nAPI(클라라이언트사이드)\n@responsebody의 의미\nhttp 프로토콜의 body에 직접 넣어 준다. 문자그대로 클라이언트에 내려감 (html요소 x)\n객체를 리턴하면 json방식으로 전달됨!!! (HttpMessageConverter)가 동작함\n","date":"28 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/170-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88/","section":"Posts","summary":"정적 컨텐츠 - /static\nmvc와 템플릿 엔진 (서버사이드)\nmodel 은 비지니스 로직 및 데이터 처리\nview는 화면을 그리는거\ncontroller는 request를 받는거\n@requestParam으로 get 파라미터를 받을 수 있음\nAPI(클라라이언트사이드)\n@responsebody의 의미\nhttp 프로토콜의 body에 직접 넣어 준다. 문자그대로 클라이언트에 내려감 (html요소 x)\n객체를 리턴하면 json방식으로 전달됨!!! (HttpMessageConverter)가 동작함\n","title":"스프링 웹 개발 기초","type":"posts"},{"content":"뭔가가 궁금하면\nspring.io -\u0026gt; project -\u0026gt;spring boot 의 공식문서를 찾아보자\n스프링 부트가 제공하는 웰컴 페이지\nresources/static/index.html 을 만들면, 자동으로 인식해줌\nthymeleaf 템플릿 엔진\nlocallhost:8080/hello - \u0026gt; 톰켓에서 getMapping(\u0026lsquo;hello\u0026quot;) 를 찾아서 컨트롤러를 찾아줌\nmodel.addAttribute(\u0026ldquo;data\u0026rdquo;,\u0026ldquo;hello\u0026rdquo;)를 통해서 속성지정해줌\nreturn \u0026ldquo;hello\u0026rdquo; 는 viewResolver에게 화면을 찾으라고 함\nresources/templates/hello.html을 찾으라는 의미임\nspring-boot-devtools라이브러리로 서버 재시작 막을 수 있음\n빌드\n./gradlew build 하면 build 폴더가 생김\nbuild/libs/에 있는 hello-spring-0.0.1-SNAPSHOT.jar 가 빌드된 파일\njava -jar hello-spring-0.0.1-SNAPSHOT.jar로 스프링 서버 실행 가능!!\n","date":"28 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/169-view-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95--%EB%B9%8C%EB%93%9C%ED%95%98%EA%B3%A0-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0/","section":"Posts","summary":"뭔가가 궁금하면\nspring.io -\u003e project -\u003espring boot 의 공식문서를 찾아보자\n스프링 부트가 제공하는 웰컴 페이지\nresources/static/index.html 을 만들면, 자동으로 인식해줌\nthymeleaf 템플릿 엔진\nlocallhost:8080/hello - \u003e 톰켓에서 getMapping(‘hello\") 를 찾아서 컨트롤러를 찾아줌\nmodel.addAttribute(“data”,“hello”)를 통해서 속성지정해줌\nreturn “hello” 는 viewResolver에게 화면을 찾으라고 함\nresources/templates/hello.html을 찾으라는 의미임\nspring-boot-devtools라이브러리로 서버 재시작 막을 수 있음\n빌드\n./gradlew build 하면 build 폴더가 생김\nbuild/libs/에 있는 hello-spring-0.0.1-SNAPSHOT.jar 가 빌드된 파일\njava -jar hello-spring-0.0.1-SNAPSHOT.jar로 스프링 서버 실행 가능!!\n","title":"view 환경설정 \u0026 빌드하고 실행하기","type":"posts"},{"content":"gradle은 의존관계가 있는 라이브러리를 함께 다운로드한다.\n현업에서는 system.out.println대신 로그를 쓴다.\n스프링부트 라이브러리\nsprint-boot-starter-web\nspring-boot-starter-tomcat : 톰캣서버\nspring-boot-webmvc : 스프링 웹 MVC\nspring-boot-starter-thymeleaf : 타임리프 템플릿 엔진\nspring-boot-starter(공통) : 스프링부트 + 스프링코어 + 로깅\nspring-boot\nspring-core spring-boot-starter-logging\nlogback, slf4j ","date":"28 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/168-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0/","section":"Posts","summary":"gradle은 의존관계가 있는 라이브러리를 함께 다운로드한다.\n현업에서는 system.out.println대신 로그를 쓴다.\n스프링부트 라이브러리\nsprint-boot-starter-web\nspring-boot-starter-tomcat : 톰캣서버\nspring-boot-webmvc : 스프링 웹 MVC\nspring-boot-starter-thymeleaf : 타임리프 템플릿 엔진\nspring-boot-starter(공통) : 스프링부트 + 스프링코어 + 로깅\nspring-boot\nspring-core spring-boot-starter-logging\nlogback, slf4j ","title":"라이브러리 살펴보기","type":"posts"},{"content":"https://www.acmicpc.net/problem/13460\n13460번: 구슬 탈출 2첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 ≤ N, M ≤ 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 \u0026lsquo;.\u0026rsquo;, \u0026lsquo;#\u0026rsquo;, \u0026lsquo;O\u0026rsquo;, \u0026lsquo;R\u0026rsquo;, \u0026lsquo;B\u0026rsquo;www.acmicpc.net\n빡세다. 예외케이스가 너무 많다.\n말로는 쉽다\n빨간구슬과 파랑구슬의 x,y좌표와 시간을 각각 큐에 넣고,\n더이상 진행할 수 없을때까지 한방향으로 움직인다음,\n조건에따라종료하면 된다. 근데 빡세다. 코드에 주석을 주렁주렁 달아놨더니 설명은 그만하겠다.\nimport sysfrom collections import dequeinput=sys.stdin.readlinearr=[] move=[[-1,0],[1,0],[0,-1],[0,1]]s=set()n,m=map(int,input().split()) for i in range(n): tmp=list(input().rstrip()) for j in range(m): if tmp[j]==\u0026#39;B\u0026#39;: blue=(i,j) tmp[j]=\u0026#39;.\u0026#39; elif tmp[j]==\u0026#39;R\u0026#39;: red=(i,j) tmp[j]=\u0026#39;.\u0026#39; elif tmp[j]==\u0026#39;O\u0026#39;: target=(i,j) else: continue arr.append(tmp)############################# arr만들기 def bfs(): q=deque() q.append((red[0],red[1],blue[0],blue[1],0)) while q: rcx,rcy,bcx,bcy,t = q.popleft() if t==10: # print(\u0026#39;10보다 커서 종료\u0026#39;) print(-1) exit() for dx,dy in move: rnx,rny = rcx,rcy # 다음에 갈 곳 bnx,bny= bcx,bcy R,B=False,False #구멍에 들어갔는지 여부 #####################################################while문 while True: rflag,bflag=False,False #r,b가 더이상 움직일수 없는가 if not R : #빨강이 아직 구멍에 안들어갔다면 tmp=arr[rnx+dx][rny+dy] # 다음 빨강의 위치 if tmp==\u0026#39;O\u0026#39;: #구멍에 들어갔을 경우 R=t+1 #R만들어줌 rnx,rny=-1,-1 #-1로 바꿔서 비교할때 중복 ㅌ elif tmp==\u0026#39;#\u0026#39;: #벽일경우 rflag=True #더이상 움직일 수 없음 else: #움직일 수 있는경우 rnx,rny=rnx+dx,rny+dy else: # 빨강이 구멍에 들어갔을경우, 조건 만족 rflag=True tmp=arr[bnx+dx][bny+dy] #파란색 if tmp==\u0026#39;O\u0026#39;: #구멍에 들어갔을 경우 B=1 # B만들어줌 break #볼필요없음 elif tmp==\u0026#39;#\u0026#39;: #벽일경우 bflag=True #더이상 움직일 수 없음 else: # 움직일 수 있는 경우 bnx,bny=bnx+dx,bny+dy if rnx==bnx and rny==bny : #만일 서로 같은 위치에 있다면 !! 둘다움직였다면 절대 같은 위치 불가능 if bflag: #만일 빨강이 움직였다면 rnx,rny= rnx-dx,rny-dy # 이전으로 초기화해줌 rflag=True elif rflag: bnx,bny= bnx-dx,bny-dy bflag=True if rflag and bflag : #서로 더이상 움직일 수 없는 상태라면 break #드디어 종료 ######################################################## While문 if B: #만일 b가 구멍에 들어갔다면 continue; #그냥넘김 elif R: #만일 b가 구멍에들어가지않고 R만 들어갔다면 # print(\u0026#39;R만 들어가서 종료\u0026#39;) print(R) #출력후 종료 exit() if (rnx,rny,bnx,bny) not in s: #만일 아무것도 구멍에 안들어갔다면 q.append(((rnx,rny,bnx,bny,t+1))) #큐에삽입 s.add((rnx,rny,bnx,bny)) #방문체크bfs()# print(s)# print(\u0026#39;끝나서 종료\u0026#39;) print(-1) ","date":"26 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/167-13460%EA%B5%AC%EC%8A%AC%ED%83%88%EC%B6%9C2/","section":"Posts","summary":"https://www.acmicpc.net/problem/13460\n13460번: 구슬 탈출 2첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 ≤ N, M ≤ 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 ‘.’, ‘#’, ‘O’, ‘R’, ‘B’www.acmicpc.net\n빡세다. 예외케이스가 너무 많다.\n말로는 쉽다\n빨간구슬과 파랑구슬의 x,y좌표와 시간을 각각 큐에 넣고,\n더이상 진행할 수 없을때까지 한방향으로 움직인다음,\n조건에따라종료하면 된다. 근데 빡세다. 코드에 주석을 주렁주렁 달아놨더니 설명은 그만하겠다.\nimport sysfrom collections import dequeinput=sys.stdin.readlinearr=[] move=[[-1,0],[1,0],[0,-1],[0,1]]s=set()n,m=map(int,input().split()) for i in range(n): tmp=list(input().rstrip()) for j in range(m): if tmp[j]=='B': blue=(i,j) tmp[j]='.' elif tmp[j]=='R': red=(i,j) tmp[j]='.' elif tmp[j]=='O': target=(i,j) else: continue arr.append(tmp)############################# arr만들기 def bfs(): q=deque() q.append((red[0],red[1],blue[0],blue[1],0)) while q: rcx,rcy,bcx,bcy,t = q.popleft() if t==10: # print('10보다 커서 종료') print(-1) exit() for dx,dy in move: rnx,rny = rcx,rcy # 다음에 갈 곳 bnx,bny= bcx,bcy R,B=False,False #구멍에 들어갔는지 여부 #####################################################while문 while True: rflag,bflag=False,False #r,b가 더이상 움직일수 없는가 if not R : #빨강이 아직 구멍에 안들어갔다면 tmp=arr[rnx+dx][rny+dy] # 다음 빨강의 위치 if tmp=='O': #구멍에 들어갔을 경우 R=t+1 #R만들어줌 rnx,rny=-1,-1 #-1로 바꿔서 비교할때 중복 ㅌ elif tmp=='#': #벽일경우 rflag=True #더이상 움직일 수 없음 else: #움직일 수 있는경우 rnx,rny=rnx+dx,rny+dy else: # 빨강이 구멍에 들어갔을경우, 조건 만족 rflag=True tmp=arr[bnx+dx][bny+dy] #파란색 if tmp=='O': #구멍에 들어갔을 경우 B=1 # B만들어줌 break #볼필요없음 elif tmp=='#': #벽일경우 bflag=True #더이상 움직일 수 없음 else: # 움직일 수 있는 경우 bnx,bny=bnx+dx,bny+dy if rnx==bnx and rny==bny : #만일 서로 같은 위치에 있다면 !! 둘다움직였다면 절대 같은 위치 불가능 if bflag: #만일 빨강이 움직였다면 rnx,rny= rnx-dx,rny-dy # 이전으로 초기화해줌 rflag=True elif rflag: bnx,bny= bnx-dx,bny-dy bflag=True if rflag and bflag : #서로 더이상 움직일 수 없는 상태라면 break #드디어 종료 ######################################################## While문 if B: #만일 b가 구멍에 들어갔다면 continue; #그냥넘김 elif R: #만일 b가 구멍에들어가지않고 R만 들어갔다면 # print('R만 들어가서 종료') print(R) #출력후 종료 exit() if (rnx,rny,bnx,bny) not in s: #만일 아무것도 구멍에 안들어갔다면 q.append(((rnx,rny,bnx,bny,t+1))) #큐에삽입 s.add((rnx,rny,bnx,bny)) #방문체크bfs()# print(s)# print('끝나서 종료') print(-1)","title":"13460구슬탈출2","type":"posts"},{"content":"https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard\n[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., - 강의 소개 | 인프런\u0026hellip;www.inflearn.com\n스프링 입문 강의를 보고 따라함\n스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 v2021-12-01_2.pdf\nIntellJ사용\n다운받은 파일을 study에 옮기고,\n거기에 있는 build.gradle에 IntellJ 프로젝트를 연다\nsrc/test - 테스트코드\nbuild.gradle 에서 알아서 설정해줌 (버전설정하고 라이브러리땡겨온다고만 생각)\ndependencies에서 의존성 다운받은것들 있음\n","date":"26 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/166-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%A4%80%EB%B9%84/","section":"Posts","summary":"https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard\n[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., - 강의 소개 | 인프런…www.inflearn.com\n스프링 입문 강의를 보고 따라함\n스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 v2021-12-01_2.pdf\nIntellJ사용\n다운받은 파일을 study에 옮기고,\n거기에 있는 build.gradle에 IntellJ 프로젝트를 연다\n","title":"프로젝트 준비","type":"posts"},{"content":"{{ date }} 티스토리-\u0026gt;velog\n","date":"24 August 2022","externalUrl":null,"permalink":"/posts/velog/110-%EC%B2%AB-%EA%B8%80/","section":"Posts","summary":"{{ date }} 티스토리-\u003evelog\n","title":"첫 글","type":"posts"},{"content":"https://www.acmicpc.net/problem/5052\n5052번: 전화번호 목록첫째 줄에 테스트 케이스의 개수 t가 주어진다. (1 ≤ t ≤ 50) 각 테스트 케이스의 첫째 줄에는 전화번호의 수 n이 주어진다. (1 ≤ n ≤ 10000) 다음 n개의 줄에는 목록에 포함되어 있는 전화번호가www.acmicpc.net\n하나하나비교하는방법밖에 안나서 인터넷을 찾아봤는데\n정말 간단했다.\n문자열을 입력받고 정렬하면,\n인접한 두 문자는 가장 가까운 문자일 것이다.\n따라서 arr[i]와 arr[i+1]만 비교하면 된다. 신기하다.\n그냥비교해줘도 되고, 좀더 빠르게 하려면\nstartswith()함수를 통해서 비교해주면 끝\nimport sysinput =sys.stdin.readlinet=int(input())for _ in range(t): n=int(input()) arr=sorted([input().rstrip() for _ in range(n)]) #정렬하면 바로 옆만 비교하면 됨! flag=False for i in range(n-1): if arr[i+1].startswith(arr[i]): flag=True break print(\u0026#39;NO\u0026#39; if flag else \u0026#39;YES\u0026#39;) ","date":"24 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/165-5052%EC%A0%84%ED%99%94%EB%B2%88%ED%98%B8%EB%AA%A9%EB%A1%9D/","section":"Posts","summary":"https://www.acmicpc.net/problem/5052\n5052번: 전화번호 목록첫째 줄에 테스트 케이스의 개수 t가 주어진다. (1 ≤ t ≤ 50) 각 테스트 케이스의 첫째 줄에는 전화번호의 수 n이 주어진다. (1 ≤ n ≤ 10000) 다음 n개의 줄에는 목록에 포함되어 있는 전화번호가www.acmicpc.net\n하나하나비교하는방법밖에 안나서 인터넷을 찾아봤는데\n정말 간단했다.\n문자열을 입력받고 정렬하면,\n인접한 두 문자는 가장 가까운 문자일 것이다.\n따라서 arr[i]와 arr[i+1]만 비교하면 된다. 신기하다.\n그냥비교해줘도 되고, 좀더 빠르게 하려면\nstartswith()함수를 통해서 비교해주면 끝\nimport sysinput =sys.stdin.readlinet=int(input())for _ in range(t): n=int(input()) arr=sorted([input().rstrip() for _ in range(n)]) #정렬하면 바로 옆만 비교하면 됨! flag=False for i in range(n-1): if arr[i+1].startswith(arr[i]): flag=True break print('NO' if flag else 'YES')","title":"5052전화번호목록","type":"posts"},{"content":"https://www.acmicpc.net/problem/18405\n18405번: 경쟁적 전염첫째 줄에 자연수 N, K가 공백을 기준으로 구분되어 주어진다. (1 ≤ N ≤ 200, 1 ≤ K ≤ 1,000) 둘째 줄부터 N개의 줄에 걸쳐서 시험관의 정보가 주어진다. 각 행은 N개의 원소로 구성되며, 해당 위치www.acmicpc.net\nㅋㅋㅋㅋㅋㅋㅋ 정신똑바로차리고풀어야겠다\n말도안되는부분에서실수했다.\n그냥 bfs다 큐에 넣을때 바이러스 내림차순으로만 넣어주면된다.\n끝\nimport sysfrom collections import dequeinput=sys.stdin.readlinearr=[] move=[[-1,0],[1,0],[0,-1],[0,1]]n,k=map(int,input().split()) virus=[[] for _ in range(k+1)]for i in range(n): tmp=[*map(int,input().split())] arr.append(tmp) for j in range(n): t=tmp[j] if t!=0: virus[t].append((i,j,0)) s,x,y=map(int,input().split())q=deque()for vii in virus: if vii: for vi in vii: q.append(vi)def bfs(): while q: cx,cy,t=q.popleft() if t==s: print(arr[x-1][y-1]) exit() for dx,dy in move: nx,ny=cx+dx,cy+dy if 0\u0026lt;=nx\u0026lt;n and 0\u0026lt;=ny\u0026lt;n and not arr[nx][ny]: arr[nx][ny]=arr[cx][cy] q.append((nx,ny,t+1)) bfs() print(arr[x-1][y-1]) ","date":"23 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/164-18405%EA%B2%BD%EC%9F%81%EC%A0%81-%EC%A0%84%EC%97%BC/","section":"Posts","summary":"https://www.acmicpc.net/problem/18405\n18405번: 경쟁적 전염첫째 줄에 자연수 N, K가 공백을 기준으로 구분되어 주어진다. (1 ≤ N ≤ 200, 1 ≤ K ≤ 1,000) 둘째 줄부터 N개의 줄에 걸쳐서 시험관의 정보가 주어진다. 각 행은 N개의 원소로 구성되며, 해당 위치www.acmicpc.net\nㅋㅋㅋㅋㅋㅋㅋ 정신똑바로차리고풀어야겠다\n말도안되는부분에서실수했다.\n그냥 bfs다 큐에 넣을때 바이러스 내림차순으로만 넣어주면된다.\n끝\nimport sysfrom collections import dequeinput=sys.stdin.readlinearr=[] move=[[-1,0],[1,0],[0,-1],[0,1]]n,k=map(int,input().split()) virus=[[] for _ in range(k+1)]for i in range(n): tmp=[*map(int,input().split())] arr.append(tmp) for j in range(n): t=tmp[j] if t!=0: virus[t].append((i,j,0)) s,x,y=map(int,input().split())q=deque()for vii in virus: if vii: for vi in vii: q.append(vi)def bfs(): while q: cx,cy,t=q.popleft() if t==s: print(arr[x-1][y-1]) exit() for dx,dy in move: nx,ny=cx+dx,cy+dy if 0\u003c=nx\u003cn and 0\u003c=ny\u003cn and not arr[nx][ny]: arr[nx][ny]=arr[cx][cy] q.append((nx,ny,t+1)) bfs() print(arr[x-1][y-1])","title":"18405경쟁적 전염","type":"posts"},{"content":"https://www.acmicpc.net/problem/4574\n분명 푸는방법은 아는데,\n계속 오류가 생겨서 엄청 고생했다.\n스도쿠랑 푸는법은 같다.\n먼저 arr을 생성하고, 입력으로 이를 채운다.\narr을 탐색하며 공백을 리스트에 넣는다.\ndfs를 통해 공백의 원소를 2개씩 채워나간다. 첫 번째 원소를 채운 후, 이에 맞게 백트래킹을 진행하여 두번째 원소를 채우고, 다음단계로 넘어간다.\n만일 공백의 다음번 좌표가 이미 전 단계에서 채워서 채워져있다면, 그 다음 공백을 채운다.\n이렇게 반복하여, 더이상 남은 공백이 없을 때 정답을 출력한다.\n어제풀때는 그렇게 에러가 나더니 오늘은 술술 풀려서 더 짜증난다.\n그래도 혼자힘으로 풀어서 기분이 좋다.\nimport sysinput = sys.stdin.readlinecounts=0def check(x,y,v): for i in range(9): if arr[x][i]==v: return False elif arr[i][y]==v: return False x=x//3*3 y=y//3*3 for i in range(x,x+3): for j in range(y,y+3): if arr[i][j]==v: return False return True def dfs(cnt=0): global flag if cnt\u0026gt;lb: #모두 탐색했다면 정답출력후 종료 flag=True for a in arr: for b in a: print(b,end=\u0026#39;\u0026#39;) print() return cx,cy=blank[cnt] if arr[cx][cy]: #만일 이전에 채웠다면 다음요소 탐색 dfs(cnt+1) return for i in range(1,10): if check(cx,cy,i): arr[cx][cy]=i for dx,dy in ((0,1),(1,0)): nx,ny=cx+dx,cy+dy if nx\u0026lt;=8 and ny\u0026lt;=8 and arr[nx][ny]==0 : #도미노가능 for j in range(1,10): tmp=((max(i,j),min(i,j))) if tmp not in domino and check(nx,ny,j): domino.add(tmp) arr[nx][ny]=j dfs(cnt+1) if flag: return domino.remove(tmp) arr[nx][ny]=0 arr[cx][cy]=0while True: counts+=1 flag=False domino=set() arr=[[0 for _ in range(9)] for _ in range(9)] n=int(input()) if n==0: exit() else: print(f\u0026#39;Puzzle {counts}\u0026#39;) for _ in range(n): u,lu,v,lv=input().rstrip().split() u=int(u) d1,d2=ord(lu[0])-65,int(lu[1])-1 arr[d1][d2]=u d1,d2=ord(lv[0])-65,int(lv[1])-1 v=int(v) arr[d1][d2]=v domino.add((max(u,v),min(u,v))) tmp=input().rstrip().split() numb=1 for t in tmp: a,b=ord(t[0])-65, int(t[1])-1 arr[a][b]=numb numb+=1 ######arr만들기 blank=[] for i in range(9): for j in range(9): if arr[i][j]==0: blank.append((i,j)) lb=len(blank)-1 ##공백모두 리스트에 넣기 dfs() 골드1달성!!\n","date":"23 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/163-4574%EC%8A%A4%EB%8F%84%EB%AF%B8%EB%85%B8%EC%BF%A0/","section":"Posts","summary":"https://www.acmicpc.net/problem/4574\n분명 푸는방법은 아는데,\n계속 오류가 생겨서 엄청 고생했다.\n스도쿠랑 푸는법은 같다.\n먼저 arr을 생성하고, 입력으로 이를 채운다.\narr을 탐색하며 공백을 리스트에 넣는다.\ndfs를 통해 공백의 원소를 2개씩 채워나간다. 첫 번째 원소를 채운 후, 이에 맞게 백트래킹을 진행하여 두번째 원소를 채우고, 다음단계로 넘어간다.\n만일 공백의 다음번 좌표가 이미 전 단계에서 채워서 채워져있다면, 그 다음 공백을 채운다.\n이렇게 반복하여, 더이상 남은 공백이 없을 때 정답을 출력한다.\n어제풀때는 그렇게 에러가 나더니 오늘은 술술 풀려서 더 짜증난다.\n","title":"4574스도미노쿠","type":"posts"},{"content":"https://www.acmicpc.net/problem/12919\n12919번: A와 B 2수빈이는 A와 B로만 이루어진 영어 단어 존재한다는 사실에 놀랐다. 대표적인 예로 AB (Abdominal의 약자), BAA (양의 울음 소리), AA (용암의 종류), ABBA (스웨덴 팝 그룹)이 있다. 이런 사실에 놀란 수빈www.acmicpc.net\n생각보다 어려운 문제였다.\n생각해야될 점은, 1번조건과 2번조건 중 어느조건을 선택해야하는지 모른다는 것이다.\n예제인\nABABA 를 볼때,\nBABA의 첫번째문자는 B이므로 2번 조건이 될 수 도 있고, 마지막에 A이므로 1번 조건이 될 수 도 있다.\n따라서 두가지 경우를 모두 탐색해야 한다.\n재귀식을 통해 1번조건과 2번조건을 각각 수행한 경우를 만들어주면된다.\n코드\ns=input()t=input()def dfs(a:str,b:str): if len(a)==len(b): if a==b: print(1) exit() else: return if b[-1]==\u0026#39;A\u0026#39;: dfs(a,b[:-1]) if b[0]==\u0026#39;B\u0026#39;: dfs(a,b[::-1][:-1])dfs(s,t)print(0) ","date":"13 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/162-a%EC%99%80b-2/","section":"Posts","summary":"https://www.acmicpc.net/problem/12919\n12919번: A와 B 2수빈이는 A와 B로만 이루어진 영어 단어 존재한다는 사실에 놀랐다. 대표적인 예로 AB (Abdominal의 약자), BAA (양의 울음 소리), AA (용암의 종류), ABBA (스웨덴 팝 그룹)이 있다. 이런 사실에 놀란 수빈www.acmicpc.net\n생각보다 어려운 문제였다.\n생각해야될 점은, 1번조건과 2번조건 중 어느조건을 선택해야하는지 모른다는 것이다.\n예제인\nABABA 를 볼때,\nBABA의 첫번째문자는 B이므로 2번 조건이 될 수 도 있고, 마지막에 A이므로 1번 조건이 될 수 도 있다.\n따라서 두가지 경우를 모두 탐색해야 한다.\n","title":"A와B 2","type":"posts"},{"content":"https://www.acmicpc.net/problem/16197\n16197번: 두 동전N×M 크기의 보드와 4개의 버튼으로 이루어진 게임이 있다. 보드는 1×1크기의 정사각형 칸으로 나누어져 있고, 각각의 칸은 비어있거나, 벽이다. 두 개의 빈 칸에는 동전이 하나씩 놓여져 있고,www.acmicpc.net\nbfs를 두 동전으로 하면 된다\n방문처리를 안하고도 풀 수있지만, 시간이 어마어마하계 걸린다.\n나는 따로 방문배열을 만든 것이 아니라, set에 각 동전에 위치를 넣고, 비교하는 방식으로 했다.\n60%에서 계속 에러가 나서 고민했는데, 최대10번까지 허용인데 11번까지 허용해서 그랬다.\n즉, 11번하면 정답인 경우에는 -1을 출력해야한다.\n더러운문제였다.\n코드\nfrom collections import dequeimport sysinput=sys.stdin.readlinecoins=[]arr=[] n,m=map(int,input().split())for i in range(n): tmp=list(input().rstrip()) arr.append(tmp) for j in range(m): if tmp[j]==\u0026#39;o\u0026#39;: coins.append(i) coins.append(j) move=[[-1,0],[1,0],[0,-1],[0,1]]s=set()q=deque()q.append(coins) s.add(tuple(coins))coins.append(0)while q: cx1,cy1,cx2,cy2,w=q.popleft() if w\u0026gt;9: print(-1) exit() for dx,dy in move: nx1,ny1,nx2,ny2=cx1+dx,cy1+dy,cx2+dx,cy2+dy if (0\u0026lt;=nx1\u0026lt;n and 0\u0026lt;=ny1\u0026lt;m)^(0\u0026lt;=nx2\u0026lt;n and 0\u0026lt;=ny2\u0026lt;m): #동전중 하나만 떨어졌으면 print(w+1) #걸린시간출력 후 종료 exit() elif (0\u0026lt;=nx1\u0026lt;n and 0\u0026lt;=ny1\u0026lt;m)and(0\u0026lt;=nx2\u0026lt;n and 0\u0026lt;=ny2\u0026lt;m): #둘다arr안에 있으면 if arr[nx1][ny1]==\u0026#39;#\u0026#39;:#벽이면 그냥 처음으로 nx1,ny1=cx1,cy1 if arr[nx2][ny2]==\u0026#39;#\u0026#39;: nx2,ny2=cx2,cy2 if (nx1,ny1,nx2,ny2) not in s: q.append(( nx1,ny1,nx2,ny2,w+1)) s.add(( nx1,ny1,nx2,ny2))print(-1) ","date":"10 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/160-16197-%EB%91%90%EB%8F%99%EC%A0%84/","section":"Posts","summary":"https://www.acmicpc.net/problem/16197\n16197번: 두 동전N×M 크기의 보드와 4개의 버튼으로 이루어진 게임이 있다. 보드는 1×1크기의 정사각형 칸으로 나누어져 있고, 각각의 칸은 비어있거나, 벽이다. 두 개의 빈 칸에는 동전이 하나씩 놓여져 있고,www.acmicpc.net\nbfs를 두 동전으로 하면 된다\n방문처리를 안하고도 풀 수있지만, 시간이 어마어마하계 걸린다.\n나는 따로 방문배열을 만든 것이 아니라, set에 각 동전에 위치를 넣고, 비교하는 방식으로 했다.\n60%에서 계속 에러가 나서 고민했는데, 최대10번까지 허용인데 11번까지 허용해서 그랬다.\n즉, 11번하면 정답인 경우에는 -1을 출력해야한다.\n","title":"16197 두동전","type":"posts"},{"content":"https://www.acmicpc.net/problem/9019\n9019번: DSLR네 개의 명령어 D, S, L, R 을 이용하는 간단한 계산기가 있다. 이 계산기에는 레지스터가 하나 있는데, 이 레지스터에는 0 이상 10,000 미만의 십진수를 저장할 수 있다. 각 명령어는 이 레지스터에www.acmicpc.net\n간단한? dfs문제\n처음에는 L,R을 string으로 구현하려 했었지만,\n생각해보니 그냥 10으로 나누거나(몫) 곱해주고, 끝자리나 첫자리만 따로 처리해서 더해주면 된다.\n파이썬은 느려서 pypy로 제출했다. 파이썬으로 푼 사람이 3명밖에없다!!\nfrom collections import dequeimport sysinput=sys.stdin.readlinedef D(n:int): rv=n\u0026lt;\u0026lt;1 if rv\u0026gt;9999: rv%=10000 return rvdef S(n:int): rv=n-1 if rv==-1: rv=9999 return rvdef L(n:int): a,b=divmod(n,1000) #첫자리, 나머지3자리 rv=(b*10)+a #세지리 값 변경, 첫자리값넣기 return rvdef R(n:int): a,b=divmod(n,10) #나머지3자리, 끝자리 rv=b*1000+a #앞세자리 return rvdd={ \u0026#39;D\u0026#39;:D, \u0026#39;S\u0026#39;:S, \u0026#39;L\u0026#39;:L, \u0026#39;R\u0026#39;:R,}def bfs(start,end): arr=[None for _ in range(10000)] arr[start]=\u0026#39;\u0026#39; q=deque([start]) while q: now=q.popleft() for k,v in dd.items(): next= v(now) if 0\u0026lt;=next\u0026lt;10000 and arr[next]==None: arr[next]=arr[now]+k q.append(next) if next==end: print(arr[end]) return for _ in range(int(input())): start,end=map(int,input().split()) bfs(start,end) ","date":"9 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/158-9019-dslr/","section":"Posts","summary":"https://www.acmicpc.net/problem/9019\n9019번: DSLR네 개의 명령어 D, S, L, R 을 이용하는 간단한 계산기가 있다. 이 계산기에는 레지스터가 하나 있는데, 이 레지스터에는 0 이상 10,000 미만의 십진수를 저장할 수 있다. 각 명령어는 이 레지스터에www.acmicpc.net\n간단한? dfs문제\n처음에는 L,R을 string으로 구현하려 했었지만,\n생각해보니 그냥 10으로 나누거나(몫) 곱해주고, 끝자리나 첫자리만 따로 처리해서 더해주면 된다.\n파이썬은 느려서 pypy로 제출했다. 파이썬으로 푼 사람이 3명밖에없다!!\nfrom collections import dequeimport sysinput=sys.stdin.readlinedef D(n:int): rv=n\u003c\u003c1 if rv\u003e9999: rv%=10000 return rvdef S(n:int): rv=n-1 if rv==-1: rv=9999 return rvdef L(n:int): a,b=divmod(n,1000) #첫자리, 나머지3자리 rv=(b*10)+a #세지리 값 변경, 첫자리값넣기 return rvdef R(n:int): a,b=divmod(n,10) #나머지3자리, 끝자리 rv=b*1000+a #앞세자리 return rvdd={ 'D':D, 'S':S, 'L':L, 'R':R,}def bfs(start,end): arr=[None for _ in range(10000)] arr[start]='' q=deque([start]) while q: now=q.popleft() for k,v in dd.items(): next= v(now) if 0\u003c=next\u003c10000 and arr[next]==None: arr[next]=arr[now]+k q.append(next) if next==end: print(arr[end]) return for _ in range(int(input())): start,end=map(int,input().split()) bfs(start,end)","title":"9019 DSLR","type":"posts"},{"content":"https://www.acmicpc.net/problem/6087\n6087번: 레이저 통신크기가 1×1인 정사각형으로 나누어진 W×H 크기의 지도가 있다. 지도의 각 칸은 빈 칸이거나 벽이며, 두 칸은 \u0026lsquo;C\u0026rsquo;로 표시되어 있는 칸이다. \u0026lsquo;C\u0026rsquo;로 표시되어 있는 두 칸을 레이저로 통신하기 위해서www.acmicpc.net\n처음엔, 모든 방향으로 더이상갈수없을때까지 bfs를 반복하다보면 답이 나올거라 생각했다.\n근데 50%정도에서 계속 틀려서 반례를보니깐\n4 4C\u0026hellip;\u0026hellip;....C\n다음과같은반례가 있었다.\n요점은, 한 지점에 도착하는 최소경로가, 방향에 따라 최적의 정답이 아닐 수 있다는 것이다.\n따라서, 만일 bfs를 진행하다가, 다음 방문점이 지금의 방문점보다 클때만 탐색을 진행하고, 만일 크다면 덮어쓰는것이다.\n코드보면뭔소린지알거다\n백준 정답자들중에선 잘 푼듯 하다.\nimport sysfrom collections import dequeinput=sys.stdin.readline move=[[-1,0],[1,0],[0,-1],[0,1]]def bfs(): que=deque([start]) arr[start[0]][start[1]]=-1 while que: cx, cy=que.popleft() for a,b in move: nx,ny=cx+a,cy+b while 0\u0026lt;=nx\u0026lt;h and 0\u0026lt;=ny\u0026lt;w and arr[nx][ny]!=\u0026#39;*\u0026#39;: if (arr[nx][ny] in(\u0026#39;.\u0026#39;,\u0026#39;C\u0026#39;))or arr[cx][cy]\u0026lt;arr[nx][ny]: #더이상 한 방향으로 나아갈때가 없을때까지 if arr[nx][ny]==\u0026#39;C\u0026#39;: print(arr[cx][cy]+1) exit() arr[nx][ny]=arr[cx][cy]+1 que.append([nx,ny]) nx+=a ny+=b else: break w,h=map(int,input().split())C=[]arr=[] for i in range(h): tmp=list(input().rstrip()) arr.append(tmp) for j in range(w): if tmp[j]==\u0026#39;C\u0026#39;: C.append((i,j)) start,end=Cbfs()print(arr) ","date":"8 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/155-6087%EB%A0%88%EC%9D%B4%EC%A0%80%ED%86%B5%EC%8B%A0/","section":"Posts","summary":"https://www.acmicpc.net/problem/6087\n6087번: 레이저 통신크기가 1×1인 정사각형으로 나누어진 W×H 크기의 지도가 있다. 지도의 각 칸은 빈 칸이거나 벽이며, 두 칸은 ‘C’로 표시되어 있는 칸이다. ‘C’로 표시되어 있는 두 칸을 레이저로 통신하기 위해서www.acmicpc.net\n처음엔, 모든 방향으로 더이상갈수없을때까지 bfs를 반복하다보면 답이 나올거라 생각했다.\n근데 50%정도에서 계속 틀려서 반례를보니깐\n4 4C……....C\n다음과같은반례가 있었다.\n요점은, 한 지점에 도착하는 최소경로가, 방향에 따라 최적의 정답이 아닐 수 있다는 것이다.\n따라서, 만일 bfs를 진행하다가, 다음 방문점이 지금의 방문점보다 클때만 탐색을 진행하고, 만일 크다면 덮어쓰는것이다.\n","title":"6087레이저통신","type":"posts"},{"content":"id = id정보가져옴 (userid, groupid, groups)\nwho = 누가 여기 접속해있는가?\nsu[option][username] = 유저변경 암것도안주면 루트로\nsudo passwd -u root = 루트에 잠금걸려있는경우 풀기\nsudo passwd -l root = 잠금걸기\n사용자의 추가 (sudo필요)\nuseradd -m [name] 사용자의 홈디렉토리까지 만들어줌\u001e\npasswd [name] 사용자 비밀번호 지정\nsudo usermod -a -G sudo [nusername] = 사용자에게 sudo권한주기 (sudo그룹에 추가)\n권한 (permission)\n-rw-rw-r\u0026ndash; 1 ubuntu ubuntu 3 Aug 7 12:10 perm.txt\n= type rw-rw-r\u0026ndash; = access mode\nubuntu ubuntu = owner, group\nchomod 를 통해 권한변경가능 (bitmask) 777=rwxrwxrwx\n실행이란? 해석해서 실행하는것을 뜻함 즉 커널이 이해할 수 있는걸뜻함\n-R옵션 = 재귀적으로 안의 모든파일\n디렉토리의 권한\nr = 디렉토리안 파일 보기\nw = 디렉토리에 파일 생성\nx = 들어갈수있는권한\n그룹 group\n일정 사용자에게만 특별한 권한을 줄 수 있음\nsudo groupadd[groupname] 그룹만듬\n/etc/group에서 확인 가능\nusermod -a -G [groupname][usernmae]로 그룹에 유저 추가 가능 (재로그인필요)\nchown = 디렉토리의 사용자와 그룹을 바꿀 수 있음\nex) sudo chown root:developer . =현재 디렉토리의 소유자는 root, 그룹은 developer로 변경\n","date":"7 August 2022","externalUrl":null,"permalink":"/posts/2022-tistory/153-%EC%82%AC%EC%9A%A9%EC%9E%90/","section":"Posts","summary":"id = id정보가져옴 (userid, groupid, groups)\nwho = 누가 여기 접속해있는가?\nsu[option][username] = 유저변경 암것도안주면 루트로\nsudo passwd -u root = 루트에 잠금걸려있는경우 풀기\nsudo passwd -l root = 잠금걸기\n사용자의 추가 (sudo필요)\nuseradd -m [name] 사용자의 홈디렉토리까지 만들어줌\u001e\npasswd [name] 사용자 비밀번호 지정\nsudo usermod -a -G sudo [nusername] = 사용자에게 sudo권한주기 (sudo그룹에 추가)\n권한 (permission)\n-rw-rw-r– 1 ubuntu ubuntu 3 Aug 7 12:10 perm.txt\n= type rw-rw-r– = access mode\n","title":"사용자","type":"posts"},{"content":"vim 실행중 ctrl-z를 통하여 백그라운드로 전환 가능\n이후에 fg명령을 통해서 포그라운드로 전환 가능\njobs라는 명령어를 통해서 백그라운드에 있는 프로세스를 볼 수 있음\nfg %[숫자] 명령어를 통하여 fg명령으로 실행할 프로세스를 결정할 수 있음\nkill %[숫자]를 통하여 프로세스 종료 가능 -9 강제종료 -15 안전종료\nls -alR / \u0026gt;result.txt 2\u0026gt;error.log \u0026amp;\n\u0026amp; 명령어를 통해 진행중인 작업을 백그라운드에서 실행 (종료되면 알려줌)\ndaemon이란?\n항상 실행되고 있는 프로그램 냉장고를 생각\n서버 등\n/etc/init.d/ 에 위치해있음 (데몬이 위치하는 디렉토리)\nex)\nsudo service apache2 start /stop 을 통해 킴/끔\ncli로 부팅된 경우에는 /etc/rc3.d 를 통하여 실행됨 (링크연결되어있음) (gui면 rc5.d)\ncron을 통한 정기적 실행\ncron이란? 정기적으로 명령을 실행시켜주는 도구\ncrontab -e\n*/1 1분에 한번\n*/1 * * * * date \u0026raquo; date.log 1분에 한번씩 date를 date.log파일로 옮겨라 기본경로는 ~\ntail date.log에 -f 옵션을 넣어서 업데이트되는걸 실시간으로 확인 가능\n쉘이 시작될 때 특정한 명령읋 자동으로 실행\nalias 는 별명을지어주는 것\nalias l=\u0026lsquo;ls -al\u0026rsquo; l 누르면 ls -al 실행됨\nalias c=\u0026lsquo;clear\u0026rsquo;\n우리가 터미널을 연다는건, bashshell 기준 /bin/bash라는 프로그램을 실행시키는 것이다.\n이를 실행할때는 .bashrc파일을 실행하게 약속이 되어 있다.\n예제로 여기 echo \u0026lsquo;hi \u0026lsquo;를 등록하면, bash shell을 열때마다 hi를 출력\nzsh면 .zshrc\n이와 alias를 응용 가능\n","date":"30 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/150-%EC%8B%A4%ED%96%89/","section":"Posts","summary":"vim 실행중 ctrl-z를 통하여 백그라운드로 전환 가능\n이후에 fg명령을 통해서 포그라운드로 전환 가능\njobs라는 명령어를 통해서 백그라운드에 있는 프로세스를 볼 수 있음\nfg %[숫자] 명령어를 통하여 fg명령으로 실행할 프로세스를 결정할 수 있음\nkill %[숫자]를 통하여 프로세스 종료 가능 -9 강제종료 -15 안전종료\nls -alR / \u003eresult.txt 2\u003eerror.log \u0026\n\u0026 명령어를 통해 진행중인 작업을 백그라운드에서 실행 (종료되면 알려줌)\ndaemon이란?\n항상 실행되고 있는 프로그램 냉장고를 생각\n서버 등\n/etc/init.d/ 에 위치해있음 (데몬이 위치하는 디렉토리)\n","title":"실행","type":"posts"},{"content":"locate\n디렉토리를 뒤지지 않고, 파일정보가 있는 데이터베이스를 뒤짐\nfind\n디렉토리를 뒤지면서 파일을 가져옴\nfind /-name \u0026lsquo;.log\u0026rsquo; ==루트 디렉토리 안에 .log 파일을 찾아라\nwhereis\n실행파일을 찾는 명령\necho $PATH를 통하여, PATH 경로를 볼 수 있음\nPATH에서 명령어를 발견하면, 실행하게 됨\n","date":"30 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/149-%ED%8C%8C%EC%9D%BC%EC%9D%84-%EC%B0%BE%EB%8A%94-%EB%B2%95/","section":"Posts","summary":"locate\n디렉토리를 뒤지지 않고, 파일정보가 있는 데이터베이스를 뒤짐\nfind\n디렉토리를 뒤지면서 파일을 가져옴\nfind /-name ‘.log’ ==루트 디렉토리 안에 .log 파일을 찾아라\nwhereis\n실행파일을 찾는 명령\necho $PATH를 통하여, PATH 경로를 볼 수 있음\nPATH에서 명령어를 발견하면, 실행하게 됨\n","title":"파일을 찾는 법","type":"posts"},{"content":"기본용어\nssd,hdd =저장장치 storage\nram = memory\ncpu = 중앙처리장치\n이런것들을 포괄적으로 processor라고 부름\nmemory와 storage는 정보를 저장한다는 공통점이 있다.\ncpu는 매우 빠르게 동작하므로, storage가 가진 속도로는 cpu의 처리속도를 따라올 수 없다.\n그래서, storage에 있는 프로그램을 사용할 때는, 프로그램을 메모리에 적재한다. 이 메모리를 cpu가 읽어 동작한다.\nprocess란?\nmkdir,top,rm \u0026hellip;. 등의 명령어는 /bin, /sbin등의 파일, 즉 storage에 저장되어있다. 이걸 프로그램이라 한다.\n이 프로그램을 실행하면, 프로그램이 memory에 적재되어 cpu에 의해서 처리되게된다. 이 상태를 프로세스라 한다.\n프로세스 모니터링\n명령어\nps 프로세스 리스트를 보여주는 명령\n-aux 백그라운드 등의 모든 프로세스 리스트 보여주기\ngrep을 사용하여 원하는걸 찾을 수 있다.\npid= 프로세스의 식별자(id)\nkill로 pid를 죽일 수 있다.\ntop,htop도 ps랑 비슷, 좀더유용\n","date":"30 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/148-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4/","section":"Posts","summary":"기본용어\nssd,hdd =저장장치 storage\nram = memory\ncpu = 중앙처리장치\n이런것들을 포괄적으로 processor라고 부름\nmemory와 storage는 정보를 저장한다는 공통점이 있다.\ncpu는 매우 빠르게 동작하므로, storage가 가진 속도로는 cpu의 처리속도를 따라올 수 없다.\n그래서, storage에 있는 프로그램을 사용할 때는, 프로그램을 메모리에 적재한다. 이 메모리를 cpu가 읽어 동작한다.\nprocess란?\nmkdir,top,rm …. 등의 명령어는 /bin, /sbin등의 파일, 즉 storage에 저장되어있다. 이걸 프로그램이라 한다.\n이 프로그램을 실행하면, 프로그램이 memory에 적재되어 cpu에 의해서 처리되게된다. 이 상태를 프로세스라 한다.\n","title":"프로세스","type":"posts"},{"content":"쉘 명령어들 실행시키는 파일\n#!/bin/bash =\u0026gt; 밑에 작성되는 코드들이 /bin/bash를 통해 실행될 것임\nif ! [ -d bak ]; =\u0026gt; 현재 디렉토리에 bak라는 디렉토리가 존재하지 않는다면\nmkdir bak =\u0026gt; bak라는 디렉토리를 만들어라\nfi =\u0026gt; 조건문 종료\ncp *.log bak =\u0026gt; 현재 디렉토리의 모든 .log 파일을 bak 복사해라\n디렉토리\n/ (루트)를 기준으로 트리 형식\n/bin =\u0026gt; user binaries 사용자들이 사용하는 명령어들 위치\n/sbin =\u0026gt; system binaries 시스템이 사용하는 명령어 위치\n/etc =\u0026gt; configuration files 설정 파일\n/var =\u0026gt; variable files 내용이 바뀔 수 있는 파일들\n/tmp =\u0026gt; 임시파일 컴퓨터껐다 키면 자동으로 삭제됨\n/home =\u0026gt; 사용자들의 디렉토리\n","date":"28 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/147-shell-script--%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC/","section":"Posts","summary":"쉘 명령어들 실행시키는 파일\n#!/bin/bash =\u003e 밑에 작성되는 코드들이 /bin/bash를 통해 실행될 것임\nif ! [ -d bak ]; =\u003e 현재 디렉토리에 bak라는 디렉토리가 존재하지 않는다면\nmkdir bak =\u003e bak라는 디렉토리를 만들어라\nfi =\u003e 조건문 종료\ncp *.log bak =\u003e 현재 디렉토리의 모든 .log 파일을 bak 복사해라\n디렉토리\n/ (루트)를 기준으로 트리 형식\n/bin =\u003e user binaries 사용자들이 사용하는 명령어들 위치\n/sbin =\u003e system binaries 시스템이 사용하는 명령어 위치\n/etc =\u003e configuration files 설정 파일\n","title":"shell script \u0026 디렉토리","type":"posts"},{"content":"https://www.acmicpc.net/problem/16954\n16954번: 움직이는 미로 탈출욱제는 학교 숙제로 크기가 8×8인 체스판에서 탈출하는 게임을 만들었다. 체스판의 모든 칸은 빈 칸 또는 벽 중 하나이다. 욱제의 캐릭터는 가장 왼쪽 아랫 칸에 있고, 이 캐릭터는 가장 오른쪽www.acmicpc.net\n처음에는 그래프를 통한 bfs를 생각했지만, 배열도 만들지 않고 풀었다.\n아이디어만 알면 진짜 간단하다.\n먼져, 벽의 위치를 집합에 넣는다.\n이후 7번 (어느 벽이던 7번 턴이 지나면 사라지므로) 반복문을 돌며 bfs를 하는데,\n다음 노드를 큐에 끝에 넣지 않고 임시집합에 넣는다.\n그후, 벽을 아래로 한칸식 이동시킨 후에,\n임시집합에서 벽을 빼주어 안겹치는 노드를 구한 후,\n다시반복문을 이어간다.\n만일 7번을 버티면 무조건 나갈수 있고, 아니면 못나가는거다.\n집합에도 한줄반복물을 쓸 수 있다는 것을 알게 되었다.\n코드\nimport sysinput=sys.stdin.readlinefrom collections import deque move=[[-1,0],[1,0],[0,-1],[0,1],[1,1],[1,-1],[-1,1],[-1,-1],[0,0]]wall=set() for i in range(8): tmp=list(input().rstrip()) for j in range(8): if tmp[j]==\u0026#39;#\u0026#39;: wall.add((i,j))q=deque([(7,0)]) for _ in range(7): qq=set() while q: cx,cy=q.popleft() for a,b in move: nx,ny=cx+a,cy+b if 0\u0026lt;=nx\u0026lt;8 and 0\u0026lt;=ny\u0026lt;8 and(nx,ny) not in wall: qq.add((nx,ny)) wall={(x[0]+1,x[1]) for x in wall if x[0]\u0026lt;7} #벽 이동 if not wall: #벽이없으면 무조건 갈 수 있다. print(1) exit() qq-=wall #벽이랑 곂치는 부분 제외하기 if not qq: #더 나아갈 부분이 없으면 못가는거 print(0) exit() for qqq in qq: q.append(qqq)print(1) ","date":"27 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/146-16954-%EC%9B%80%EC%A7%81%EC%9D%B4%EB%8A%94-%EB%AF%B8%EB%A1%9C-%ED%83%88%EC%B6%9C/","section":"Posts","summary":"https://www.acmicpc.net/problem/16954\n16954번: 움직이는 미로 탈출욱제는 학교 숙제로 크기가 8×8인 체스판에서 탈출하는 게임을 만들었다. 체스판의 모든 칸은 빈 칸 또는 벽 중 하나이다. 욱제의 캐릭터는 가장 왼쪽 아랫 칸에 있고, 이 캐릭터는 가장 오른쪽www.acmicpc.net\n처음에는 그래프를 통한 bfs를 생각했지만, 배열도 만들지 않고 풀었다.\n아이디어만 알면 진짜 간단하다.\n먼져, 벽의 위치를 집합에 넣는다.\n이후 7번 (어느 벽이던 7번 턴이 지나면 사라지므로) 반복문을 돌며 bfs를 하는데,\n다음 노드를 큐에 끝에 넣지 않고 임시집합에 넣는다.\n","title":"16954 움직이는 미로 탈출","type":"posts"},{"content":"http://zvon.org/comp/r/tut-Regexp.html#Pages~Page_1 참고함\n위치와 이스케이핑\n^ = ^ 뒤에 있는 패턴이 시작되는 부분에 있는것 검출\n^ who = who로 시작하는거 검출\n$ = $뒤에있는 패턴이 끝나는 부분인거 검출\nwho$ = $who로 끝나는거 검출\n만일 ^,$가 소스상에 있다면? : 역슬래시를 해준다. (이스케이프)\n모든문자\n. (점) 은 모든 문자를 의미\n\u0026hellip;.. 어떠한것도 상관없이 5개의 문자를 가지고있는거\n만일 . 을 찾으려면 이스케이프!\n특정문자\n[] 사용 (문자 하나를 해당)\n[-] = range\n[a-Z] = a부터 Z까지의 모든 문자 [c-ka-d2-6] = c에서 k, a에서 d, 2에서 6까지의 모든문자\n[] 안에서의 ^ 은 xor, 즉 아닌 걸 의미함\n패턴들\n(on|use|rida) 한덩어리임\n문자열 중 on,use,rida를 찾음\n(Mon|Tues|Fri)day 이런식으로 씀\n수량자 (Quantifiers)\n어떠한 패턴이 얼마나 등장하는가\n, + , ?\n= * 앞의 문자가 있을수도있고 없을수도있다.\na*b = b 앞에 a 가\u001cb앞에 a가 있을수도있고, 없을수도있다\n= + 앞의 문자가 반드시 하나 이상이어야 됨 a+b = b 앞에 a 가 1개 이상\n? = ?앞의 문자가 없거나 1개\na?b = b 앞에 a가 하나이거나 없다.\n[ ^ ]+ 공백이 아닌것이 하나이상일#때\n.{5} = . ,즉 어떠한 문자든 간에 5개가 와야됨\n[els]{1,3} = e,l,s 중 하나가 1~3번 등장\n[a-z]{3.} = a부터z까지의 문자가 3 이상\nr.* = r 뒤에 어떤문자가 있을수도있고, 없을수도 있다\n수량자 뒤에 물음표가 온다면, 0,1이라는 뜻이 아니라 그냥 0임 (가장작은수가 선택됨)\n만일 뒤에 설명한 div예제같은 경우에는, 매치되는것 중 가장 적은 수임(선택 범위를 lazy)\nr.*? = r 뒤에 아무것도 오지 않음\nr.+ = r 뒤에 어떤 문자라도 하나이상 와야됨\nr.+? = r 뒤에 문자가 하나만옴\nr.? = r 뒤에 어떤 문자가 1이나 0개\nr.?? = r 뒤에 암것도 오지 않음\n탐욕적/게으름\ntesttest2를****\n≤div\u0026gt;.+로 매칭하면, 첨부터 끝까지 찾아짐 이를 탐욕적이라함\n따라서\n.+?를 사용하면, 하나만 찾아짐 이를 게으름이라함 경계\n\\w =[a-z0-9]를 나타냄 (word)\n\\W=[^A-z0-9]를 나타냄 (word가 아니다)\n\\d = 숫자 [0-9]\n\\D = 숫자가 아님\n워드 바운더리 (\\b)\n\\b\\w 단어가 시작되는 지점\n\\w\\b 단어가 끝나는 지점\n\\b\\w\\b 단어가 하나인 단어\n\\b\\\\w+\\b 단어가 하나이상인 단어\n\\B. 직접해봐야 이해됨 \\b의 반대\n\\A\u0026hellip; =문장시작점에 3개의 문자\n\u0026hellip;\\Z = 문장 뒤에있는 3개의 문자\n^,$ 은 multiline 즉 여러줄일대 각자 선택\n\\A,\\Z는 multiline이여도 첫번째만\n패턴들\n(?=X)의 의미= 검색할때는 X를 쓰지만, 선택할때는 제외함\n문자=AAAX\u0026mdash;aaax\u0026mdash;111\n\\w+(?=X)\nAAA만 선택됨\n\\w+(?=\\w) 뒤에 아무문자나 하나 들어가지만, 그 문자는 선택되지 않음\nAAA,aaa,11 선택됨\n","date":"26 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/145-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EA%B8%B0%EB%B3%B8/","section":"Posts","summary":"http://zvon.org/comp/r/tut-Regexp.html#Pages~Page_1 참고함\n위치와 이스케이핑\n^ = ^ 뒤에 있는 패턴이 시작되는 부분에 있는것 검출\n^ who = who로 시작하는거 검출\n$ = $뒤에있는 패턴이 끝나는 부분인거 검출\nwho$ = $who로 끝나는거 검출\n만일 ^,$가 소스상에 있다면? : 역슬래시를 해준다. (이스케이프)\n모든문자\n. (점) 은 모든 문자를 의미\n….. 어떠한것도 상관없이 5개의 문자를 가지고있는거\n만일 . 을 찾으려면 이스케이프!\n특정문자\n[] 사용 (문자 하나를 해당)\n[-] = range\n[a-Z] = a부터 Z까지의 모든 문자 [c-ka-d2-6] = c에서 k, a에서 d, 2에서 6까지의 모든문자\n","title":"정규표현식 기본","type":"posts"},{"content":"https://www.youtube.com/watch?v=vgIc4ctNFbc 참고함\nDML : 데이터 조작(crud) 하는데 사용\nDDL : 데이터 정의 (create,drop,alter) 하는데 사용\nDCL :데이터 컨트롤(제어) Grant,REVOKE \u0026hellip;. (권한)\n기본적인 mysql의 world db 사용\nSHOW DATABASES :현재 서버에 있는 모든 db 조회\nUSE world : world db를 사용하겠다.\nshow tablews ; wolrd의 테이블 보기 status 붙이면 정보까지\ndescribe [tablename] 테이블 정보 보기 (줄여서 desc)\nSELECT\nwhere : 조건\n관계연산자 사용 가능\nor, and , 조건연산자, not, 등\nEX)7000000~8000000인구수의 도시 검색\nselect Name, Population from citywhere population \u0026lt;8000000 and population \u0026gt;7000000; EX) 한국에있는도시들 보기\nselect * from city where CountryCode=\u0026#39;KOR\u0026#39;; BETWEEN 사이에있는값\nEX) 7000000~8000000인구수의 도시 검색\nselect * from city where population between 7000000 and 8000000; IN 이산적인 값\nselect * from city where Name in (\u0026#39;seoul\u0026#39;,\u0026#39;new york\u0026#39;,\u0026#39;tokyo\u0026#39;); LIKE 문자열 검색\n문자열 뒤에 무엇이든 혀용 한글자일때는 \u0026lsquo;-\u0026quot; , 여러글자일때는 \u0026lsquo;%\u0026rsquo; 사용\nEX) counttycode가 \u0026lsquo;ko?\u0026lsquo;인 거 검색\nselect * from citywhere countryCode Like \u0026#39;ko_\u0026#39;; SUB QUERY\n쿼리안의 쿼리\nEX) 컨트리코드가 (서울이라는 도시의 컨트리코드) 인 정보\nselect * from city where countrycode = (select countrycode from city where name =\u0026#39;seoul\u0026#39;); ANY: 서브쿼리의 여러 개의 결과중 하나만 만족해도 가능 (in과 동일)\nEX) 인구가 (district가 뉴욕인 모든 city의 인구 중 가장 적은 도시 )보다 높은 정보\nselect * from city where population \u0026gt; ANY (select population from city where district=\u0026#39;New York\u0026#39;); All: ANY와 달리 모든 서브쿼리의 결과를 만족시켜야 함\nORDER BY (정렬 default=ASC) 여러기준 가능\nEX) \bcountrycode 기준으로 오름차순, 만일 같으면 인구수기준으로 내림차순\nselect * from cityorder by countrycode asc, population desc; DISTINCT( 중복된건 1개만)\nselect countrycode from city;select distinct countrycode from city; LIMIT (출력 개수 제한)\nEX) 인구수 기준으로 10개만\nselect * from cityorder by population desclimit 10; Group By\n그룹으로 묶어주는 역활\n별칭 사용\n집계함수 사용\nAVG(),\nMIN(),\nMAX(),\nCOUNT(),\nCOUNT(DISTINCT) 중복제거된 개수\n,STDEV() 표준편차\nVARIANCE() 분산\nEX) 가장 큰 population을 mp, 그리고 countrycode\n를 countrycode별로 알려줌\nselect countrycode, max(population) as mpfrom citygroup by countrycode HAVING: where과 비슷한 개념으로 조건 제한 (집계함수에 대하여)\n무조근 groupby 다음에 나와야 함\nEX) max(population)이 800만을 초과되는 countrycode, max(population)\nselect countrycode, max(population) from citygroup by countrycode having MAX(population) \u0026gt;8000000; ROLLUP:총합 또는 중간합계가 필요한 경우 사용\ngroupby와 withrollup문\nex) countrycode로 묶는데, 묶은것들의 총합까지 보여줌 (해보면 암)\nselect countrycode, name, sum(population)from city group by countrycode, name with rollup JOIN !!!!!!따로공부\nselect * from cityjoin country on city.countrycode = country.code join countrylanguage on city.countrycode=countrylanguage.countrycode 내장함수\nlength(): 길이\n정확히 바이트를 말한다. 왜인진모르겠는데 한글은 3byte다.\nselect lemngth(\u0026#39;aaaaa\u0026#39;) select lemngth(\u0026#39;다섯글자임\u0026#39;) concat(): 문자열결합\nnull있으면 무조건 null됨\nselect concat(\u0026#39;my\u0026#39;,\u0026#39;sql op\u0026#39;,\u0026#39;en source\u0026#39;); location() : 문자열이 처음으로 나타나는 위치반환\n없으면 0 (1부터계산)\nselect locate(\u0026#39;abc\u0026#39;, \u0026#39;ababababababc\u0026#39;) left() \u0026amp; right() 문자열의 왼,오에서 지정한 개수만큼 반환\nselect left(\u0026#39;mysql is an open source realation database,\u0026#39;,5) LOWER(), UPPER() 대소문자 변경\nREPLACE(), 대체 문자열로 교체\nselect replace(\u0026#39;mssql\u0026#39;,\u0026#39;ms\u0026#39;,\u0026#39;my\u0026#39;); trim(): 문자열의 앞이나 뒤, 또는 양쪽 모두에 있는 특정 문자 제거 (자동으로 공백)\n지정자:\nboth =양끝\nleading = 앞\ntraling =뒤\nselect trim(\u0026#39; mysql \u0026#39;),trim( LEADING \u0026#39;#\u0026#39; from \u0026#39;###mysql###\u0026#39;), trim( trailing \u0026#39;#\u0026#39; from \u0026#39;###mysql###\u0026#39;); FORMAT() : 숫자타입을 새자리마다 쉼표를 사용하는 형식으로 변경 (문자열로 반환됨), 두번째인수는 반올림할 소수부분\nselect format(123123123123123.123123123123,3); floor(), cell(), round() 가능\n날짜\nnow(): 현재날짜, 시간반환\ncurdate():현재 날짜반환\ncurtime():현재시간반환\nDate_format() 날자와 시간정보 포멧팅\nselect Date_format(now(),\u0026#39;%d %y %a %d %m %n\u0026#39;); 고급(ddl)\ncity와 같은 city2 테이블 생성\nCreate table city2 as select * from city; 새로운 db 생성\ncreate database jiheon;use jiheon; 테이블\nCREATE TABLE `jiheon`.`test` ( `id` INT NOT NULL, `col1` INT NULL, `col2` FLOAT NULL, `col3` VARCHAR(45) NULL, PRIMARY KEY (`id`)); 테이블에 컬럼 추가(Alter)\ncol4 int null로 추가\nAlter Table testadd col4 int null; modify: 컬럼속성 변경\ncol4를 int에서 varchar로 변경\nAlter Table testadd col4 int null; drop: 컬럼 제거\nAlter Table testdrop col4; 인덱스\n-테이블에서 원하는데이터를 빠르게 찾기 위해 사용\n-검쌕과 질의를 할 때 테이블 전체를 읽지 않기 때문에 빠름\n-수정보다는 검색에 유리 (수정할때 인덱스도 수정해야됨)\nCREATE INDEX :인덱스만들기\ncreate index col1idxon test(col1) SHOW INDEDX : 인덱스 정보보기\nshow index from test; create unique index :중복값 허용안하는 인덱스\ncreate unique index col2idxon test (col2);show index from test; Fulltext index\n매우 빠르게 테이블의 모든 텍스트 컬럼을 검색\nAlter table testadd fulltext col3idx(col3);show index from test; index 삭제 (2가지방법)\nAlter문\nAlter table testdrop index col3idx;show index from test; 그냥 drop만 사용\ndrop index col2idx on test;show index from test; VIEW\n데이터베이스에 존재하는 일종의 가상 테이블\n만들기 col1과 col2를 가지는 테스트뷰\nCREATE view testview asselect col1, col2from test;\bselect * from testview Alter문 적용 가능\nalter view testview asselect col1, col2, col3from test;select * from testview 삭제\ndrop view testview city, country, countrylanguage 테이블 조인하고, 한국에 대한 정보만 뷰 생성하기\ncreate view allview asselect c.name, co.surfacearea, c.population, cl.language from city c, country co, countrylanguage cl where c.countrycode = co.code and c.countrycode = cl.countrycode and c.countrycode=\u0026#39;kor\u0026#39;; select * from allview; INSERT\ndb 전환후\nInsert into testvalue(1, 123, 1.1 ,\u0026#39;test\u0026#39;);select * from test; UPDATE\n기존값 변경\nwhere 안쓰면 전체행 내용 변경\nupdate testset col1 =1, col2=1.0, col3=\u0026#39;test\u0026#39;where id= 1;select * from test Delete\ncommit 하지 않았으면 롤백 가능\nDelete from testwhere id=1; Truncate\n테이블삭제는 안하지만, 데이터만 삭제 (롤백 불가!)\ndrop table []\n그냥 삭제 저절로 커밋됨\ndrop database jiheon\ndb삭제\n","date":"25 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/144-mysql-%ED%95%9C%EB%B2%88%EC%97%90%EB%81%9D%EB%82%B4%EA%B8%B0/","section":"Posts","summary":"https://www.youtube.com/watch?v=vgIc4ctNFbc 참고함\nDML : 데이터 조작(crud) 하는데 사용\nDDL : 데이터 정의 (create,drop,alter) 하는데 사용\nDCL :데이터 컨트롤(제어) Grant,REVOKE …. (권한)\n기본적인 mysql의 world db 사용\nSHOW DATABASES :현재 서버에 있는 모든 db 조회\nUSE world : world db를 사용하겠다.\nshow tablews ; wolrd의 테이블 보기 status 붙이면 정보까지\ndescribe [tablename] 테이블 정보 보기 (줄여서 desc)\nSELECT\nwhere : 조건\n관계연산자 사용 가능\nor, and , 조건연산자, not, 등\nEX)7000000~8000000인구수의 도시 검색\n","title":"mysql 한번에끝내기","type":"posts"},{"content":"https://www.acmicpc.net/problem/14466\n14466번: 소가 길을 건너간 이유 6첫 줄에 N, K, R이 주어진다. 다음 R줄에는 한 줄에 하나씩 길이 주어진다. 길은 상하좌우로 인접한 두 목초지를 잇고, r c r′ c′의 형태 (행, 열, 행, 열)로 주어진다. 각 수는 1 이상 N 이하이다.www.acmicpc.net\n처음엔 단순히 모든 소에 대해 bfs를 실행하여, 만나지못하는 모든 소를 구하고,\n만난소+1(자신) 에다가 만나지못한 모든소를 곱해준다음, 쌍이니 2로 나눠주는식으로 구했다.\n이렇게 통과를 하긴 했는데, 다른사람들정답을 보니 전부나보다 빨랐다. (1048ms나옴)\n약속이 있어서 나갔다가 생각났는데,\narr에 대하여 bfs로 구역을 나누고, 배열에 구역별 소의 수를 집어넣는다.\n그 후 현재 구역이 만나지 못하는 소의 수는 (총소의수 - 현재구역의 소의 수)*현재구역의 소의 수이다.\n그 후 다음구역으로 넘어갈 때는, 중복을 방지하기 위하여 총 소의수-=현재구역의 소의 수 를 해주고 넘어간다.\n이렇게 하면 배열전체를 한번만 탐색해도 되므로, 10배정도 빠르다. (128ms)\nimport sysfrom collections import dequeinput=sys.stdin.readlineroad=set() move=[[-1,0],[1,0],[0,-1],[0,1]]cow=set()n,k,r=map(int,input().split())answer=0 arr=[[0 for _ in range(n)]for _ in range(n)]for _ in range(r): a,b,c,d=map(lambda x: int(x)-1,input().split()) road.add((a,b,c,d)) road.add((c,d,a,b))for _ in range(k): a,b=map(lambda x: int(x)-1,input().split()) cow.add((a,b))def bfs(i,j): res=[0,1][(i,j) in cow] arr[i][j]=1 q=deque([(i,j )]) while q: cx,cy=q.popleft() for a,b in move: nx,ny=cx+a,cy+b if 0\u0026lt;=nx\u0026lt;n and 0\u0026lt;=ny\u0026lt;n and (cx,cy,nx,ny) not in road and arr[nx][ny]==0: arr[nx][ny]=1 q.append((nx,ny)) if (nx,ny) in cow: res+=1 return res sec=[]for i in range(n): for j in range(n): if arr[i][j]==0: sec.append(bfs(i,j))# print(sec)# exit() for i in range(len(sec)-1): tmp=sec[i] k-=tmp answer += k*tmp print(answer) 재밌는문제였다.\n","date":"19 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/143-14466%EC%86%8C%EA%B0%80%EA%B8%B8%EC%9D%84%EA%B1%B4%EB%84%88%EA%B0%84%EC%9D%B4%EC%9C%A06/","section":"Posts","summary":"https://www.acmicpc.net/problem/14466\n14466번: 소가 길을 건너간 이유 6첫 줄에 N, K, R이 주어진다. 다음 R줄에는 한 줄에 하나씩 길이 주어진다. 길은 상하좌우로 인접한 두 목초지를 잇고, r c r′ c′의 형태 (행, 열, 행, 열)로 주어진다. 각 수는 1 이상 N 이하이다.www.acmicpc.net\n처음엔 단순히 모든 소에 대해 bfs를 실행하여, 만나지못하는 모든 소를 구하고,\n만난소+1(자신) 에다가 만나지못한 모든소를 곱해준다음, 쌍이니 2로 나눠주는식으로 구했다.\n이렇게 통과를 하긴 했는데, 다른사람들정답을 보니 전부나보다 빨랐다. (1048ms나옴)\n","title":"14466소가길을건너간이유6","type":"posts"},{"content":"leading: 아이콘버튼이나 간단한 위젯을 왼쪽에 배치할 때\ntrailing: 오른쪽에 배치\nactions: 복수의 아이콘 버튼 등을 오른쪽에 배치할 때\nonpressed: 함수의 형태로 (eventlisenter)\nDrawer: 왼쪽에 누르면 나오는 메뉴\nBuildContext란?\n1.widget tree에서 현재 위젯의 위치를 알 수 있는 정보\nbuild-\u0026gt;scaffold wiget 리턴 이때 이게 어디에 위치하는지의 정보를 담은 context를 넣어서 리턴해줌\n2.이 BuildContext는 stateless | state 빌드 메서드에 의해서 리턴 된 위젯의 부모가 됨 (부모걸 상속받음)\n","date":"19 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/142-drawer%EB%A9%94%EB%89%B4--buildcontext/","section":"Posts","summary":"leading: 아이콘버튼이나 간단한 위젯을 왼쪽에 배치할 때\ntrailing: 오른쪽에 배치\nactions: 복수의 아이콘 버튼 등을 오른쪽에 배치할 때\nonpressed: 함수의 형태로 (eventlisenter)\nDrawer: 왼쪽에 누르면 나오는 메뉴\nBuildContext란?\n1.widget tree에서 현재 위젯의 위치를 알 수 있는 정보\nbuild-\u003escaffold wiget 리턴 이때 이게 어디에 위치하는지의 정보를 담은 context를 넣어서 리턴해줌\n2.이 BuildContext는 stateless | state 빌드 메서드에 의해서 리턴 된 위젯의 부모가 됨 (부모걸 상속받음)\n","title":"Drawer메뉴 \u0026 BuildContext","type":"posts"},{"content":"위젯이 tree구조로 이루어져있다.\n변화없으면 stateless stateful\nmyapp (root)\nMaterialApp 전체앱을 감싸는 위젯\nMyHomePage 기능,디자인들이 만들어짐\nScaffold (중요!) 앱화면과 기능을 구성하기 위한 빈 페이지를 준비\n이 밑으로 모든 위젯들이 들어감\n중요! 수정할때 항상 밑에괄호부터 지울것\nmainAxisAlignment= 현재 주 축에서 정렬 ex) column이면 세로축에서 정렬\ncrossAxisAlignment=보조축정렬\nSizedbox=공백만들기\npubspec.yaml ==설정 (들여쓰기 중요)\nasset을 통하여 이미지 삽입\n안드로이드스튜디오에서 에뮬레이터를 만들었으면,\n그 폴더에 vsc로도 실행가능!\n","date":"18 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/141-%ED%94%8C%EB%9F%AC%ED%84%B0-%EA%B8%B0%EB%B3%B8-%EC%9C%84%EC%A0%AF/","section":"Posts","summary":"위젯이 tree구조로 이루어져있다.\n변화없으면 stateless stateful\nmyapp (root)\nMaterialApp 전체앱을 감싸는 위젯\nMyHomePage 기능,디자인들이 만들어짐\nScaffold (중요!) 앱화면과 기능을 구성하기 위한 빈 페이지를 준비\n이 밑으로 모든 위젯들이 들어감\n중요! 수정할때 항상 밑에괄호부터 지울것\nmainAxisAlignment= 현재 주 축에서 정렬 ex) column이면 세로축에서 정렬\ncrossAxisAlignment=보조축정렬\nSizedbox=공백만들기\npubspec.yaml ==설정 (들여쓰기 중요)\nasset을 통하여 이미지 삽입\n안드로이드스튜디오에서 에뮬레이터를 만들었으면,\n그 폴더에 vsc로도 실행가능!\n","title":"플러터 기본 위젯","type":"posts"},{"content":"https://www.acmicpc.net/problem/2206\n2206번: 벽 부수고 이동하기N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로www.acmicpc.net\n가장 기본적인 방식\nvisited 2차원배열에, 한차원을 더 추가하여\n벽을 부쉈는지를 판별한다.\n만일 벽을 부쉈다면, 앞으로 벽을 부수지 못할 것이고,\n벽을 부수지 않았다면, 벽을 부순 대신 벽을 부순 차원으로 넘어간다.\n이렇게 가장 먼저 n-1,m-1에 도착한 요소를 찾아 +1 해주고 print(해주면된다.)\n2차원까지는 머리가 돌아가는데, 3차원부터는 상당히 어지럽다.\n주의할점: 파이썬에서 리스트 반복을 쓸 때는 거꾸로해줘야됨!!\nimport sysinput=sys.stdin.readlinefrom collections import dequearr=[] move=[[-1,0],[1,0],[0,-1],[0,1]]n,m=map(int,input().split())if n==m==1: print(1) exit()answer=1e+8for _ in range(n): arr.append(list(map(int,input().strip())))def bfs(): global answer visited=[[[0 for _ in range(m)]for _ in range(n)]for _ in range(2)] #벽여부, x,y visited[0][0][0]=1 #지금있는곳 1로처리 visited[1][0][0]=1 #지금있는곳 1로처리 que=deque([[0,0,0]]) while que: w,cx,cy=que.popleft() for dx,dy in move: nx,ny=cx+dx,cy+dy if 0\u0026lt;=nx\u0026lt;n and 0\u0026lt;=ny\u0026lt;m and not visited[w][nx][ny]: #벗어나지 않고, 방문하지 않았다면 if nx==n-1 and ny==m-1: #정답조건이면 print(visited[w][cx][cy]+1) exit() if arr[nx][ny]==1: #벽이라면 if not w: #안부순상태라면 visited[1][nx][ny]= visited[0][cx][cy]+1 que.append((1,nx,ny)) else: continue #이미 부쉈다면 못부숨 else: visited[w][nx][ny]=visited[w][cx][cy]+1 #벽아니라면 그냥 그상태로통과 que.append((w,nx,ny)) bfs()print(-1) ","date":"14 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/140-2206-%EB%B2%BD%EB%B6%80%EC%88%98%EA%B3%A0-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/2206\n2206번: 벽 부수고 이동하기N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로www.acmicpc.net\n가장 기본적인 방식\nvisited 2차원배열에, 한차원을 더 추가하여\n벽을 부쉈는지를 판별한다.\n만일 벽을 부쉈다면, 앞으로 벽을 부수지 못할 것이고,\n벽을 부수지 않았다면, 벽을 부순 대신 벽을 부순 차원으로 넘어간다.\n이렇게 가장 먼저 n-1,m-1에 도착한 요소를 찾아 +1 해주고 print(해주면된다.)\n","title":"2206 벽부수고 이동하기","type":"posts"},{"content":"https://www.acmicpc.net/problem/16236\n16236번: 아기 상어N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다. 아기 상어와 물고기는 모두 크기를 가www.acmicpc.net\n가장 어려웠던 조건이\n거리가 가까운 물고기가 많다면, 가장 위에 있는 물고기, 그러한 물고기가 여러마리라면, 가장 왼쪽에 있는 물고기를 먹는다\n였다.\n처음에는 방향 순서만 잘 하면 될 줄 알았지만,\n디버깅해보면 방향만으로는 부족하다.\n한참 헤메다가 질문계시판에서 정렬 힌트를 얻고 풀었다.\n결국 \u0026lsquo;해당 거리에서 먹을수 있는 모든 물고기들\u0026rsquo; 을 모아놓고,\n이를 정렬하여 \u0026lsquo;가장 위,왼쪽의 물고기\u0026rsquo; 를 먹는 식으로 구현하면 된다.\n나는 물고기의 시간이 바뀔때마다 heap안에 물고기가 있는지 확인하고, pop하는 식으로 구현했다.\n이 아이디어만 알면 쉽게 풀리는데, 이 아이디어를 어떻게 생각하는지 모르겠다.\nimport sysinput =sys.stdin.readlinefrom collections import dequeimport heapq move=[[-1,0],[0,-1],[0,1],[1,0]] #움직임n=int(input())Flag=Falsearr=[] for i in range(n): tmp=[*map(int,input().split())] arr.append(tmp) for j in range(n): if not Flag: if tmp[j]==9: shark=[i,j] #입력받기 Flag=0 arr[shark[0]][shark[1]]=0 #상어표시된거 초기화 !중요size=2 #크기eat=0 #먹은횟수answer=0 ############################################################### def bfs(shark:list): visited=[[0 for _ in range(n)] for _ in range(n)] #방문처리용 visited[shark[0]][shark[1]]=1 #현재있는곳방문처리 que=deque([[0]+shark]) #shark = x,y,t heap=[] # 물고기판별용 tt=0 while que: t,cx,cy=que.popleft() if t!=tt: #한사이클이 돌았으면 tt+=1 if heap: #만일 물고기가있다면 return heapq.heappop(heap) for dx,dy in move: nx,ny=cx+dx,cy+dy if 0\u0026lt;=nx\u0026lt;n and 0\u0026lt;=ny\u0026lt;n and not visited[nx][ny]: #만일 조건에 맞는다면 visited[nx][ny]=1 tmp=arr[nx][ny] next=[t+1,nx,ny] if tmp==0 or tmp==size: #암것도없거나 똑같은 사이즈면 que.append(next) #다음노드로 elif size\u0026gt;tmp: #상어가 더 크다면 heapq.heappush(heap,next) #heap에 들어가기 if heap: return heapq.heappop(heap) return -1 ########################################### while True: res=bfs(shark) if res==-1: #더이상먹을물고기가없으면 print(answer) #정답출력 exit() else: t,x,y=res #시간,x,y arr[x][y]=0 eat+=1 #먹은거 if eat==size: #크기만큼 먹었으면 size+=1 #크기증가 eat=0 #먹는거 초기화 answer+=t shark=[x,y] ","date":"13 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/139-%EC%95%84%EA%B8%B0%EC%83%81%EC%96%B4/","section":"Posts","summary":"https://www.acmicpc.net/problem/16236\n16236번: 아기 상어N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다. 아기 상어와 물고기는 모두 크기를 가www.acmicpc.net\n가장 어려웠던 조건이\n거리가 가까운 물고기가 많다면, 가장 위에 있는 물고기, 그러한 물고기가 여러마리라면, 가장 왼쪽에 있는 물고기를 먹는다\n였다.\n처음에는 방향 순서만 잘 하면 될 줄 알았지만,\n디버깅해보면 방향만으로는 부족하다.\n한참 헤메다가 질문계시판에서 정렬 힌트를 얻고 풀었다.\n결국 ‘해당 거리에서 먹을수 있는 모든 물고기들’ 을 모아놓고,\n","title":"아기상어","type":"posts"},{"content":"https://www.acmicpc.net/problem/5430\n5430번: AC각 테스트 케이스에 대해서, 입력으로 주어진 정수 배열에 함수를 수행한 결과를 출력한다. 만약, 에러가 발생한 경우에는 error를 출력한다.www.acmicpc.net\n푸른건안어려웠는데 빈 배열이 주어질 경우 예외처리때문에 한참을 헤멨다.\n중요한건 실제로 뒤집는게 아니라\nboolean 변수 하나를 두고\n뒤집는걸 실제로 뒤집는게 아니라\npopleft, pop 으로 구현한다\n그후 만일 뒤집힌 상태면 리스트를 거꾸로 출력만하면 된다.\nimport sysfrom collections import dequeinput =sys.stdin.readline def solve(p:str,l:deque): ss=0 # 상태바 0은정상 1은 뒤집힌상태 for i in p: if i==\u0026#39;R\u0026#39;: ss^=1 #0이면 1로, 1이면 0으로 continue else: #버리는연산일경우 if not l: #비어있으면 print(\u0026#39;error\u0026#39;) return else: if ss: #뒤집힌상태면 l.pop() #뒤에서빼고 else: #아니면 l.popleft() #앞에서뺌 if ss: l.reverse() print(\u0026#34;[\u0026#34;+\u0026#34;,\u0026#34;.join(l)+\u0026#34;]\u0026#34;) t=int(input()) for _ in range(t): p=input().rstrip() n=int(input()) arr=input().rstrip()[1:-1].split(\u0026#39;,\u0026#39;) l=deque() if not n else deque(arr) solve(p,l) ","date":"12 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/138-5430-ac/","section":"Posts","summary":"https://www.acmicpc.net/problem/5430\n5430번: AC각 테스트 케이스에 대해서, 입력으로 주어진 정수 배열에 함수를 수행한 결과를 출력한다. 만약, 에러가 발생한 경우에는 error를 출력한다.www.acmicpc.net\n푸른건안어려웠는데 빈 배열이 주어질 경우 예외처리때문에 한참을 헤멨다.\n중요한건 실제로 뒤집는게 아니라\nboolean 변수 하나를 두고\n뒤집는걸 실제로 뒤집는게 아니라\npopleft, pop 으로 구현한다\n그후 만일 뒤집힌 상태면 리스트를 거꾸로 출력만하면 된다.\nimport sysfrom collections import dequeinput =sys.stdin.readline def solve(p:str,l:deque): ss=0 # 상태바 0은정상 1은 뒤집힌상태 for i in p: if i=='R': ss^=1 #0이면 1로, 1이면 0으로 continue else: #버리는연산일경우 if not l: #비어있으면 print('error') return else: if ss: #뒤집힌상태면 l.pop() #뒤에서빼고 else: #아니면 l.popleft() #앞에서뺌 if ss: l.reverse() print(\"[\"+\",\".join(l)+\"]\") t=int(input()) for _ in range(t): p=input().rstrip() n=int(input()) arr=input().rstrip()[1:-1].split(',') l=deque() if not n else deque(arr) solve(p,l)","title":"5430 AC","type":"posts"},{"content":"https://www.acmicpc.net/problem/3190\n3190번: 뱀\u0026rsquo;Dummy\u0026rsquo; 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다. 게임www.acmicpc.net\n그냥구현이다\n하나 아쉬운점은 따로 뱀을 사전으로 나타내지 않고\n그냥 arr에 나타내면 더 빠르다.\n이게 더 간단한데 내가 왜 사전을 썼는지 몰겠다.\nimport sysinput=sys.stdin.readlinefrom collections import dequen=int(input()) arr=[[0 for _ in range(n)]for _ in range(n)]for _ in range(int(input())): a,b=map(int,input().split()) arr[a-1][b-1]=1info=dict() #방향변환정보 l=int(input())for _ in range(l): a,b=input().split() info[int(a)]=b r,c=(0,0) #가로세로좌표dd=[[0,1],[1,0],[0,-1],[-1,0]] #보고있는방향d=0 #방향 (첨에오른쪽)t=0 #시간 snake=deque([(0,0)]) #뱀본체ss=dict() #뱀위치장용ss[(0,0)]=1while True: t+=1 a,b=dd[d] r,c=r+a,c+b if 0\u0026lt;=r\u0026lt;n and 0\u0026lt;=c\u0026lt;n and (r,c) not in ss: #벽이나 자기자신과 부딪치지 않았다면 snake.append((r,c)) ss[(r,c)]=1 if not arr[r][c]: # 이동한 칸에 사과가 없다면 tmp=snake.popleft() #꼬리자르기 del(ss[tmp]) #꼬리자르기 else: arr[r][c]=0 else: #종료 break if t in info: #방향정보변환이 있다면 if info[t]==\u0026#39;L\u0026#39;: d-=1 if d\u0026lt;0: d=3 else: d+=1 if d\u0026gt;3: d=0 #여기까지print(t) ","date":"11 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/137-3190%EB%B1%80/","section":"Posts","summary":"https://www.acmicpc.net/problem/3190\n3190번: 뱀’Dummy’ 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다. 게임www.acmicpc.net\n그냥구현이다\n하나 아쉬운점은 따로 뱀을 사전으로 나타내지 않고\n그냥 arr에 나타내면 더 빠르다.\n이게 더 간단한데 내가 왜 사전을 썼는지 몰겠다.\nimport sysinput=sys.stdin.readlinefrom collections import dequen=int(input()) arr=[[0 for _ in range(n)]for _ in range(n)]for _ in range(int(input())): a,b=map(int,input().split()) arr[a-1][b-1]=1info=dict() #방향변환정보 l=int(input())for _ in range(l): a,b=input().split() info[int(a)]=b r,c=(0,0) #가로세로좌표dd=[[0,1],[1,0],[0,-1],[-1,0]] #보고있는방향d=0 #방향 (첨에오른쪽)t=0 #시간 snake=deque([(0,0)]) #뱀본체ss=dict() #뱀위치장용ss[(0,0)]=1while True: t+=1 a,b=dd[d] r,c=r+a,c+b if 0\u003c=r\u003cn and 0\u003c=c\u003cn and (r,c) not in ss: #벽이나 자기자신과 부딪치지 않았다면 snake.append((r,c)) ss[(r,c)]=1 if not arr[r][c]: # 이동한 칸에 사과가 없다면 tmp=snake.popleft() #꼬리자르기 del(ss[tmp]) #꼬리자르기 else: arr[r][c]=0 else: #종료 break if t in info: #방향정보변환이 있다면 if info[t]=='L': d-=1 if d\u003c0: d=3 else: d+=1 if d\u003e3: d=0 #여기까지print(t)","title":"3190뱀","type":"posts"},{"content":"https://www.acmicpc.net/problem/1461\n1461번: 도서관세준이는 도서관에서 일한다. 도서관의 개방시간이 끝나서 세준이는 사람들이 마구 놓은 책을 다시 가져다 놓아야 한다. 세준이는 현재 0에 있고, 사람들이 마구 놓은 책도 전부 0에 있다. 각 책www.acmicpc.net\n쉬운문제였는데 생각이 너무많아서 어렵게풀었다.\n먼져, 음수와 양수를 각각 리스트에넣고 음수는 abs해준다. 이를 거꾸로 정렬한다.\n그 후, 각각 길이//m, 길이%m을 구한다. (a,b)\na만큼 반복문을 돌면서 (a-1까지)\n각 배열[i*m](그 범위에서의 최댓값) 의 2배(왕복) 정답에 더해주다\n만약 b가 있다면,\n더 처리할 원소가 있으므로\n위 결과에 [i*m](나머지 중 가장 큰 수)*2를 더해준다\n그리고, 음수와 양수에서 가장 큰 수가 최댓값인데,\n이 최댓값을 왕복하지 않아야하므로\n정답에서 최댓값을 빼주면 된다.\n써놓고보면 간단한데 웰케 헷갈리는지 모르겠다.\nn,m= map(int,input().split())arr=[*map(int,input().split())]plus,minus=[],[] for a in arr: if a\u0026gt;0: plus.append(a) else: minus.append(abs(a))plus.sort(reverse=True)minus.sort(reverse=True) answer=0max_val=0for ttt in (plus,minus): if ttt: a,b=divmod(len(ttt),m) for i in range(a): answer+=ttt[i*m]*2 if ttt:tmp=ttt[0] if b: answer+=ttt[a*m]*2 max_val=max(max_val,tmp) print(answer-max_val) ","date":"10 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/136-1461-%EB%8F%84%EC%84%9C%EA%B4%80/","section":"Posts","summary":"https://www.acmicpc.net/problem/1461\n1461번: 도서관세준이는 도서관에서 일한다. 도서관의 개방시간이 끝나서 세준이는 사람들이 마구 놓은 책을 다시 가져다 놓아야 한다. 세준이는 현재 0에 있고, 사람들이 마구 놓은 책도 전부 0에 있다. 각 책www.acmicpc.net\n쉬운문제였는데 생각이 너무많아서 어렵게풀었다.\n먼져, 음수와 양수를 각각 리스트에넣고 음수는 abs해준다. 이를 거꾸로 정렬한다.\n그 후, 각각 길이//m, 길이%m을 구한다. (a,b)\na만큼 반복문을 돌면서 (a-1까지)\n각 배열[i*m](그 범위에서의 최댓값) 의 2배(왕복) 정답에 더해주다\n만약 b가 있다면,\n더 처리할 원소가 있으므로\n","title":"1461 도서관","type":"posts"},{"content":"import sysinput=sys.stdin.readlinen,m=map(int,input().split()) r,c,d=map(int,input().split())arr=[]for _ in range(n): arr.append([* map(int,input().split())])ddd=[[-1,0],[0,1],[1,0],[0,-1]] #방향 def dd(d,mv=1): #방향왼쪽으로 d-=mv if d\u0026lt;0: d=4+d return dcnt=0 answer=0while True: if arr[r][c]==0: #현재칸이 비어있다면 arr[r][c]=\u0026#39;#\u0026#39; #청소하고 answer+=1 #청소한칸+=1 next_d=dd(d) #현재의 왼쪽방향 a,b=ddd[next_d] rr,cc=r+a,c+b #왼쪽칸좌표 if not arr[rr][cc]: #왼쪽이 비어있다면 r,c=rr,cc # 다음 반복에 그 칸으로 이동 d=next_d # 방향바꿈 cnt=0 #카운트 초기화 continue else:#왼쪽이 차있다면 if cnt\u0026lt;4: #4번이하라면, d=next_d #방향만 바꿈 cnt+=1 #횟수올려줌 if cnt==4: #4번연속 방향만 바꿨다면 next_d=dd(d,2) #뒤쪽을 탐색 a,b=ddd[next_d] rr,cc=a+r,b+c if arr[rr][cc]==1: #벽이라면 break #끗 else: #벽이아니라면 cnt=0 #카운트초기화 r,c=rr,cc #다음반복때 들어갈 칸print(answer) https://www.acmicpc.net/problem/14503\n14503번: 로봇 청소기로봇 청소기가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오. 로봇 청소기가 있는 장소는 N×M 크기의 직사각형으로 나타낼 수 있으며, 1×1크기의 정사각형 칸으로 나누어www.acmicpc.net\n이게 빡구현이구나\n딱히 설명할 건 없고 그냥 구현하면 된다.\n주의해야할건, 청소한 칸을 1로 놔두면 벽과 헷갈린다는 점이다.\n","date":"10 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/135-14503-%EB%A1%9C%EB%B4%87-%EC%B2%AD%EC%86%8C%EA%B8%B0/","section":"Posts","summary":"import sysinput=sys.stdin.readlinen,m=map(int,input().split()) r,c,d=map(int,input().split())arr=[]for _ in range(n): arr.append([* map(int,input().split())])ddd=[[-1,0],[0,1],[1,0],[0,-1]] #방향 def dd(d,mv=1): #방향왼쪽으로 d-=mv if d\u003c0: d=4+d return dcnt=0 answer=0while True: if arr[r][c]==0: #현재칸이 비어있다면 arr[r][c]='#' #청소하고 answer+=1 #청소한칸+=1 next_d=dd(d) #현재의 왼쪽방향 a,b=ddd[next_d] rr,cc=r+a,c+b #왼쪽칸좌표 if not arr[rr][cc]: #왼쪽이 비어있다면 r,c=rr,cc # 다음 반복에 그 칸으로 이동 d=next_d # 방향바꿈 cnt=0 #카운트 초기화 continue else:#왼쪽이 차있다면 if cnt\u003c4: #4번이하라면, d=next_d #방향만 바꿈 cnt+=1 #횟수올려줌 if cnt==4: #4번연속 방향만 바꿨다면 next_d=dd(d,2) #뒤쪽을 탐색 a,b=ddd[next_d] rr,cc=a+r,b+c if arr[rr][cc]==1: #벽이라면 break #끗 else: #벽이아니라면 cnt=0 #카운트초기화 r,c=rr,cc #다음반복때 들어갈 칸print(answer) https://www.acmicpc.net/problem/14503\n14503번: 로봇 청소기로봇 청소기가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오. 로봇 청소기가 있는 장소는 N×M 크기의 직사각형으로 나타낼 수 있으며, 1×1크기의 정사각형 칸으로 나누어www.acmicpc.net\n","title":"14503 로봇 청소기","type":"posts"},{"content":"https://www.acmicpc.net/problem/1927\n힙2\n힙1과 같음\n구현:\nimport sysinput = sys.stdin.readlineheap=[0]def add(x): heap.append(x) l=len(heap)-1 while l\u0026gt;1: if heap[l]\u0026lt;heap[l\u0026gt;\u0026gt;1]: heap[l\u0026gt;\u0026gt;1],heap[l]=heap[l],heap[l\u0026gt;\u0026gt;1] l\u0026gt;\u0026gt;=1 else: returndef rm(): if len(heap)\u0026lt;=1: return 0 heap[1],heap[-1]=heap[-1],heap[1] res=heap.pop() heapify() return resdef heapify(node=1): l=len(heap) left,right=0,0 tmp=node*2 if tmp\u0026lt;l: left=tmp if left+1 \u0026lt; l: right=left+1 else : return if right: tmp=[left,right][heap[left]\u0026gt;heap[right]] else: tmp=left if heap[tmp]\u0026lt;heap[node]: heap[node],heap[tmp]=heap[tmp],heap[node] heapify(tmp)for _ in range(int(input())): x=int(input()) if x==0: print(rm()) else: add(x) 모듈:\nimport sysimport heapqinput = sys.stdin.readlineh=[] for _ in range(int(input())): x=int(input()) if x==0: try: print(heapq.heappop(h)) except:print(0) else: heapq.heappush(h,x) ","date":"7 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/134-1927-%EC%B5%9C%EC%86%8C%ED%9E%99/","section":"Posts","summary":"https://www.acmicpc.net/problem/1927\n힙2\n힙1과 같음\n구현:\nimport sysinput = sys.stdin.readlineheap=[0]def add(x): heap.append(x) l=len(heap)-1 while l\u003e1: if heap[l]\u003cheap[l\u003e\u003e1]: heap[l\u003e\u003e1],heap[l]=heap[l],heap[l\u003e\u003e1] l\u003e\u003e=1 else: returndef rm(): if len(heap)\u003c=1: return 0 heap[1],heap[-1]=heap[-1],heap[1] res=heap.pop() heapify() return resdef heapify(node=1): l=len(heap) left,right=0,0 tmp=node*2 if tmp\u003cl: left=tmp if left+1 \u003c l: right=left+1 else : return if right: tmp=[left,right][heap[left]\u003eheap[right]] else: tmp=left if heap[tmp]\u003cheap[node]: heap[node],heap[tmp]=heap[tmp],heap[node] heapify(tmp)for _ in range(int(input())): x=int(input()) if x==0: print(rm()) else: add(x) 모듈:\nimport sysimport heapqinput = sys.stdin.readlineh=[] for _ in range(int(input())): x=int(input()) if x==0: try: print(heapq.heappop(h)) except:print(0) else: heapq.heappush(h,x)","title":"1927 최소힙","type":"posts"},{"content":"오랫만에 글쓴다\nheap은 정말 ㅈ같은 구조이다.\n여서 설명할 자신이 없으니 그냥 나중에 인터넷 다시한번 찾아본다.\n이진트리이며, 배열로 구성되있고, maxheap이므로, 모든 부모는 자식보다 커야한다.\n편의를 위해 배열의 첫 번째는 0이다.\n먼져 insert는, 배열의 마지막에 삽입한다. 이후, 2씩 나눠주며(몫만취함) \u0026ndash;완전 2진트리이므로 2씩나누면 부모임\n부모가 자기보다 큰 원소일때까지 바꿔나간다.(아님루트일때만)\n힙에서 자료를 빼는것이 조금 까다롭다.\n먼져, 루트와 마지막노드를 바꿔준다. 그리고 pop해주면, 일단 빼긴 뺀거다.\n그럼 당연히 마지막 노드는 작을것이므로, heapify()를 수행해준다.\nheapify()란 루트부터 내려가면서 자신의 자식들과 비교하며 자식보다 자기가 작을 경우엔 위치를 바꾼다 \u0026ndash;힙구조를 유지시키는 연산이다\n재귀적으로 구현해야한다.\n참고로, 자식이 2개라면 두 자식들 중 큰놈과 비교하여 자리를 바꾸는 거다.\n제일 ㅈ같은거는, 이미 천재들이 모듈을 만들어놨다.\n모듈을 쓰면 내것보다 코드가 4배는 짧고 3배는 빠르다.\n괜히 구현해보겠다고 객기부렸다가 2시간동안 뻘짓했다.\n클래스로 구현 (별치이없음) import sysinput=sys.stdin.readlineimport heapqclass MaxHeap: def __init__(self): self.data=[None] def insert(self,item): #삽입 #임시로 마지막에 원소 추가 self.data.append(item) i=len(self.data)-1 #i가 추가된 아이템임 while i\u0026gt;1: if self.data[i] \u0026gt; self.data[(i\u0026gt;\u0026gt;1)]: # 만일 추가된 데이터가 부모부다 크다면 self.data[i],self.data[(i\u0026gt;\u0026gt;1)]=self.data[(i\u0026gt;\u0026gt;1)],self.data[i] #둘의 위치를 바꿔준다 i=i\u0026gt;\u0026gt;1 #바꿨으니 부모노드의 값을 가진다 else: break #만일 추가된 데이터가 부모보다 작으면, 거기가 알맞은 위치이다. def remove(self): #삭제 if len(self.data)\u0026gt;1: #만일 data가 있다면 [none생각] self.data[1],self.data[-1]=self.data[-1],self.data[1] #루트와 마지막값을 바꾼다 data=self.data.pop() self.maxHeapify(1) else: data=0 return data def maxHeapify(self,i): left=i\u0026lt;\u0026lt;1 right=left+1 smallest=i d=0 if left\u0026lt;len(self.data) and self.data[i]\u0026lt;self.data[left]: #왼쪽자식이 존재하고, 왼쪽자식보다 작다면 smallest=left d=self.data[left] if right\u0026lt;len(self.data) and self.data[i]\u0026lt;self.data[right] and self.data[right]\u0026gt;d: #오른자식이 존재하고, 이보다 작다면 smallest=right if smallest !=i: #현재노드가 최솟값이 아니면 self.data[i],self.data[smallest]=self.data[smallest],self.data[i] self.maxHeapify(smallest)h=MaxHeap()n=int(input())for _ in range(n): x=int(input()) if x==0: print(h.remove()) else: h.insert(x) 그냥 배열로 구현 import sysinput =sys.stdin.readlineheap=[0]def add(x): heap.append(x) idx= len(heap)-1 while idx\u0026gt;1: if heap[idx]\u0026gt;heap[idx\u0026gt;\u0026gt;1]: heap[idx],heap[idx\u0026gt;\u0026gt;1]=heap[idx\u0026gt;\u0026gt;1],heap[idx] idx\u0026gt;\u0026gt;=1 else: returndef remove(): if len(heap)\u0026gt;1: heap[1],heap[-1]=heap[-1],heap[1] val=heap.pop() heapify(1) else: val=0 return valdef heapify(node): l=len(heap) left=node\u0026lt;\u0026lt;1 right = left+1 max_val=0 if left\u0026lt;l: if right\u0026lt;l: max_val=[left,right][heap[left]\u0026lt;heap[right]] else: max_val=left if max_val and heap[max_val]\u0026gt;heap[node]: heap[node],heap[max_val]=heap[max_val],heap[node] heapify(max_val) return for _ in range(int(input())): x=int(input()) if x==0: print(remove()) else: add(x) 모듈로 구현 import sysinput=sys.stdin.readlineimport heapqheap=[] for _ in range(int(input())): num= int(input()) if not num : if not heap: print(0) else: print(heapq.heappop(heap)[1]) else: heapq.heappush(heap,(-num,num)) 나중에 헷갈리면 인터넷 다시헌번 찾아보자\n그리고 모듈을 쓰자\n","date":"6 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/133-11279%EC%B5%9C%EB%8C%80%ED%9E%99/","section":"Posts","summary":"오랫만에 글쓴다\nheap은 정말 ㅈ같은 구조이다.\n여서 설명할 자신이 없으니 그냥 나중에 인터넷 다시한번 찾아본다.\n이진트리이며, 배열로 구성되있고, maxheap이므로, 모든 부모는 자식보다 커야한다.\n편의를 위해 배열의 첫 번째는 0이다.\n먼져 insert는, 배열의 마지막에 삽입한다. 이후, 2씩 나눠주며(몫만취함) –완전 2진트리이므로 2씩나누면 부모임\n부모가 자기보다 큰 원소일때까지 바꿔나간다.(아님루트일때만)\n힙에서 자료를 빼는것이 조금 까다롭다.\n먼져, 루트와 마지막노드를 바꿔준다. 그리고 pop해주면, 일단 빼긴 뺀거다.\n그럼 당연히 마지막 노드는 작을것이므로, heapify()를 수행해준다.\nheapify()란 루트부터 내려가면서 자신의 자식들과 비교하며 자식보다 자기가 작을 경우엔 위치를 바꾼다 –힙구조를 유지시키는 연산이다\n","title":"11279최대힙","type":"posts"},{"content":"개념만 알면 쉽다\n그냥 리스트로 하면 O(mn) 이 된다.\n참고로 행렬 m개에 대해 n번의 더하기를 수행한다는 뜻이다.\n따라서, 누적합리스트를 생성한다.\n그리고 구간이 주어지면, 끝구간의 값에서 시작구간 값-1을 해주면 된다\nimport sysinput=sys.stdin.readlinel=[0]n,m=map(int,input().split()) t=[* map(int,input().split())]for tt in t: l.append(l[-1]+tt) for _ in range(m): a,b=map(int,input().split()) print(l[b]-l[a-1]) ","date":"5 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/132-11659-%EA%B5%AC%EA%B0%84%ED%95%A9-%EA%B5%AC%ED%95%98%EA%B8%B0-4/","section":"Posts","summary":"개념만 알면 쉽다\n그냥 리스트로 하면 O(mn) 이 된다.\n참고로 행렬 m개에 대해 n번의 더하기를 수행한다는 뜻이다.\n따라서, 누적합리스트를 생성한다.\n그리고 구간이 주어지면, 끝구간의 값에서 시작구간 값-1을 해주면 된다\nimport sysinput=sys.stdin.readlinel=[0]n,m=map(int,input().split()) t=[* map(int,input().split())]for tt in t: l.append(l[-1]+tt) for _ in range(m): a,b=map(int,input().split()) print(l[b]-l[a-1])","title":"11659 구간합 구하기 4","type":"posts"},{"content":"OS=OPERATING SYSTEM = 운영체제\n접근제어+동기화+관리\n프로세스들 관리하기 (자원관리) 컴퓨터가 \u0026lsquo;국가\u0026rsquo; 라면 운영체제는 \u0026lsquo;정부조직\u0026rsquo; 이다 스레드는 \u0026lsquo;개인\u0026rsquo;이고 프로세스는 \u0026lsquo;가족\u0026rsquo; 이다\n커널영역 - 유저영역\n프로세스는 각자의 전용공간을 가지고 있다. (virtual Memory)\n이 안의 작업단위가 쓰레드임\n따라서, 쓰레드들은 프로세스에게 할당된 전용공간을 공유한다.\n따라서 동기화가 중요하다.\n가장 중요한 전산자원은 cpu와 메모리이다. 운영체제는 이것을 프로세스에게 분배환다.\n이를 분배하는 것을 스케줄링이라 한다.\n쓰레드마다 stack, 프로세스마다 heap과 실행코드 영역을 갖는다.\n가상메모리란?\nRAM과 HDD를 하나의논리적 메모리로 추상화시킨 메모리 관리 방법\n모든 메모리는 \u0026lsquo;페이지\u0026rsquo; 단위로 관리되며\nPaged 될 수 있는 \u0026lsquo;페이징 풀\u0026rsquo; 영역과 절대로 Paged되면 안 되는 \u0026lsquo;비페이징 풀\u0026rsquo; 영역이 있다. (커널)\n프로세스가 운영체제로부터 메모미를 할당 받는 일은 시간이 꽤 많이 걸린다.\n","date":"5 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/131-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EA%B0%80-%ED%95%98%EB%8A%94-%EC%9D%BC/","section":"Posts","summary":"OS=OPERATING SYSTEM = 운영체제\n접근제어+동기화+관리\n프로세스들 관리하기 (자원관리) 컴퓨터가 ‘국가’ 라면 운영체제는 ‘정부조직’ 이다 스레드는 ‘개인’이고 프로세스는 ‘가족’ 이다\n커널영역 - 유저영역\n프로세스는 각자의 전용공간을 가지고 있다. (virtual Memory)\n이 안의 작업단위가 쓰레드임\n따라서, 쓰레드들은 프로세스에게 할당된 전용공간을 공유한다.\n따라서 동기화가 중요하다.\n가장 중요한 전산자원은 cpu와 메모리이다. 운영체제는 이것을 프로세스에게 분배환다.\n이를 분배하는 것을 스케줄링이라 한다.\n쓰레드마다 stack, 프로세스마다 heap과 실행코드 영역을 갖는다.\n가상메모리란?\nRAM과 HDD를 하나의논리적 메모리로 추상화시킨 메모리 관리 방법\n","title":"운영체제가 하는 일","type":"posts"},{"content":"동시성 = 여러 일을 여러 사람이 각자 동시에 하는 것\n병렬성 = 같은 (혹은 대상이 같은 ) 일을 여러 사람이 동시에 하는 것\n원자성 = 쪼개어 나눌 수 없는 연속된 일\n의존성 = 전제조건 보통 행위나 존립(성립)에 대해 의존성을 고려\ndeadlock= 교착상태\n","date":"5 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/130-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%9A%A9%EC%96%B4%EC%A0%95%EB%A6%AC/","section":"Posts","summary":"동시성 = 여러 일을 여러 사람이 각자 동시에 하는 것\n병렬성 = 같은 (혹은 대상이 같은 ) 일을 여러 사람이 동시에 하는 것\n원자성 = 쪼개어 나눌 수 없는 연속된 일\n의존성 = 전제조건 보통 행위나 존립(성립)에 대해 의존성을 고려\ndeadlock= 교착상태\n","title":"간단한 용어정리","type":"posts"},{"content":"우리가 어떤 정보에 접근하려면, 이 자료에 적절히 접근해야한다.\n이 정보가 저장된 방식이 자료구조, 이에 접근하는 방식이 알고리즘이다.\n다시, \u0026lsquo;일정규칙으로 정리된 자료\u0026rsquo; 가 자료구조이고, 이 구조화된 자료에서 원하는 것을 빨리 찾아내는 방법이 알고리즘이다.\n일열로 되있는 자료구조를 선형구조라고 한다.\nlist and linked list\nlist(배열)\n배열은 말그대로 자료가 연접함\n그냥 idx증가시키면 다음 자료임\n가장 단순\n크기를 늘리거나 줄이려면 구조를 변경해야함\n중간에 삽입 또는 삭제도 문제\nlinked list\n각자 따로 떨어진 자료를 위치정보로 묶은 것\n한 자료에서 다음으로 넘어가려면 위치정보를 활용해 찾아가야 함\n배열에 비해 복잡 옆으로 가는것이아니라 찾아가야 함\n순서 바꾸기가 매우 쉬움\n중간 삽입삭제도 쉬움\n비선형 자료구조 (트리,그래프)\n트리 (선형 자료구조에 대해 매우 우수한 성능)\n자료당 두 개의 위치정보를 이용해 셋을 하나로 묶는다.\n","date":"5 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/129-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98/","section":"Posts","summary":"우리가 어떤 정보에 접근하려면, 이 자료에 적절히 접근해야한다.\n이 정보가 저장된 방식이 자료구조, 이에 접근하는 방식이 알고리즘이다.\n다시, ‘일정규칙으로 정리된 자료’ 가 자료구조이고, 이 구조화된 자료에서 원하는 것을 빨리 찾아내는 방법이 알고리즘이다.\n일열로 되있는 자료구조를 선형구조라고 한다.\nlist and linked list\nlist(배열)\n배열은 말그대로 자료가 연접함\n그냥 idx증가시키면 다음 자료임\n가장 단순\n크기를 늘리거나 줄이려면 구조를 변경해야함\n중간에 삽입 또는 삭제도 문제\nlinked list\n각자 따로 떨어진 자료를 위치정보로 묶은 것\n","title":"자료구조와 알고리즘","type":"posts"},{"content":"반도체는 전기가 흐르는 도체와 흐르지 않는 부도체 성격을 조건에 따라 선택할 수 있다.\na = 0 0 1 0 (2)\n+b = 0 0 1 1 (3)\nres= 1 0 1 (5)\n빼기일때는? 보수를 더하면 됨\n보수란?\n12-9 =?\n9의 10에대한 보수는 9를 10으로 만들어 줄 수 있는 수이다. 따라서 1\n12에다가 보수인 1을 더하고, 윗자리 숫자를 하나 버림\n13 -\u0026gt; 3\n2진수로 할 때는 2의 보수를 씀\n2의 보수= 1의보수 +1\n1의 보수란?\n0-\u0026gt;1\n1-\u0026gt;0\n이렇게 뒤집으면 됨\n즉\n3-2= 0 0 1 1 - 0 0 1 0\n= 0 1 0 1 1 + (1 1 0 1 + 1) # 1의보수 + 1\n=1 0 0 0 1\n= 4비트를 넘어간 수는 사라지므로 0 0 0 1 =1\n컴퓨터는 더하는 방식으로 뺀다.\n곱셈하는법\n여러번 더하면 곱셈이 됨 ㅋ\n나눗셈하는법\n여러번 빼면 나눗셈이 됨 ㅋ\n5/3\n5-3=2\n2\u0026lt;3 이므로 나머지\n\u0026laquo; = *2\n= /2\n참고\nhttps://ndb796.tistory.com/4\n","date":"5 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/128-cpu%EA%B0%80-%EC%9E%91%EB%8F%99%ED%95%98%EB%8A%94-%EC%9B%90%EB%A6%AC/","section":"Posts","summary":"반도체는 전기가 흐르는 도체와 흐르지 않는 부도체 성격을 조건에 따라 선택할 수 있다.\na = 0 0 1 0 (2)\n+b = 0 0 1 1 (3)\nres= 1 0 1 (5)\n빼기일때는? 보수를 더하면 됨\n보수란?\n12-9 =?\n9의 10에대한 보수는 9를 10으로 만들어 줄 수 있는 수이다. 따라서 1\n12에다가 보수인 1을 더하고, 윗자리 숫자를 하나 버림\n","title":"cpu가 작동하는 원리","type":"posts"},{"content":"기본 상식 반드시 외울것\nColumn 1 Column 2 Column 3 단위 크기 특징 1bit 전기 스위치 1개 (0,1) 용량이 아니라 표한할 수 있는 최소수준 1Byte 8bit 한 묶음 영문자 1개, 메모리 관리 단위 (한글은2) 여기서부턴 알아만 놓자 1024배 증가할땨맏 단위가 바뀐다\n1KB 1024byte\n1MB 1024kb\n1GB 1024mb\n1TB 1024gb\n1PB\n1EB\n1ZB\n1YB\n4비트는 16진수 1자리로 표현이 가능하다. 1비트는 2진수 한 자리 숫자가 되고, \u0026lsquo;4비트\u0026rsquo;를 하나로 묶어 16진수 1자리로 표현\n2진수 1011= 10진수 11\n이를 한자리로 표시하기 위해서 16진수에서는 A로 쓴다\n16진수를 나타낼때 0x를 씀\n2진수 1 1 0 0 0 0 1 1을 16진수로 나타내려면,\n4자리씩 끊으면 1 1 0 0 0 0 1 1 이 된다.\n이는 각각 12, 3 이고, 16진수표기에 따라 c,3이되어 결고 0xC3이 된다.\n이를 10진수로 나타내면,\n12*16^1 + 3이므로 192+3=195 가 된다.\n","date":"5 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/127-%EC%9A%A9%EB%9F%89/","section":"Posts","summary":"기본 상식 반드시 외울것\nColumn 1 Column 2 Column 3 단위 크기 특징 1bit 전기 스위치 1개 (0,1) 용량이 아니라 표한할 수 있는 최소수준 1Byte 8bit 한 묶음 영문자 1개, 메모리 관리 단위 (한글은2) 여기서부턴 알아만 놓자 1024배 증가할땨맏 단위가 바뀐다\n1KB 1024byte\n1MB 1024kb\n1GB 1024mb\n1TB 1024gb\n1PB\n1EB\n1ZB\n1YB\n4비트는 16진수 1자리로 표현이 가능하다. 1비트는 2진수 한 자리 숫자가 되고, ‘4비트’를 하나로 묶어 16진수 1자리로 표현\n","title":"용량","type":"posts"},{"content":"자료형이란? 일정 길이의 메모리에 저장된 정보를 해석하는 방법\n자료(정보) -\u0026gt; 수(숫자) 메모리에 보관됨\n관리를 쉽게 하려고 번호를 붙인다.\n하나의 공간에 저장할 수 있는 크기는 1byte (8bit) (영문1글자) 메모리의 최소단위\n이 공간에는 번호가 붙어있으며 우리는 이것을 메모리의 주소라고 부른다.\n변수란?\n-\u0026gt; 아직 확정되지 않은 숫자\n상수란?\n-\u0026gt;확정된 숫자\nc언어에선\n변수-\u0026gt;메모리 반드시 주소를 가지고 있음 이에 저장되어 있는것이 data 이를 해석하는 방법이 자료형\n컴퓨터의 모든 정보는 숫자임 문자로 보여도 숫자임 ord(\u0026lsquo;a)생각\n영어는 1byte 한글은 두배\nvoid= 없으면 없다고 말하는거 (헷갈림 방지)\nint a ; 선언\na=10 ; 정의\nint a=10; 선언 및 정의\nchar 8bits\nshort\nint 32bit (4byte)\nlong\nlong long int\n부동소수점\nfloat (32bit) 유효 자릿수 소숫점 이하 6자리\ndouble (64bit) 유효 자릿수 소숫점 이하 15자리\n실수형식은 근본적으로 오차를 가지고 있음 왜?\n근사값처리때문임\n꼭 double을 쓸 것\n문자열== 상수화된 문자 \u0026lsquo;배열\u0026rsquo;\n문자열의 끝은 항상 null문자 (\u0026quot;\\0\u0026quot;)\n","date":"4 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/126-2.-%EC%9E%90%EB%A3%8C%ED%98%95/","section":"Posts","summary":"자료형이란? 일정 길이의 메모리에 저장된 정보를 해석하는 방법\n자료(정보) -\u003e 수(숫자) 메모리에 보관됨\n관리를 쉽게 하려고 번호를 붙인다.\n하나의 공간에 저장할 수 있는 크기는 1byte (8bit) (영문1글자) 메모리의 최소단위\n이 공간에는 번호가 붙어있으며 우리는 이것을 메모리의 주소라고 부른다.\n변수란?\n-\u003e 아직 확정되지 않은 숫자\n상수란?\n-\u003e확정된 숫자\nc언어에선\n변수-\u003e메모리 반드시 주소를 가지고 있음 이에 저장되어 있는것이 data 이를 해석하는 방법이 자료형\n컴퓨터의 모든 정보는 숫자임 문자로 보여도 숫자임 ord(‘a)생각\n영어는 1byte 한글은 두배\n","title":"2. 자료형","type":"posts"},{"content":"IP를 직접 지정하려면 너무 어려움\n우리가 공유기에 연결되는 순간 각각의 기기에는 동적으로 복잡한 정보들이 설정됨 이를 위해 DHCP서버 필요!\n인터넷을 사용하는 기기에는 DHCP client 프로그램이 깔려있음\n우리가 사용하는 기계의 부품들엔, 공장에서 기록된 고유한 식별자를 가지고 있음\n이것이 MAC ADRRESS임 (physical address)\n","date":"4 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/124-dhcp/","section":"Posts","summary":"IP를 직접 지정하려면 너무 어려움\n우리가 공유기에 연결되는 순간 각각의 기기에는 동적으로 복잡한 정보들이 설정됨 이를 위해 DHCP서버 필요!\n인터넷을 사용하는 기기에는 DHCP client 프로그램이 깔려있음\n우리가 사용하는 기계의 부품들엔, 공장에서 기록된 고유한 식별자를 가지고 있음\n이것이 MAC ADRRESS임 (physical address)\n","title":"DHCP","type":"posts"},{"content":"맥에 기본적으로 깔려있는 apache를 통해 해보자\n먼져\n$ apachectl -v\n$ php -v\n를 통해 버전을 확인한다\nsudo apachectl start 를 통해 서버를 실행시켜준다\ncd /Library/WebServer/Documents 에 있는 index.html.en 파일을 확인한다\n포트포워딩을 통해 8081 포트로 오면, 내 컴퓨터의 80포트와 연결되게 한다\n이후 어디서나 http://59.26.61.169:8081/ 로 접속하면, 내가 띄운 페이지가 보이게 된다\n","date":"4 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/123-%ED%8F%AC%ED%8A%B8%ED%8F%AC%EC%9B%8C%EB%94%A9-%ED%95%B4%EB%B3%B4%EA%B8%B0/","section":"Posts","summary":"맥에 기본적으로 깔려있는 apache를 통해 해보자\n먼져\n$ apachectl -v\n$ php -v\n를 통해 버전을 확인한다\nsudo apachectl start 를 통해 서버를 실행시켜준다\ncd /Library/WebServer/Documents 에 있는 index.html.en 파일을 확인한다\n포트포워딩을 통해 8081 포트로 오면, 내 컴퓨터의 80포트와 연결되게 한다\n이후 어디서나 http://59.26.61.169:8081/ 로 접속하면, 내가 띄운 페이지가 보이게 된다\n","title":"포트포워딩 해보기","type":"posts"},{"content":"포트란? 영어로 항구임\n하나의 컴퓨터에는 여러가지 형테의 서버가 있을 수 있다.\n이 서버를 식별하기 위해서는 포트번호가 필요함\n0~1023 well-known-port\n포트포워딩이란?\n우리 서버에 접속하기 위에선, 공유기의 아피이 주소를 알려주는데, 그 아이피로 접속했을때 내 컴퓨터로 연결되게 하는 것\n내 공유기 고급에서 설정!\n","date":"3 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/122-port--port-forwarding/","section":"Posts","summary":"포트란? 영어로 항구임\n하나의 컴퓨터에는 여러가지 형테의 서버가 있을 수 있다.\n이 서버를 식별하기 위해서는 포트번호가 필요함\n0~1023 well-known-port\n포트포워딩이란?\n우리 서버에 접속하기 위에선, 공유기의 아피이 주소를 알려주는데, 그 아이피로 접속했을때 내 컴퓨터로 연결되게 하는 것\n내 공유기 고급에서 설정!\n","title":"port \u0026 port forwarding","type":"posts"},{"content":"맥\ncurl ifconfig.me -\u0026gt;외부 (59.26.61.169)\nipconfig getifaddr en0 -\u0026gt; 내부아이피\n59.26.61.169 로 접속하면 공유기 환경설정이 나옴\n내부아이피주소 == gateway address\n외부아이피 주소 == public ip address\n","date":"3 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/121-ip-%EC%A3%BC%EC%86%8C-%EC%95%8C%EC%95%84%EB%82%B4%EA%B8%B0/","section":"Posts","summary":"맥\ncurl ifconfig.me -\u003e외부 (59.26.61.169)\nipconfig getifaddr en0 -\u003e 내부아이피\n59.26.61.169 로 접속하면 공유기 환경설정이 나옴\n내부아이피주소 == gateway address\n외부아이피 주소 == public ip address\n","title":"IP 주소 알아내기","type":"posts"},{"content":"Network Address Translation\n우리가 위키피디아에 접속한다면?\n나 -\u0026gt; 공유기에 위키피디아로 가는 요청이 내 내부아이피의 요청이라는 것을 기록 -\u0026gt; 공유기에서 NAT 기술을 통해 public ip로 변경-\u0026gt;\n위키피디아로 보냄\n위키피디아에서 공유기로 응답을 보냄 -\u0026gt; 내 내부아이피가 요청했다는 사실을 파악 -\u0026gt; 나에게 응답\n내 컴퓨터를 서버로 사용한다면????????????????\n","date":"3 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/120-nat/","section":"Posts","summary":"Network Address Translation\n우리가 위키피디아에 접속한다면?\n나 -\u003e 공유기에 위키피디아로 가는 요청이 내 내부아이피의 요청이라는 것을 기록 -\u003e 공유기에서 NAT 기술을 통해 public ip로 변경-\u003e\n위키피디아로 보냄\n위키피디아에서 공유기로 응답을 보냄 -\u003e 내 내부아이피가 요청했다는 사실을 파악 -\u003e 나에게 응답\n내 컴퓨터를 서버로 사용한다면????????????????\n","title":"NAT","type":"posts"},{"content":"IPv4 - 0,0,0,0 ~ 255.255.255.255\nIPv6 - IPv4가 다채워져서 만들어짐\nIP address - 컴퓨터들이 정보를 주고받는데에서 필요\nwan (wide area network) = 통신사와 계약해서 받은 케이블 (공유기에 접속) (!! 공용ip !!) public ip\nlan = 공유기에 소속되어있는 컴퓨터들 (공유기 포함) ex)192.168.?. ? (!! 사설ip !!) private ip\n","date":"3 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/119-router-%EA%B3%B5%EC%9C%A0%EA%B8%B0/","section":"Posts","summary":"IPv4 - 0,0,0,0 ~ 255.255.255.255\nIPv6 - IPv4가 다채워져서 만들어짐\nIP address - 컴퓨터들이 정보를 주고받는데에서 필요\nwan (wide area network) = 통신사와 계약해서 받은 케이블 (공유기에 접속) (!! 공용ip !!) public ip\nlan = 공유기에 소속되어있는 컴퓨터들 (공유기 포함) ex)192.168.?. ? (!! 사설ip !!) private ip\n","title":"Router (공유기)","type":"posts"},{"content":"문제겪을때마다 하나씩 추가예정\nset directory: 를 통해 파일경로알수있음\n","date":"3 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/118-vim-%EC%97%90%EB%94%94%ED%84%B0/","section":"Posts","summary":"문제겪을때마다 하나씩 추가예정\nset directory: 를 통해 파일경로알수있음\n","title":"vim 에디터","type":"posts"},{"content":"내 컴퓨터의 ~/htdocs 폴더에서 진행 (만듬)\nindex.html 파일을 만듬\n터미널을 열고,\ndocker run -p 8888:80 -v ~/Desktop/htdocs:/usr/local/apache2/htdocs/ httpd (새로운 컨테이너 만든거임)\n#8888은 이미 jupyter가 사용하고있어서 난 8889포트를 썼더니 됨\n이제 호스트 파일 안에서 수정하면, 반영됨\n호스트의 8888:80을 연결하고, 호스트의 /Desktop/htdoc 와 컨테이너의 /usr/local/apache2/htdocs/\n-v 옵션은 volume을 뜻함 container와 host, 또는 또다른 container와 연결할 때 사용\n","date":"2 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/117-%ED%98%B8%EC%8A%A4%ED%8A%B8%EC%99%80-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%9D%98-%ED%8C%8C%EC%9D%BC%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%97%B0%EA%B2%B0/","section":"Posts","summary":"내 컴퓨터의 ~/htdocs 폴더에서 진행 (만듬)\nindex.html 파일을 만듬\n터미널을 열고,\ndocker run -p 8888:80 -v ~/Desktop/htdocs:/usr/local/apache2/htdocs/ httpd (새로운 컨테이너 만든거임)\n#8888은 이미 jupyter가 사용하고있어서 난 8889포트를 썼더니 됨\n이제 호스트 파일 안에서 수정하면, 반영됨\n호스트의 8888:80을 연결하고, 호스트의 /Desktop/htdoc 와 컨테이너의 /usr/local/apache2/htdocs/\n-v 옵션은 volume을 뜻함 container와 host, 또는 또다른 container와 연결할 때 사용\n","title":"호스트와 컨테이너의 파일시스템 연결","type":"posts"},{"content":" docker exec [OPTIONS] CONTAINER COMMAND [ARGS\u0026hellip;]\nex) docker exec -it ws3 /bin/sh docker exec -it ws3 /bin/bash(본,배쉬 쉘실행하는명령어 i,t옵션 필요)\n옵션 i와 t는 같이 사용하는 경우가 많은데 i는 –interactive 옵션으로 STDIN(표준입력)으로 컨테이너를 생성 하라는 뜻이다.t는 –tty 옵션으로 영어로 Allocate a pseudo-TTY 라고 설명이 되어있는데 여기서 pseudo-TTY는 유사 터미널로 컨테이너에 터미널 드라이버를 추가하여 컨테이너를 터미널을 이용하여 연결 할 수있도록 하는 옵션이다.정리하자면 i옵션으로 표준 입력을 받으며 t옵션으로 터미널로 연결 가능한 컨테이너를 만드는 것이다\n명령어를 실행하면, host가 아닌 container 내부에서 실행된 거임\n아파치 기본 index.html은./htdocs/안에 있음\n처음에는 apt update 해주고, vi나 nano등 설치\n","date":"2 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/116-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC--%EB%AA%85%EB%A0%B9%EC%96%B42/","section":"Posts","summary":" docker exec [OPTIONS] CONTAINER COMMAND [ARGS…]\nex) docker exec -it ws3 /bin/sh docker exec -it ws3 /bin/bash(본,배쉬 쉘실행하는명령어 i,t옵션 필요)\n옵션 i와 t는 같이 사용하는 경우가 많은데 i는 –interactive 옵션으로 STDIN(표준입력)으로 컨테이너를 생성 하라는 뜻이다.t는 –tty 옵션으로 영어로 Allocate a pseudo-TTY 라고 설명이 되어있는데 여기서 pseudo-TTY는 유사 터미널로 컨테이너에 터미널 드라이버를 추가하여 컨테이너를 터미널을 이용하여 연결 할 수있도록 하는 옵션이다.정리하자면 i옵션으로 표준 입력을 받으며 t옵션으로 터미널로 연결 가능한 컨테이너를 만드는 것이다\n","title":"네트워크 \u0026 명령어2","type":"posts"},{"content":"cli\n도커 어플을 실행시키거나 open -a Docker\n명령어 안되면 sudo해볼것\ndocker run [OPTIONS] IMAGE [COMMAND] [ARGS\u0026hellip;] 컨테이너 실행시키기\ndocker ps 만든 컨테이너 확인\n하나의 의미지는 여러 컨테이너로 만들 수 있다.\ndocker stop [OPTIONS] CONTAINER이름 [CONTAINER\u0026hellip;] 실행중인 컨테이너를 끄기\ndocker start 컨테이너이름 껐던 컨테이너 실행시키기\ndocker logs [OPTION] CONTAINER이름 로그 확인 (실시간원하면 -f 옵션 줄것)\ndocker rm [OPTIONS] CONTAINER이름 [CONTAINER\u0026hellip;] 삭제 (실행중인컨테이너면 에러남)\ndocker rmi image이름 (이미지 삭제)\n","date":"1 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/115-%EA%B8%B0%EB%B3%B8-%EB%AA%85%EB%A0%B9%EC%96%B4/","section":"Posts","summary":"cli\n도커 어플을 실행시키거나 open -a Docker\n명령어 안되면 sudo해볼것\ndocker run [OPTIONS] IMAGE [COMMAND] [ARGS…] 컨테이너 실행시키기\ndocker ps 만든 컨테이너 확인\n하나의 의미지는 여러 컨테이너로 만들 수 있다.\ndocker stop [OPTIONS] CONTAINER이름 [CONTAINER…] 실행중인 컨테이너를 끄기\ndocker start 컨테이너이름 껐던 컨테이너 실행시키기\ndocker logs [OPTION] CONTAINER이름 로그 확인 (실시간원하면 -f 옵션 줄것)\ndocker rm [OPTIONS] CONTAINER이름 [CONTAINER…] 삭제 (실행중인컨테이너면 에러남)\ndocker rmi image이름 (이미지 삭제)\n","title":"기본 명령어","type":"posts"},{"content":"vmware같은거라고 생각하면 됨 (서버용)\nappstore - program - process 생각\ndocker hub - 필요한 소프트웨어를 찾는곳 https://hub.docker.com/\nimage - docker hub 에서 다운받은거\ncontainer - image를 실행하는거\ndocker hub =\u0026gt; image 다운받는거를 Pull\nimage =\u0026gt; container 실행시키는것을 run\ndocker pull [OPTIONS] NAME[:TAG|@DIGEST] 이미지 다운받기\ndocker pull httpd (아파치)\ndocker images 이미지를 잘 다운받았는지 확인\n","date":"1 July 2022","externalUrl":null,"permalink":"/posts/2022-tistory/114-%EB%8F%84%EC%BB%A4%EB%9E%80/","section":"Posts","summary":"vmware같은거라고 생각하면 됨 (서버용)\nappstore - program - process 생각\ndocker hub - 필요한 소프트웨어를 찾는곳 https://hub.docker.com/\nimage - docker hub 에서 다운받은거\ncontainer - image를 실행하는거\ndocker hub =\u003e image 다운받는거를 Pull\nimage =\u003e container 실행시키는것을 run\ndocker pull [OPTIONS] NAME[:TAG|@DIGEST] 이미지 다운받기\ndocker pull httpd (아파치)\ndocker images 이미지를 잘 다운받았는지 확인\n","title":"도커란?","type":"posts"},{"content":"헷갈리는 부분\nmainpost=get_object_or_404(Mainpost,pk=mainpost_id)\nform =MainpostForm(request.POST, instance=mainpost)\nMainpost모델의 form 인 MainpostForm을 불러오는데, instance를 받아온 mainpost로 준다.\n만일 instance를 안받으면, 이는 Mainpost model의 형식만 가져올 것이고, instance를 준다면, 이는 받아온데이터를 원래 있던 데이터에 덮어쓰게 된다.\n이후 form.is_valid()를 통하여 유효하면저장하고, 아니면 에러메시지를 남긴다.\n","date":"18 June 2022","externalUrl":null,"permalink":"/posts/2022-tistory/113-4%EC%9D%BC%EC%B0%A8/","section":"Posts","summary":"헷갈리는 부분\nmainpost=get_object_or_404(Mainpost,pk=mainpost_id)\nform =MainpostForm(request.POST, instance=mainpost)\nMainpost모델의 form 인 MainpostForm을 불러오는데, instance를 받아온 mainpost로 준다.\n만일 instance를 안받으면, 이는 Mainpost model의 형식만 가져올 것이고, instance를 준다면, 이는 받아온데이터를 원래 있던 데이터에 덮어쓰게 된다.\n이후 form.is_valid()를 통하여 유효하면저장하고, 아니면 에러메시지를 남긴다.\n","title":"4일차","type":"posts"},{"content":"오늘은 집중을 별로 못했다.\nXMLHttpRequest 를 통해 서버와 비동기 통신을 하여 아이디 중복체크 기능을 만들었다.\n도중 넘어온 json데이터는, json형식이 아닌 문자열 형식으로 온다. 이것땜에 시간을 많이 잡아먹었다. JSON.parse 를 해주면 해결된다.\n이후 대충 부트스트랩 강의와 점프투 장고 강의를 보면서, 틀을 꾸려나가고 있다.\n새로 안 사실인데, 줄바꿈을 나타내주려면, 컨텐츠를 나타낼때 [ |linebreaksbr ] 을 해주면 된다.\n오늘은 이거보다 그냥 유튜브만 본거같다.\n내일 일단 글 수정, 삭제기능을 할 예정이다.\n","date":"16 June 2022","externalUrl":null,"permalink":"/posts/2022-tistory/112-3%EC%9D%BC%EC%B0%A8/","section":"Posts","summary":"오늘은 집중을 별로 못했다.\nXMLHttpRequest 를 통해 서버와 비동기 통신을 하여 아이디 중복체크 기능을 만들었다.\n도중 넘어온 json데이터는, json형식이 아닌 문자열 형식으로 온다. 이것땜에 시간을 많이 잡아먹었다. JSON.parse 를 해주면 해결된다.\n이후 대충 부트스트랩 강의와 점프투 장고 강의를 보면서, 틀을 꾸려나가고 있다.\n새로 안 사실인데, 줄바꿈을 나타내주려면, 컨텐츠를 나타낼때 [ |linebreaksbr ] 을 해주면 된다.\n오늘은 이거보다 그냥 유튜브만 본거같다.\n내일 일단 글 수정, 삭제기능을 할 예정이다.\n","title":"3일차","type":"posts"},{"content":"유저모델은 따로 추가 없이 그냥 auth_user_model사용하기로 했다.\n기본적인 글과 댓글을 남길 수 있는걸 구현하고, 로그인이 필요하도록 했다.\n부트스트랩 강의를 보면서 하나씩 알아가고 있다.\n메인페이지를 만들고, 메인페이지에서 계시판 등의 어플리케이션에 접근 할 수 있도록 할 계획이다.\n일단 내일까진 혼자하고, 목요일엔 어짜피 가야되니깐 진수형한테 함 보여주고 피드백을 받아야겠다.\n이번주에 집에서 usb받아서 노트북에 리눅스 설치하는거 잊지 말자\n","date":"14 June 2022","externalUrl":null,"permalink":"/posts/2022-tistory/111-2%EC%9D%BC%EC%B0%A8/","section":"Posts","summary":"유저모델은 따로 추가 없이 그냥 auth_user_model사용하기로 했다.\n기본적인 글과 댓글을 남길 수 있는걸 구현하고, 로그인이 필요하도록 했다.\n부트스트랩 강의를 보면서 하나씩 알아가고 있다.\n메인페이지를 만들고, 메인페이지에서 계시판 등의 어플리케이션에 접근 할 수 있도록 할 계획이다.\n일단 내일까진 혼자하고, 목요일엔 어짜피 가야되니깐 진수형한테 함 보여주고 피드백을 받아야겠다.\n이번주에 집에서 usb받아서 노트북에 리눅스 설치하는거 잊지 말자\n","title":"2일차","type":"posts"},{"content":"오늘부터 kdt게시판을 만들어볼려고한다\n가장 먼저 익명의 계시판을 만들고, 이후에 차차 여러 기능들을 추가할 것이다.\n오늘은 기본적인 백엔드 뼈대만 만들어볼려한다.\n가장먼저 파이썬 가상환경을 만들고, 장고를 설치했다.\nhomepage라는 루트 디렉토리 안에, config라는 설정 디렉토리를 만들고, 가장먼저 계시판 board 부터 구현했다.\nsettings.py에서 templates폴더 설정을해주고, 앱에 board를 추가시켰다.\nconfig의 urls.py에서 board를 include해줬다.\n계시글이 될 Mainpost, 답변이 될 Comment 모델을 생성했다.\nmakemigrations, migrate를 통해 db에 반영해주고, admin superuser을 생성했다.\n이후 forms를 통해 계시글 form 을 만들고, 이를 통해 계시글을 등록할 수 있게 했다.\n├── board│ ├── init.py│ ├── pycache│ ├── admin.py│ ├── apps.py│ ├── forms.py│ ├── migrations│ │ ├── 0001_initial.py│ │ ├── init.py│ │ └── pycache│ ├── models.py│ ├── tests.py│ ├── urls.py│ └── views.py├── config│ ├── init.py│ ├── pycache│ ├── asgi.py│ ├── settings.py│ ├── urls.py│ └── wsgi.py├── db.sqlite3├── manage.py└── templates ├── board │ ├── mainpost_detail.html │ ├── mainpost_form.html │ └── mainpost_list.html └── common └── login.html\n이후에 pythonanywhere에 호스팅해봤다.\n솔직히 뭐가뭔지 모르겠다.\n그냥 장고걸즈블로그 따라한거다\n오늘은여서끝내자\n","date":"13 June 2022","externalUrl":null,"permalink":"/posts/2022-tistory/110-kdt%EA%B2%8C%EC%8B%9C%ED%8C%90%EB%A7%8C%EB%93%A4%EA%B8%B0/","section":"Posts","summary":"오늘부터 kdt게시판을 만들어볼려고한다\n가장 먼저 익명의 계시판을 만들고, 이후에 차차 여러 기능들을 추가할 것이다.\n오늘은 기본적인 백엔드 뼈대만 만들어볼려한다.\n가장먼저 파이썬 가상환경을 만들고, 장고를 설치했다.\nhomepage라는 루트 디렉토리 안에, config라는 설정 디렉토리를 만들고, 가장먼저 계시판 board 부터 구현했다.\nsettings.py에서 templates폴더 설정을해주고, 앱에 board를 추가시켰다.\nconfig의 urls.py에서 board를 include해줬다.\n계시글이 될 Mainpost, 답변이 될 Comment 모델을 생성했다.\nmakemigrations, migrate를 통해 db에 반영해주고, admin superuser을 생성했다.\n이후 forms를 통해 계시글 form 을 만들고, 이를 통해 계시글을 등록할 수 있게 했다.\n","title":"kdt게시판만들기","type":"posts"},{"content":"https://wikidocs.net/71240\n글이 여러개라면, 화면에 끝도없이 나타날 것이므로 페이징의 필요하다.\nviews.py에\nfrom django.core.paginator import Paginator 를 사용한다.\npage = request.GET.get(\u0026lsquo;page\u0026rsquo;, \u0026lsquo;1\u0026rsquo;)# get 방식으로 호출된 url에서 페이지를 가져올 때 사용한다. 호출값이 없을 땐 default로 1을 반환한다.\ncontext로 page_obj = paginator.get_page(page)를 받게된다.\nquestion_list.html의 페이징 코드이다.\n\u0026lt;!-- 페이징처리 시작 --\u0026gt; \u0026lt;ul class=\u0026#34;pagination justify-content-center\u0026#34;\u0026gt; \u0026lt;!-- 이전페이지 --\u0026gt; \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page=1\u0026#34;\u0026gt;처음\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% if question_list.has_previous %} \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page={{ question_list.previous_page_number }}\u0026#34;\u0026gt;이전\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% else %} \u0026lt;li class=\u0026#34;page-item disabled\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; tabindex=\u0026#34;-1\u0026#34; aria-disabled=\u0026#34;true\u0026#34; href=\u0026#34;#\u0026#34;\u0026gt;이전\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% endif %} \u0026lt;!-- 페이지리스트 --\u0026gt; {% for page_number in question_list.paginator.page_range %} {% if page_number \u0026gt;= question_list.number|add:-3 and page_number \u0026lt;= question_list.number|add:3 %} {% if page_number == question_list.number %} \u0026lt;li class=\u0026#34;page-item active\u0026#34; aria-current=\u0026#34;page\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page={{ page_number }}\u0026#34;\u0026gt;{{ page_number }}\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% else %} \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page={{ page_number }}\u0026#34;\u0026gt;{{ page_number }}\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% endif %} {% elif page_number \u0026gt;= question_list.number|add:-4 and page_number \u0026lt;= question_list.number|add:4 %} \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt;...\u0026lt;/li\u0026gt; {% endif %} {% endfor %} \u0026lt;!-- 다음페이지 --\u0026gt; {% if question_list.has_next %} \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page={{ question_list.next_page_number }}\u0026#34;\u0026gt;다음\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% else %} \u0026lt;li class=\u0026#34;page-item disabled\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; tabindex=\u0026#34;-1\u0026#34; aria-disabled=\u0026#34;true\u0026#34; href=\u0026#34;#\u0026#34;\u0026gt;다음\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% endif %} \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page={{ max_index }}\u0026#34;\u0026gt;마지막\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;!-- 페이징처리 끝 --\u0026gt; 이걸 이해할려고 하다가 너무 답답해서 쓴다.\n먼저, {% if question_list.has_previous를 %} 통하여, 이전페이지가 있으면 이전 버튼을 활성화/비활성화한다.\n그후,\nquestion_list.paginator.page_range는, 말그대로 모든 페이지 범위를 range로 가지고있다. #range(1,32)\n{% for page_number in question_list.paginator.page_range %} 를 통하여, page는 숫자를 가지게 된다.\n{% if page_number \u0026gt;= question_list.number|add:-3 and page_number \u0026lt;= question_list.number|add:3 %}\niquestion_list.number|add:3 의 뜻은, question_list+3이다. # 반복문으로 page를 1부터 돌거다.\n만일 page가 현재 페이지의 번호랑 절댓값 3이상의 차이면, 나타내라는 뜻이다. 이게 너무 헷갈렸다.\n전체코드\npybo/views.py\nfrom django.shortcuts import render,get_object_or_404,redirect from django.utils import timezone from django.http import HttpResponse, HttpResponseNotAllowedfrom . import models from .forms import QuestionForm,AnswerForm from django.core.paginator import Paginator# Create your views here. def index(request): page=request.GET.get(\u0026#39;page\u0026#39;,\u0026#39;1\u0026#39;) question_list=models.Question.objects.order_by(\u0026#39;-create_data\u0026#39;) pagintor=Paginator(question_list,10) page_obj=pagintor.get_page(page) context={\u0026#39;question_list\u0026#39;:page_obj,\u0026#39;max_index\u0026#39;:len(pagintor.page_range)} return render(request,\u0026#39;pybo/question_list.html\u0026#39;,context) def detail(request,question_id): # question=models.Question.objects.get(id=question_id) question=get_object_or_404(models.Question,pk=question_id) context={\u0026#39;question\u0026#39;:question} return render(request, \u0026#39;pybo/question_detail.html\u0026#39;,context) def answer_create(request,question_id): #답변등록 question=get_object_or_404(models.Question,pk=question_id) if request.method==\u0026#39;POST\u0026#39;: form = AnswerForm(request.POST) if form.is_valid(): answer=form.save(commit=False) answer.create_data=timezone.now() answer.question=question answer.save() return redirect(\u0026#39;pybo:detail\u0026#39;,question_id=question_id) else: return HttpResponseNotAllowed(\u0026#39;Only POST\u0026#39;) context={\u0026#39;question\u0026#39;:question,\u0026#39;form\u0026#39;:form} return render(request,\u0026#39;pybo/question_detail.html\u0026#39;,context) # question=get_object_or_404(models.Question,pk=question_id) # question.answer_set.create(content=request.POST.get(\u0026#39;content\u0026#39;), create_data=timezone.now()) # # answer=models.Answer(question=question,content=request.POST.get(\u0026#39;content\u0026#39;),create_data=timezone.now()) # # answer.save() # return redirect(\u0026#39;pybo:detail\u0026#39;,question_id=question.id) def question_create(request): if request.method==\u0026#34;POST\u0026#34;: form=QuestionForm(request.POST) if form.is_valid(): question=form.save(commit=False) question.create_data=timezone.now() question.save() return redirect(\u0026#39;pybo:index\u0026#39;) else: form=QuestionForm() context={\u0026#39;form\u0026#39;:form} return render(request,\u0026#39;pybo/question_form.html\u0026#39;,context) question_detail.html\n{% extends \u0026#39;base.html\u0026#39; %}{% block content %}\u0026lt;div class=\u0026#34;container my-3\u0026#34;\u0026gt; \u0026lt;table class=\u0026#34;table\u0026#34;\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr class=\u0026#34;table-dark\u0026#34;\u0026gt; \u0026lt;th\u0026gt;번호\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;제목\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;작성일시\u0026lt;/th\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody\u0026gt; {% if question_list %} {% for question in question_list %} \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;{{ forloop.counter }}\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt; \u0026lt;a href=\u0026#34;{% url \u0026#39;pybo:detail\u0026#39; question.id %}\u0026#34;\u0026gt;{{ question.subject }}\u0026lt;/a\u0026gt; \u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;{{ question.create_data }}\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; {% endfor %} {% else %} \u0026lt;tr\u0026gt; \u0026lt;td colspan=\u0026#34;3\u0026#34;\u0026gt;질문이 없습니다.\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; {% endif %} \u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;!-- 페이징처리 시작 --\u0026gt; \u0026lt;ul class=\u0026#34;pagination justify-content-center\u0026#34;\u0026gt; \u0026lt;!-- 이전페이지 --\u0026gt; \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page=1\u0026#34;\u0026gt;처음\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% if question_list.has_previous %} \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page={{ question_list.previous_page_number }}\u0026#34;\u0026gt;이전\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% else %} \u0026lt;li class=\u0026#34;page-item disabled\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; tabindex=\u0026#34;-1\u0026#34; aria-disabled=\u0026#34;true\u0026#34; href=\u0026#34;#\u0026#34;\u0026gt;이전\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% endif %} \u0026lt;!-- 페이지리스트 --\u0026gt; {% for page_number in question_list.paginator.page_range %} {% if page_number \u0026gt;= question_list.number|add:-3 and page_number \u0026lt;= question_list.number|add:3 %} {% if page_number == question_list.number %} \u0026lt;li class=\u0026#34;page-item active\u0026#34; aria-current=\u0026#34;page\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page={{ page_number }}\u0026#34;\u0026gt;{{ page_number }}\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% else %} \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page={{ page_number }}\u0026#34;\u0026gt;{{ page_number }}\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% endif %} {% elif page_number \u0026gt;= question_list.number|add:-4 and page_number \u0026lt;= question_list.number|add:4 %} \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt;...\u0026lt;/li\u0026gt; {% endif %} {% endfor %} \u0026lt;!-- 다음페이지 --\u0026gt; {% if question_list.has_next %} \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page={{ question_list.next_page_number }}\u0026#34;\u0026gt;다음\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% else %} \u0026lt;li class=\u0026#34;page-item disabled\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; tabindex=\u0026#34;-1\u0026#34; aria-disabled=\u0026#34;true\u0026#34; href=\u0026#34;#\u0026#34;\u0026gt;다음\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; {% endif %} \u0026lt;li class=\u0026#34;page-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;page-link\u0026#34; href=\u0026#34;?page={{ max_index }}\u0026#34;\u0026gt;마지막\u0026lt;/a\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;!-- 페이징처리 끝 --\u0026gt; \u0026lt;a href=\u0026#34;{% url \u0026#39;pybo:question_create\u0026#39; %}\u0026#34; class=\u0026#34;btn btn-primary\u0026#34;\u0026gt;질문 등록하기\u0026lt;/a\u0026gt; \u0026lt;/div\u0026gt;{% endblock %} 책에 안나와있는 처음,끝으로 가기와 \u0026hellip;까지 구현해봤다.\n처음으로 가는 버튼은 href=\u0026quot;?page=1\u0026quot;버튼을 만들면 되고,\n끝으로 가는 버튼은 views.py에서 context에 max_index하나를 추가해 page={{max_index}}로 가게 만들면 된다\n#len(paginator.get_page)\n\u0026hellip;을 구현하는 것은\n앞서 설명한 {% if page_number \u0026gt;= question_list.number|add:-3 and page_number \u0026lt;= question_list.number|add:3 %}\n에 대한 elif 구문을 만들고, |3|이 아닌 |4|를 통해 만들었다.\n정신나갈것같다.\n","date":"4 June 2022","externalUrl":null,"permalink":"/posts/2022-tistory/109-3-2%ED%8E%98%EC%9D%B4%EC%A7%95/","section":"Posts","summary":"https://wikidocs.net/71240\n글이 여러개라면, 화면에 끝도없이 나타날 것이므로 페이징의 필요하다.\nviews.py에\nfrom django.core.paginator import Paginator 를 사용한다.\npage = request.GET.get(‘page’, ‘1’)# get 방식으로 호출된 url에서 페이지를 가져올 때 사용한다. 호출값이 없을 땐 default로 1을 반환한다.\ncontext로 page_obj = paginator.get_page(page)를 받게된다.\nquestion_list.html의 페이징 코드이다.\n\u003c!-- 페이징처리 시작 --\u003e \u003cul class=\"pagination justify-content-center\"\u003e \u003c!-- 이전페이지 --\u003e \u003cli class=\"page-item\"\u003e \u003ca class=\"page-link\" href=\"?page=1\"\u003e처음\u003c/a\u003e \u003c/li\u003e {% if question_list.has_previous %} \u003cli class=\"page-item\"\u003e \u003ca class=\"page-link\" href=\"?page={{ question_list.previous_page_number }}\"\u003e이전\u003c/a\u003e \u003c/li\u003e {% else %} \u003cli class=\"page-item disabled\"\u003e \u003ca class=\"page-link\" tabindex=\"-1\" aria-disabled=\"true\" href=\"#\"\u003e이전\u003c/a\u003e \u003c/li\u003e {% endif %} \u003c!-- 페이지리스트 --\u003e {% for page_number in question_list.paginator.page_range %} {% if page_number \u003e= question_list.number|add:-3 and page_number \u003c= question_list.number|add:3 %} {% if page_number == question_list.number %} \u003cli class=\"page-item active\" aria-current=\"page\"\u003e \u003ca class=\"page-link\" href=\"?page={{ page_number }}\"\u003e{{ page_number }}\u003c/a\u003e \u003c/li\u003e {% else %} \u003cli class=\"page-item\"\u003e \u003ca class=\"page-link\" href=\"?page={{ page_number }}\"\u003e{{ page_number }}\u003c/a\u003e \u003c/li\u003e {% endif %} {% elif page_number \u003e= question_list.number|add:-4 and page_number \u003c= question_list.number|add:4 %} \u003cli class=\"page-item\"\u003e...\u003c/li\u003e {% endif %} {% endfor %} \u003c!-- 다음페이지 --\u003e {% if question_list.has_next %} \u003cli class=\"page-item\"\u003e \u003ca class=\"page-link\" href=\"?page={{ question_list.next_page_number }}\"\u003e다음\u003c/a\u003e \u003c/li\u003e {% else %} \u003cli class=\"page-item disabled\"\u003e \u003ca class=\"page-link\" tabindex=\"-1\" aria-disabled=\"true\" href=\"#\"\u003e다음\u003c/a\u003e \u003c/li\u003e {% endif %} \u003cli class=\"page-item\"\u003e \u003ca class=\"page-link\" href=\"?page={{ max_index }}\"\u003e마지막\u003c/a\u003e \u003c/li\u003e \u003c/ul\u003e \u003c!-- 페이징처리 끝 --\u003e 이걸 이해할려고 하다가 너무 답답해서 쓴다.\n","title":"3-2페이징","type":"posts"},{"content":"https://www.acmicpc.net/problem/9935\n9935번: 문자열 폭발첫째 줄에 문자열이 주어진다. 문자열의 길이는 1보다 크거나 같고, 1,000,000보다 작거나 같다. 둘째 줄에 폭발 문자열이 주어진다. 길이는 1보다 크거나 같고, 36보다 작거나 같다. 두 문자열은 모www.acmicpc.net\n아이디어만 떠올리면 쉽다 (그래서 어렵다)\n먼져, stack에 하나씩 넣는다.\n그후, stack의 마지막 문자가 폭팔문자의 마지막이면,\n폭팔문자수만큼 확인하면서 빼버린다.\ns=list(input())boom=list(input())stack=[]for i in range(len(s)): stack.append(s[i]) if stack[-1]==boom[-1] and len(stack)\u0026gt;=len(boom): if stack[-len(boom):]==boom: del stack[-len(boom):]if stack: print(\u0026#39;\u0026#39;.join(stack))else: print(\u0026#39;FRULA\u0026#39;) ","date":"20 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/108-%EB%B0%B1%EC%A4%80-9935-%EB%AC%B8%EC%9E%90%EC%97%B4-%ED%8F%AD%ED%8C%94/","section":"Posts","summary":"https://www.acmicpc.net/problem/9935\n9935번: 문자열 폭발첫째 줄에 문자열이 주어진다. 문자열의 길이는 1보다 크거나 같고, 1,000,000보다 작거나 같다. 둘째 줄에 폭발 문자열이 주어진다. 길이는 1보다 크거나 같고, 36보다 작거나 같다. 두 문자열은 모www.acmicpc.net\n아이디어만 떠올리면 쉽다 (그래서 어렵다)\n먼져, stack에 하나씩 넣는다.\n그후, stack의 마지막 문자가 폭팔문자의 마지막이면,\n폭팔문자수만큼 확인하면서 빼버린다.\ns=list(input())boom=list(input())stack=[]for i in range(len(s)): stack.append(s[i]) if stack[-1]==boom[-1] and len(stack)\u003e=len(boom): if stack[-len(boom):]==boom: del stack[-len(boom):]if stack: print(''.join(stack))else: print('FRULA')","title":"백준 9935 문자열 폭팔","type":"posts"},{"content":"https://www.acmicpc.net/problem/10026\n10026번: 적록색약적록색약은 빨간색과 초록색의 차이를 거의 느끼지 못한다. 따라서, 적록색약인 사람이 보는 그림은 아닌 사람이 보는 그림과는 좀 다를 수 있다. 크기가 N×N인 그리드의 각 칸에 R(빨강), G(초록)www.acmicpc.net\ndfs를 두번수행한다.\n처음엔 색깔을 다 따로,\n다음엔 초록,빨강을 묶어서\n끝\nimport sysfrom collections import dequeimport copyinput=sys.stdin.readline move=((-1,0),(1,0),(0,-1),(0,1))def bfs1(i,j,color,arr): que=deque() que.append((i,j)) arr[i][j]=-1 while que: i,j=que.popleft() for a,b in move: ti,tj=i+a,j+b if 0\u0026lt;=ti\u0026lt;n and 0\u0026lt;=tj\u0026lt;n and arr[ti][tj]==color: arr[ti][tj]=-1 que.append((ti,tj))n=int(input()) graph=[list(input().strip())for _ in range(n)]arr=copy.deepcopy(graph) arr1=copy.deepcopy(graph)for i in range(n): for j in range(n): if arr1[i][j] in (\u0026#39;R\u0026#39;,\u0026#39;G\u0026#39;): arr1[i][j]=\u0026#39;R\u0026#39;answer0=0answer1=0 for i in range(n): for j in range(n): if arr[i][j]!=-1: bfs1(i,j,arr[i][j],arr) answer0+=1 if arr1[i][j]!=-1: bfs1(i,j,arr1[i][j],arr1) answer1+=1print(answer0,answer1) ","date":"18 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/107-%EB%B0%B1%EC%A4%8010026%EC%A0%81%EB%A1%9D%EC%83%89%EC%95%BD/","section":"Posts","summary":"https://www.acmicpc.net/problem/10026\n10026번: 적록색약적록색약은 빨간색과 초록색의 차이를 거의 느끼지 못한다. 따라서, 적록색약인 사람이 보는 그림은 아닌 사람이 보는 그림과는 좀 다를 수 있다. 크기가 N×N인 그리드의 각 칸에 R(빨강), G(초록)www.acmicpc.net\ndfs를 두번수행한다.\n처음엔 색깔을 다 따로,\n다음엔 초록,빨강을 묶어서\n끝\nimport sysfrom collections import dequeimport copyinput=sys.stdin.readline move=((-1,0),(1,0),(0,-1),(0,1))def bfs1(i,j,color,arr): que=deque() que.append((i,j)) arr[i][j]=-1 while que: i,j=que.popleft() for a,b in move: ti,tj=i+a,j+b if 0\u003c=ti\u003cn and 0\u003c=tj\u003cn and arr[ti][tj]==color: arr[ti][tj]=-1 que.append((ti,tj))n=int(input()) graph=[list(input().strip())for _ in range(n)]arr=copy.deepcopy(graph) arr1=copy.deepcopy(graph)for i in range(n): for j in range(n): if arr1[i][j] in ('R','G'): arr1[i][j]='R'answer0=0answer1=0 for i in range(n): for j in range(n): if arr[i][j]!=-1: bfs1(i,j,arr[i][j],arr) answer0+=1 if arr1[i][j]!=-1: bfs1(i,j,arr1[i][j],arr1) answer1+=1print(answer0,answer1)","title":"백준10026적록색약","type":"posts"},{"content":"https://www.acmicpc.net/problem/1062\n1062번: 가르침첫째 줄에 단어의 개수 N과 K가 주어진다. N은 50보다 작거나 같은 자연수이고, K는 26보다 작거나 같은 자연수 또는 0이다. 둘째 줄부터 N개의 줄에 남극 언어의 단어가 주어진다. 단어는 영어 소문www.acmicpc.net\n거의 처음으로 풀어본 비트마스킹 문제였다.\n생각보다 할만했다.\n가장먼져, 무조건 포함되는 (a,n,t,i,c) 를 각각ord(\u0026lsquo;a)를 빼줘 비트집합 must_have로 바꾼다\n0b10000010000100000101 가 된다.\n이후, 단어를 받을때마다 \u0026amp;를 통해 (a,n,t,i,c)를 제거한다.\n그리고, 남아있는 단어들을 k-5개의 원소를 가지는 comb로 만들어서,\n각각의 받은 단어와 \u0026amp;로 비교했을때 받은 단어가 나오면 읽을 수 있는것이다.\n처음풀이해본거라 코드가 좀 난잡하다\nimport sysfrom itertools import combinationsinput=sys.stdin.readline n,k=map(int,input().split())must_have=0comb=list(range(26)) for w in (\u0026#39;a\u0026#39;,\u0026#39;n\u0026#39;,\u0026#39;t\u0026#39;,\u0026#39;i\u0026#39;,\u0026#39;c\u0026#39;): bn=(ord(w)-ord(\u0026#39;a\u0026#39;)) must_have|=1\u0026lt;\u0026lt;bn comb.remove(bn)word_list=[]for _ in range(n): tmp=0 wo=list(set(input().strip())) for w in wo: b= 1\u0026lt;\u0026lt;(ord(w)-ord(\u0026#39;a\u0026#39;)) tmp |= b tmp\u0026amp;= ~(must_have) word_list.append(tmp)if k\u0026lt;5: print(0) exit() teaches=combinations(comb,k-5)answer=0 for teach in teaches: tmp_answer=0 tmp=0 for t in teach: tmp |= 1\u0026lt;\u0026lt;t for word in word_list: if (word\u0026amp;tmp)==word: tmp_answer+=1 if tmp_answer\u0026gt;answer: answer=tmp_answer print(answer) 다른사람의 풀이를 보다가 시간이 엄청짧은 코드가 있길래 보니, 비트마스킹이 아닌\n집합연산으로만 풀었다.\n참고용#내풀이아님\n##새로운사실: 집합의 비교연산자는 상위집합인지 아닌지를 비교!\nfrom itertools import combinationsfrom sys import stdininput = stdin.readline def solution(): n,k = map(int, input().split()) if k \u0026lt; 5: return 0 k -= 5 learned = set(\u0026#39;antic\u0026#39;) unlearned = set() cant_read_words = [] can_read_cnt = 0 for _ in range(n): word = set(input().rstrip()) - learned if word: unlearned.update(word) cant_read_words.append(word) else: can_read_cnt += 1 if len(unlearned) \u0026lt;= k: return n answer = 0 for comb in combinations(unlearned,k): comb = set(comb) cnt = 0 for word in cant_read_words: if comb \u0026gt;= word: cnt += 1 answer = max(answer, cnt) answer += can_read_cnt return answer print(solution()) ","date":"18 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/106-%EB%B0%B1%EC%A4%801062-%EA%B0%80%EB%A5%B4%EC%B9%A8/","section":"Posts","summary":"https://www.acmicpc.net/problem/1062\n1062번: 가르침첫째 줄에 단어의 개수 N과 K가 주어진다. N은 50보다 작거나 같은 자연수이고, K는 26보다 작거나 같은 자연수 또는 0이다. 둘째 줄부터 N개의 줄에 남극 언어의 단어가 주어진다. 단어는 영어 소문www.acmicpc.net\n거의 처음으로 풀어본 비트마스킹 문제였다.\n생각보다 할만했다.\n가장먼져, 무조건 포함되는 (a,n,t,i,c) 를 각각ord(‘a)를 빼줘 비트집합 must_have로 바꾼다\n0b10000010000100000101 가 된다.\n이후, 단어를 받을때마다 \u0026를 통해 (a,n,t,i,c)를 제거한다.\n그리고, 남아있는 단어들을 k-5개의 원소를 가지는 comb로 만들어서,\n각각의 받은 단어와 \u0026로 비교했을때 받은 단어가 나오면 읽을 수 있는것이다.\n","title":"백준1062 가르침","type":"posts"},{"content":"https://www.acmicpc.net/problem/16928\n16928번: 뱀과 사다리 게임첫째 줄에 게임판에 있는 사다리의 수 N(1 ≤ N ≤ 15)과 뱀의 수 M(1 ≤ M ≤ 15)이 주어진다. 둘째 줄부터 N개의 줄에는 사다리의 정보를 의미하는 x, y (x \u0026lt; y)가 주어진다. x번 칸에 도착하면, y번 칸으www.acmicpc.net\n처음에 멍청하게 배열을 만들어서 풀려했다 ㅋㅋㅋㅋ\n간단한 dfs문제이다.\n하필이면 kdt시간에 푸느라 엄청 안풀렸는데, 집에서푸니 10분컷이였다.\n주의할 점은 단 하나이다.\n뱀과 사다리는 무조건 타야 하므로\n조건문을 제대로 설정해서, 타지 않은 경우를 큐에 넣지 않도록 주의하자\nimport sysfrom collections import dequeinput=sys.stdin.readlinedef bfs(start=1): que=deque() que.append(start) arr[start]=0 while que: now=que.popleft() for i in range(1,7): next=now+i if next==100: print(arr[now]+1) exit() elif next\u0026lt;=100 and arr[next]==-1: arr[next]=arr[now]+1 if next in ladder : if arr[ladder[next]]==-1: arr[ladder[next]]=arr[next] que.append(ladder[next]) elif next in snake : if arr[snake[next]]==-1: arr[snake[next]]=arr[next] que.append(snake[next]) else: que.append(next) ladder={}snake={} arr=[-1 for _ in range(101)]n,m=map(int,input().split())for _ in range(n): a,b=map(int,input().split()) ladder[a]=bfor _ in range(m): a,b=map(int,input().split()) snake[a]=bbfs(1) ","date":"16 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/105-%EB%B0%B1%EC%A4%80-16928-%EB%B1%80%EA%B3%BC-%EC%82%AC%EB%8B%A4%EB%A6%AC-%EA%B2%8C%EC%9E%84/","section":"Posts","summary":"https://www.acmicpc.net/problem/16928\n16928번: 뱀과 사다리 게임첫째 줄에 게임판에 있는 사다리의 수 N(1 ≤ N ≤ 15)과 뱀의 수 M(1 ≤ M ≤ 15)이 주어진다. 둘째 줄부터 N개의 줄에는 사다리의 정보를 의미하는 x, y (x \u003c y)가 주어진다. x번 칸에 도착하면, y번 칸으www.acmicpc.net\n처음에 멍청하게 배열을 만들어서 풀려했다 ㅋㅋㅋㅋ\n간단한 dfs문제이다.\n하필이면 kdt시간에 푸느라 엄청 안풀렸는데, 집에서푸니 10분컷이였다.\n주의할 점은 단 하나이다.\n뱀과 사다리는 무조건 타야 하므로\n조건문을 제대로 설정해서, 타지 않은 경우를 큐에 넣지 않도록 주의하자\n","title":"백준 16928 뱀과 사다리 게임","type":"posts"},{"content":"https://www.acmicpc.net/problem/1987\n1987번: 알파벳세로 R칸, 가로 C칸으로 된 표 모양의 보드가 있다. 보드의 각 칸에는 대문자 알파벳이 하나씩 적혀 있고, 좌측 상단 칸 (1행 1열) 에는 말이 놓여 있다. 말은 상하좌우로 인접한 네 칸 중의 한 칸으www.acmicpc.net\n처음에 온갖 똥고쇼를 해도 시간초과를 피할 수 없었다.\n그나마 줄일대로줄여서 pypy로 간신히 맞았다.\nimport sysinput=sys.stdin.readlinemove=((-1,0),(1,0),(0,-1),(0,1))answer=1 def dfs(next,value): global answer answer=max(answer,value) i,j=next for a,b in move: ti,tj=i+a,j+b if 0\u0026lt;=ti\u0026lt;r and 0\u0026lt;=tj\u0026lt;c and arr[ti][tj] not in visited : visited.add(arr[ti][tj]) dfs((ti,tj),value+1) visited.remove(arr[ti][tj]) r,c=map(int,input().split()) arr=[list(input().strip()) for _ in range(r)]visited=set(arr[0][0])dfs((0,0),1) print(answer) 그후 다른사람들의 풀이를 보니 bfs로 푸는 방법이 있었다.\nimport sysinput=sys.stdin.readlinedef bfs(i=0,j=0): global answer q=set([(i,j,arr[i][j])]) while q: i,j,ans=q.pop() for a,b in move: ti,tj=i+a,j+b if 0\u0026lt;=ti\u0026lt;r and 0\u0026lt;=tj\u0026lt;c and arr[ti][tj] not in ans: q.add((ti,tj,ans+arr[ti][tj])) answer=max(answer,len(ans)+1) print(answer) move=((-1,0),(1,0),(0,-1),(0,1))answer=1r,c=map(int,input().split()) arr=[list(input().strip())for _ in range(r)] bfs() 솔직히 아직 잘 이해 못하겠지만\n대충 set으로 중복을 방지하고, bfs를 진행하며, ans가 최대점인 값을 찾는다.\n다음에다시풀어봐야겠다.\n","date":"15 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/104-%EB%B0%B1%EC%A4%80-1987-%EC%95%8C%ED%8C%8C%EB%B2%B3/","section":"Posts","summary":"https://www.acmicpc.net/problem/1987\n1987번: 알파벳세로 R칸, 가로 C칸으로 된 표 모양의 보드가 있다. 보드의 각 칸에는 대문자 알파벳이 하나씩 적혀 있고, 좌측 상단 칸 (1행 1열) 에는 말이 놓여 있다. 말은 상하좌우로 인접한 네 칸 중의 한 칸으www.acmicpc.net\n처음에 온갖 똥고쇼를 해도 시간초과를 피할 수 없었다.\n그나마 줄일대로줄여서 pypy로 간신히 맞았다.\nimport sysinput=sys.stdin.readlinemove=((-1,0),(1,0),(0,-1),(0,1))answer=1 def dfs(next,value): global answer answer=max(answer,value) i,j=next for a,b in move: ti,tj=i+a,j+b if 0\u003c=ti\u003cr and 0\u003c=tj\u003cc and arr[ti][tj] not in visited : visited.add(arr[ti][tj]) dfs((ti,tj),value+1) visited.remove(arr[ti][tj]) r,c=map(int,input().split()) arr=[list(input().strip()) for _ in range(r)]visited=set(arr[0][0])dfs((0,0),1) print(answer) 그후 다른사람들의 풀이를 보니 bfs로 푸는 방법이 있었다.\n","title":"백준 1987 알파벳","type":"posts"},{"content":"https://www.acmicpc.net/problem/1339\n1339번: 단어 수학첫째 줄에 단어의 개수 N(1 ≤ N ≤ 10)이 주어진다. 둘째 줄부터 N개의 줄에 단어가 한 줄에 하나씩 주어진다. 단어는 알파벳 대문자로만 이루어져있다. 모든 단어에 포함되어 있는 알파벳은 최대www.acmicpc.net\n그리디는 아이디어가 중요한 거 같다.\n맨처음에는 string.uppercase()를 이용해 dcit를만들었는데 그럴필요조차 없었다.\ndictionary에 알파벳마다의 중요도를 넣는다.\n각 단어에서 알파벳의자릿수**10을 더해준다.\n그리고 중요도가 높은 순서대로 9부터 곱하면서 더해준다.\nimport stringn=int(input())str_dict={}for _ in range(n): tmp=input() len_tmp=len(tmp) for i in range(len_tmp): if tmp[i] in str_dict: str_dict[tmp[i]]+=10**(len_tmp-i-1) else: str_dict[tmp[i]]=10**(len_tmp-i-1) tmp_list=sorted(str_dict.values(),reverse=True)answer=0 for i in range(len(tmp_list)-1,-1,-1): answer+=tmp_list[i]*(9-i)print(answer) 여기서 한가지 알게 된 사실이있다.\ncollections의 defaultdict를 이용하면, key가 없어도 에러가나는대신 default값(int는 0이다) 을 반환해준다.\n이를 이용하면, 굳이 귀찮게 key를 만들필요가 없다.\nfrom collections import defaultdictn=int(input())str_dict=defaultdict(int) for _ in range(n): tmp=input() len_tmp=len(tmp) for i in range(len_tmp): str_dict[tmp[i]]+=10**(len_tmp-i-1) tmp_list=sorted(str_dict.values(),reverse=True)answer=0 for i in range(len(tmp_list)-1,-1,-1): answer+=tmp_list[i]*(9-i)print(answer) ","date":"14 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/103-%EB%B0%B1%EC%A4%80-1339-%EB%8B%A8%EC%96%B4-%EC%88%98%ED%95%99/","section":"Posts","summary":"https://www.acmicpc.net/problem/1339\n1339번: 단어 수학첫째 줄에 단어의 개수 N(1 ≤ N ≤ 10)이 주어진다. 둘째 줄부터 N개의 줄에 단어가 한 줄에 하나씩 주어진다. 단어는 알파벳 대문자로만 이루어져있다. 모든 단어에 포함되어 있는 알파벳은 최대www.acmicpc.net\n그리디는 아이디어가 중요한 거 같다.\n맨처음에는 string.uppercase()를 이용해 dcit를만들었는데 그럴필요조차 없었다.\ndictionary에 알파벳마다의 중요도를 넣는다.\n각 단어에서 알파벳의자릿수**10을 더해준다.\n그리고 중요도가 높은 순서대로 9부터 곱하면서 더해준다.\nimport stringn=int(input())str_dict={}for _ in range(n): tmp=input() len_tmp=len(tmp) for i in range(len_tmp): if tmp[i] in str_dict: str_dict[tmp[i]]+=10**(len_tmp-i-1) else: str_dict[tmp[i]]=10**(len_tmp-i-1) tmp_list=sorted(str_dict.values(),reverse=True)answer=0 for i in range(len(tmp_list)-1,-1,-1): answer+=tmp_list[i]*(9-i)print(answer) 여기서 한가지 알게 된 사실이있다.\n","title":"백준 1339 단어 수학","type":"posts"},{"content":"https://www.acmicpc.net/problem/15686\n15686번: 치킨 배달크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸www.acmicpc.net\n처음에 bfs를생각했는데 더 간단한 풀이가 있었다.\n모든 집과 치킨집을 구해놓고,\n치킨집에 대한 원소가m개인 combination을 구하고,\n각 combination에 대하여 치킨 거리를 구해 비교한다.\nimport sysfrom itertools import combinationsinput=sys.stdin.readline n,m=map(int,input().split())arr=[]chickenhouse=[]blank=[]for i in range(n): tmp=list(map(int,input().split())) for j in range(n): if tmp[j]==1: blank.append((i,j)) elif tmp[j]==2: chickenhouse.append((i,j))chicken_comb=combinations(chickenhouse,m) answer=1e+7for chickens in chicken_comb: dist=0 for i,j in blank: dist+=min([abs(i-c[0])+abs(j-c[1]) for c in chickens]) if dist\u0026gt;=answer:break answer=min(answer,dist)print(answer) ","date":"13 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/102-%EB%B0%B1%EC%A4%80-15686%EC%B9%98%ED%82%A8%EB%B0%B0%EB%8B%AC/","section":"Posts","summary":"https://www.acmicpc.net/problem/15686\n15686번: 치킨 배달크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸www.acmicpc.net\n처음에 bfs를생각했는데 더 간단한 풀이가 있었다.\n모든 집과 치킨집을 구해놓고,\n치킨집에 대한 원소가m개인 combination을 구하고,\n각 combination에 대하여 치킨 거리를 구해 비교한다.\nimport sysfrom itertools import combinationsinput=sys.stdin.readline n,m=map(int,input().split())arr=[]chickenhouse=[]blank=[]for i in range(n): tmp=list(map(int,input().split())) for j in range(n): if tmp[j]==1: blank.append((i,j)) elif tmp[j]==2: chickenhouse.append((i,j))chicken_comb=combinations(chickenhouse,m) answer=1e+7for chickens in chicken_comb: dist=0 for i,j in blank: dist+=min([abs(i-c[0])+abs(j-c[1]) for c in chickens]) if dist\u003e=answer:break answer=min(answer,dist)print(answer)","title":"백준 15686치킨배달","type":"posts"},{"content":"https://www.acmicpc.net/problem/14502\n14502번: 연구소인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크www.acmicpc.net\n브루트포스\n모든 blank에 대하여, 길이가 3인 comb를만들고,\n각각의 경우에대해 bfs를수행하여 0이 최대인값을 구한다.\n지금까지 copy가 만능인줄알았는데, 아니였다.\ncopy를 쓰면, 그 객체 자체의 주소값은은 다르지만,\n다차원배열인경우, 그 안의 배열의 주소값은 같다.\n#ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 왜일케만들었지\n따라서, copy모듈의 decopy()를 사용하여 깊은복사를 해야만, 독립적인 arr을 유지할 수 있다.\n이것때문에 한참고민했다.\nimport sysfrom collections import dequefrom itertools import combinations import copy ##!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!input=sys.stdin.readline move=[[-1,0],[1,0],[0,-1],[0,1]]answer=0def bfs(walls): global answer tmp_arr=copy.deepcopy(arr) !!!!!!!!!!!!!!!!!!!!!!!!! tmp_answer=len(blank_list) for i,j in walls: tmp_arr[i][j]=1 tmp_answer-=1 que=deque() que.extend(virus_list) while que: i,j=que.popleft() for a,b in move: tmp_i,tmp_j=i+a,j+b if 0\u0026lt;=tmp_i\u0026lt;n and 0\u0026lt;=tmp_j\u0026lt;m and tmp_arr[tmp_i][tmp_j]==0: tmp_arr[tmp_i][tmp_j]=2 tmp_answer-=1 que.append((tmp_i,tmp_j)) answer=max(answer,tmp_answer) n,m=map(int,input().split())arr=[]virus_list=[]blank_list=[] for i in range(n): tmp=[* map(int,input().split())] arr.append(tmp) for j in range(len(tmp)): if tmp[j]==2: virus_list.append((i,j)) elif tmp[j]==0: blank_list.append((i,j))wall_comb=list(combinations(blank_list,3)) for walls in wall_comb: bfs(walls)print(answer) ","date":"12 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/101-%EB%B0%B1%EC%A4%80-14502-%EC%97%B0%EA%B5%AC%EC%86%8C/","section":"Posts","summary":"https://www.acmicpc.net/problem/14502\n14502번: 연구소인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크www.acmicpc.net\n브루트포스\n모든 blank에 대하여, 길이가 3인 comb를만들고,\n각각의 경우에대해 bfs를수행하여 0이 최대인값을 구한다.\n지금까지 copy가 만능인줄알았는데, 아니였다.\ncopy를 쓰면, 그 객체 자체의 주소값은은 다르지만,\n다차원배열인경우, 그 안의 배열의 주소값은 같다.\n#ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 왜일케만들었지\n따라서, copy모듈의 decopy()를 사용하여 깊은복사를 해야만, 독립적인 arr을 유지할 수 있다.\n이것때문에 한참고민했다.\nimport sysfrom collections import dequefrom itertools import combinations import copy ##!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!input=sys.stdin.readline move=[[-1,0],[1,0],[0,-1],[0,1]]answer=0def bfs(walls): global answer tmp_arr=copy.deepcopy(arr) !!!!!!!!!!!!!!!!!!!!!!!!! tmp_answer=len(blank_list) for i,j in walls: tmp_arr[i][j]=1 tmp_answer-=1 que=deque() que.extend(virus_list) while que: i,j=que.popleft() for a,b in move: tmp_i,tmp_j=i+a,j+b if 0\u003c=tmp_i\u003cn and 0\u003c=tmp_j\u003cm and tmp_arr[tmp_i][tmp_j]==0: tmp_arr[tmp_i][tmp_j]=2 tmp_answer-=1 que.append((tmp_i,tmp_j)) answer=max(answer,tmp_answer) n,m=map(int,input().split())arr=[]virus_list=[]blank_list=[] for i in range(n): tmp=[* map(int,input().split())] arr.append(tmp) for j in range(len(tmp)): if tmp[j]==2: virus_list.append((i,j)) elif tmp[j]==0: blank_list.append((i,j))wall_comb=list(combinations(blank_list,3)) for walls in wall_comb: bfs(walls)print(answer)","title":"백준 14502 연구소","type":"posts"},{"content":"from collections import deque#f=총 층, g=스타트링크의 층, s=내가있는층, u=u층위로, d=d층아래로 f,s,g,u,d=map(int,input().split())arr=[-1 for _ in range(f+1)]que=deque([s]) arr[s]=0if s==g: print(0) exit()while que: now=que.popleft() for next in (now+u,now-d): if 0\u0026lt;next\u0026lt;=f and arr[next]==-1: arr[next]=arr[now]+1 que.append(next) if next==g: print(arr[next]) exit() print(\u0026#34;use the stairs\u0026#34;) https://www.acmicpc.net/problem/5014\n5014번: 스타트링크첫째 줄에 F, S, G, U, D가 주어진다. (1 ≤ S, G ≤ F ≤ 1000000, 0 ≤ U, D ≤ 1000000) 건물은 1층부터 시작하고, 가장 높은 층은 F층이다.www.acmicpc.net\n간단한 dfs문제\n처음에 틀렸는데, 내가있는층==가고싶은층일때를 제외했기 때문\n설명 x\n","date":"12 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/100-%EB%B0%B1%EC%A4%80-5014-%EC%8A%A4%ED%83%80%ED%8A%B8%EB%A7%81%ED%81%AC/","section":"Posts","summary":"from collections import deque#f=총 층, g=스타트링크의 층, s=내가있는층, u=u층위로, d=d층아래로 f,s,g,u,d=map(int,input().split())arr=[-1 for _ in range(f+1)]que=deque([s]) arr[s]=0if s==g: print(0) exit()while que: now=que.popleft() for next in (now+u,now-d): if 0\u003cnext\u003c=f and arr[next]==-1: arr[next]=arr[now]+1 que.append(next) if next==g: print(arr[next]) exit() print(\"use the stairs\") https://www.acmicpc.net/problem/5014\n5014번: 스타트링크첫째 줄에 F, S, G, U, D가 주어진다. (1 ≤ S, G ≤ F ≤ 1000000, 0 ≤ U, D ≤ 1000000) 건물은 1층부터 시작하고, 가장 높은 층은 F층이다.www.acmicpc.net\n간단한 dfs문제\n처음에 틀렸는데, 내가있는층==가고싶은층일때를 제외했기 때문\n설명 x\n","title":"백준 5014 스타트링크","type":"posts"},{"content":"https://www.acmicpc.net/problem/1967\n1967번: 트리의 지름파일의 첫 번째 줄은 노드의 개수 n(1 ≤ n ≤ 10,000)이다. 둘째 줄부터 n-1개의 줄에 각 간선에 대한 정보가 들어온다. 간선에 대한 정보는 세 개의 정수로 이루어져 있다. 첫 번째 정수는 간선이 연www.acmicpc.net\n1167번 문제와 똑같은 개념\n이번엔 입력이 한방향만 주어지므로, 둘다 업데이트시켜줘야됨\n##필수개념##\n트리의 한 점에서 가장 먼 점은, 항상 지름 중 한 점이다!! https://fuckingcomputer.tistory.com/97 참고\n##코드##\nimport sysfrom collections import dequeinput=sys.stdin.readlinedef dfs(start): one=1 long_d=0 visited=[-1 for _ in range(n+1)] que=deque([start]) visited[start]=0 while que: now=que.popleft() for next,weight in tree[now]: if visited[next]==-1: visited[next]=visited[now]+weight que.append(next) if visited[next]\u0026gt;long_d: long_d=visited[next] one=next return one,long_d n=int(input())tree=[[]for _ in range(n+1)]#트리 채우기 for _ in range(n-1): a,b,c=map(int,input().split()) tree[a].append((b,c)) tree[b].append((a,c)) one,tresh=dfs(1) #지름중하나, 쓰레기값(거리 print(dfs(one)[1]) #거리만 출력! ","date":"12 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/099-%EB%B0%B1%EC%A4%80-1967-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EC%A7%80%EB%A6%84/","section":"Posts","summary":"https://www.acmicpc.net/problem/1967\n1967번: 트리의 지름파일의 첫 번째 줄은 노드의 개수 n(1 ≤ n ≤ 10,000)이다. 둘째 줄부터 n-1개의 줄에 각 간선에 대한 정보가 들어온다. 간선에 대한 정보는 세 개의 정수로 이루어져 있다. 첫 번째 정수는 간선이 연www.acmicpc.net\n1167번 문제와 똑같은 개념\n이번엔 입력이 한방향만 주어지므로, 둘다 업데이트시켜줘야됨\n##필수개념##\n트리의 한 점에서 가장 먼 점은, 항상 지름 중 한 점이다!! https://fuckingcomputer.tistory.com/97 참고\n##코드##\nimport sysfrom collections import dequeinput=sys.stdin.readlinedef dfs(start): one=1 long_d=0 visited=[-1 for _ in range(n+1)] que=deque([start]) visited[start]=0 while que: now=que.popleft() for next,weight in tree[now]: if visited[next]==-1: visited[next]=visited[now]+weight que.append(next) if visited[next]\u003elong_d: long_d=visited[next] one=next return one,long_d n=int(input())tree=[[]for _ in range(n+1)]#트리 채우기 for _ in range(n-1): a,b,c=map(int,input().split()) tree[a].append((b,c)) tree[b].append((a,c)) one,tresh=dfs(1) #지름중하나, 쓰레기값(거리 print(dfs(one)[1]) #거리만 출력!","title":"백준 1967 트리의 지름","type":"posts"},{"content":"https://www.acmicpc.net/problem/11404\n11404번: 플로이드첫째 줄에 도시의 개수 n이 주어지고 둘째 줄에는 버스의 개수 m이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가www.acmicpc.net\n내 기억상 유튜브에서 보고 처음으로 감탄사를 내뱉었던 알고리즘\n우리는 n*n 배열을 만들거다.\n배열 i,j는 i에서 j로가는 최소 경로이다.\n직접가는 버스가 있으면 가중치를, 없으면 존나큰값을 넣는다.\nn에 대한 3중 반복문을 돌면서,\narr[i][j]가arr[i][k]+arr[k][j] 즉 직접가는 경로보다 k지점을 거쳐서 가는 경로가 빠르다면,\n더 빠른 경로를 업데이트해준다.\n이게다다.\n##플로이드알고리즘!!!import sysinput=sys.stdin.readlinen=int(input())bus=int(input()) ##arr[i][j]는 i에서j로 가는 최소비용 arr=[[0 if i==j else 1e+8 for i in range(n)]for j in range(n)]# 0은 제외니 -1해주기 for _ in range(bus): a,b,c=map(int,input().split()) a-=1 b-=1 arr[a][b]=min(arr[a][b],c)#여기까지 arr완성#플로이드알고리즘for k in range(n): for i in range(n): for j in range(n): if i==j:continue if arr[i][j]\u0026gt;arr[i][k]+arr[k][j]: arr[i][j]=arr[i][k]+arr[k][j]for i in range(n): for j in range(n): if arr[i][j]==1e+8: print(0,end=\u0026#39; \u0026#39;) else: print(arr[i][j],end=\u0026#39; \u0026#39;) print() p.s\n알고리즘에 대해서 한참 고민했던것이, i,j가 아직 업데이트되지 않았을때의 경로가 최소경로일 경우를 못구할 것 같았다.\n하지만, 쓸데없는 걱정이였다.\n배열에는 이미 이전 모든k를 거쳤을때의 최솟값이 업데이트되어있을것이다.\nk를 두번 거칠 일은 절대 없으므로(상식상)\n반복문이종료된 후에는 모든 값이 최소경로를 나타내게 된다!!\n","date":"12 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/098-%EB%B0%B1%EC%A4%80-11404%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%93%9C/","section":"Posts","summary":"https://www.acmicpc.net/problem/11404\n11404번: 플로이드첫째 줄에 도시의 개수 n이 주어지고 둘째 줄에는 버스의 개수 m이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가www.acmicpc.net\n내 기억상 유튜브에서 보고 처음으로 감탄사를 내뱉었던 알고리즘\n우리는 n*n 배열을 만들거다.\n배열 i,j는 i에서 j로가는 최소 경로이다.\n직접가는 버스가 있으면 가중치를, 없으면 존나큰값을 넣는다.\nn에 대한 3중 반복문을 돌면서,\narr[i][j]가arr[i][k]+arr[k][j] 즉 직접가는 경로보다 k지점을 거쳐서 가는 경로가 빠르다면,\n더 빠른 경로를 업데이트해준다.\n","title":"백준 11404플로이드","type":"posts"},{"content":"https://www.acmicpc.net/problem/1167\n1167번: 트리의 지름트리가 입력으로 주어진다. 먼저 첫 번째 줄에서는 트리의 정점의 개수 V가 주어지고 (2 ≤ V ≤ 100,000)둘째 줄부터 V개의 줄에 걸쳐 간선의 정보가 다음과 같이 주어진다. 정점 번호는 1부터 V까지www.acmicpc.net\n개념도 ㅈ같고 구현도 ㅈ같았던 문제\n가장 먼져 든 생각은, 트리의 모든 정점에서 모든 정점까지의 길이를 탐색하는거였는데, 말도안되서 포기했다.\n가장 중요한건 트리의 개념이다.\n트리는, 어떠한 두 노드를 선택해도, 경로는 항상 하나이다.\n따라서, 임이의 한 점에서 가장 먼 정점은, 트리의 지름(가장 먼 정점) 의 두 정점 중 하나이다.\n더욱 자세한 설명 참고 https://blog.myungwoo.kr/112 \u0026lt;- 진짜 천재인듯\n따라서 한 점에서 (나는 1로 함) 최대거리인 정점 one을 구한다. (이왕 구하는 김에 최대거리까지 구한다.)(bfs)\n그리고 bfs를 재사용하여 그 한 점에서 최대거리인 정점(이건 필요x)과 최대거리를 구한다.\n이 최대거리가 트리의 지름이다.\n심지어 그래프 입력하는것까지 ㅈ같은 문제였다. 치가떨린다.\n#구현하기 어지럽다 ㅅㅂ#트리에서 임의의 한 점에서의 최대의 길이는, 항상 지름 중 하나이다!import sys from collections import dequeinput=sys.stdin.readlinedef bfs(start): one=0 max_val=0 visited=[-1 for _ in range(v+1)] visited[start]=0 que=deque([start]) while que: now=que.popleft() for next,weight in arr[now]: if visited[next]==-1: visited[next]=visited[now]+weight if visited[next]\u0026gt;max_val: one=next max_val=visited[next] que.append(next) return one,max_val #(지름의 정점중 1, \u0026lt;-까지의 최댓값 ) v=int(input())arr=[[]for _ in range(v+1)]for _ in range(v): tmp=list(map(int,input().strip().split())) current=tmp[0] for i in range(1,len(tmp)-1,2): arr[current].append([tmp[i],tmp[i+1]]) #어지럽다진짜one,max_val=bfs(1) print(bfs(one)[1]) ","date":"12 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/097-%EB%B0%B1%EC%A4%80-1167-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EC%A7%80%EB%A6%84/","section":"Posts","summary":"https://www.acmicpc.net/problem/1167\n1167번: 트리의 지름트리가 입력으로 주어진다. 먼저 첫 번째 줄에서는 트리의 정점의 개수 V가 주어지고 (2 ≤ V ≤ 100,000)둘째 줄부터 V개의 줄에 걸쳐 간선의 정보가 다음과 같이 주어진다. 정점 번호는 1부터 V까지www.acmicpc.net\n개념도 ㅈ같고 구현도 ㅈ같았던 문제\n가장 먼져 든 생각은, 트리의 모든 정점에서 모든 정점까지의 길이를 탐색하는거였는데, 말도안되서 포기했다.\n가장 중요한건 트리의 개념이다.\n트리는, 어떠한 두 노드를 선택해도, 경로는 항상 하나이다.\n따라서, 임이의 한 점에서 가장 먼 정점은, 트리의 지름(가장 먼 정점) 의 두 정점 중 하나이다.\n","title":"백준 1167 트리의 지름","type":"posts"},{"content":"https://www.acmicpc.net/problem/11725\n11725번: 트리의 부모 찾기루트 없는 트리가 주어진다. 이때, 트리의 루트를 1이라고 정했을 때, 각 노드의 부모를 구하는 프로그램을 작성하시오.www.acmicpc.net\n1을 시작으로 dfs든 bfs든 하면서 parent를 업데이트해준다.\n#dfs\nimport syssys.setrecursionlimit(10**6)input=sys.stdin.readlinedef dfs(node=1): for next_node in arr[node]: if parent[next_node]==-1: parent[next_node]=node dfs(next_node)n=int(input()) parent=[-1 for _ in range(n+1)]arr=[[]for _ in range(n+1)]for _ in range(n-1): a,b=map(int,input().split()) arr[a].append(b) arr[b].append(a)dfs() for p in parent[2:]: print(p) #bfs\nimport sysfrom collections import dequeinput=sys.stdin.readlinedef bfs(root=1): que=deque([root]) while que: node=que.popleft() for next_node in arr[node]: if parent[next_node]==-1: parent[next_node]=node que.append(next_node) n=int(input())parent=[-1 for _ in range(n+1)]arr=[[]for _ in range(n+1)] for _ in range(n-1): a,b=map(int,input().split()) arr[a].append(b) arr[b].append(a)bfs()for p in parent[2:]: print(p) ","date":"11 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/096-%EB%B0%B1%EC%A4%80-11725-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EB%B6%80%EB%AA%A8-%EC%B0%BE%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/11725\n11725번: 트리의 부모 찾기루트 없는 트리가 주어진다. 이때, 트리의 루트를 1이라고 정했을 때, 각 노드의 부모를 구하는 프로그램을 작성하시오.www.acmicpc.net\n1을 시작으로 dfs든 bfs든 하면서 parent를 업데이트해준다.\n#dfs\nimport syssys.setrecursionlimit(10**6)input=sys.stdin.readlinedef dfs(node=1): for next_node in arr[node]: if parent[next_node]==-1: parent[next_node]=node dfs(next_node)n=int(input()) parent=[-1 for _ in range(n+1)]arr=[[]for _ in range(n+1)]for _ in range(n-1): a,b=map(int,input().split()) arr[a].append(b) arr[b].append(a)dfs() for p in parent[2:]: print(p) #bfs\nimport sysfrom collections import dequeinput=sys.stdin.readlinedef bfs(root=1): que=deque([root]) while que: node=que.popleft() for next_node in arr[node]: if parent[next_node]==-1: parent[next_node]=node que.append(next_node) n=int(input())parent=[-1 for _ in range(n+1)]arr=[[]for _ in range(n+1)] for _ in range(n-1): a,b=map(int,input().split()) arr[a].append(b) arr[b].append(a)bfs()for p in parent[2:]: print(p)","title":"백준 11725 트리의 부모 찾기","type":"posts"},{"content":"https://www.acmicpc.net/problem/2250\n2250번: 트리의 높이와 너비첫째 줄에 노드의 개수를 나타내는 정수 N(1 ≤ N ≤ 10,000)이 주어진다. 다음 N개의 줄에는 각 줄마다 노드 번호와 해당 노드의 왼쪽 자식 노드와 오른쪽 자식 노드의 번호가 순서대로 주어진다.www.acmicpc.net\n와 진짜 나한테는 개어렵다.\n다른사람의 풀이를 참고했다.\n먼져, 정보를 받아서 tree를 구성한다.\nroot가주어지지 않기에, 각각의 트리에 parent를 -1로 초기화시켜놓고,\n각각의 자식노드들의 parent를 업데이트한다.\n이후 트리에서 parent가 -1인 노드가 root이다.\n루트에서 중위순회를 시작한다. 이때 레벨정보를 넘겨줘야한다.\n레벨정보는 root일때 1이고, 한단계 들어갈때마다 1씩 증가시킨다.\n가장 먼저 실행하는 곳은, 가장 왼쪽에 있는 노드일것이다.\n전역변수인 position을 0으로 선언하고, 노드를 방문할때마다 1씩 더해준다.\nresult에 레벨정보에 따라서, 각 레벨의 position을 한데 묶는다. (dict)\n여기까지 끝났으면, 이제 답만 구하면 된다.\nresult의 레벨을 정렬하고, 낮은 레벨부터 최대-최소의 값을 구한다.\n같을 경우에는 레벨이 낮은게 우선이므로, 클 때만 레벨과 최대-최소를 업데이트해준다.\n이후 출력해준다.\n내가 트리구조를 첨 접해서인지 너무 어렵다진짜. 아이디어를 얻지않는 이상 혼자서 못풀겠다.\nimport sysinput=sys.stdin.readlineclass Node: def __init__(self,data,left,right,parent): self.data=data self.left=left self.right=right self.parent=parent position=0 #가장 왼쪽부터1씩증가시키면서번호매기기result={} #레벨에 따른 노드들위치 def inorder(node,level=1): #중위순회 global position if node.left !=-1: inorder(tree[node.left],level+1) position+=1 #가장왼쪽부터 증가 if level in result: result[level].append(position) else: result[level]=[position] if node.right != -1: inorder(tree[node.right],level+1) n=int(input())tree={} #각 노드의 자기자신,left,right,parent업데이트하기for _ in range(n): data,left,right=map(int,input().split()) tree[data]=Node(data,left,right,-1)for i in range(1,n+1): if tree[i].left != -1: tree[tree[i].left].parent=data if tree[i].right != -1: tree[tree[i].right].parent=data for i in tree.keys(): if tree[i].parent==-1: root=i break; inorder(tree[root])answer_idx=1answer=-19for i in sorted(result.keys()): tmp=max(result[i])-min(result[i]) if tmp\u0026gt;answer: answer_idx=i answer=tmpprint(answer_idx,answer+1) ","date":"10 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/095-%EB%B0%B1%EC%A4%80-2250-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EB%86%92%EC%9D%B4%EC%99%80-%EB%84%88%EB%B9%84/","section":"Posts","summary":"https://www.acmicpc.net/problem/2250\n2250번: 트리의 높이와 너비첫째 줄에 노드의 개수를 나타내는 정수 N(1 ≤ N ≤ 10,000)이 주어진다. 다음 N개의 줄에는 각 줄마다 노드 번호와 해당 노드의 왼쪽 자식 노드와 오른쪽 자식 노드의 번호가 순서대로 주어진다.www.acmicpc.net\n와 진짜 나한테는 개어렵다.\n다른사람의 풀이를 참고했다.\n먼져, 정보를 받아서 tree를 구성한다.\nroot가주어지지 않기에, 각각의 트리에 parent를 -1로 초기화시켜놓고,\n각각의 자식노드들의 parent를 업데이트한다.\n이후 트리에서 parent가 -1인 노드가 root이다.\n루트에서 중위순회를 시작한다. 이때 레벨정보를 넘겨줘야한다.\n레벨정보는 root일때 1이고, 한단계 들어갈때마다 1씩 증가시킨다.\n","title":"백준 2250 트리의 높이와 너비","type":"posts"},{"content":"https://www.acmicpc.net/problem/2580\n2580번: 스도쿠스도쿠는 18세기 스위스 수학자가 만든 \u0026lsquo;라틴 사각형\u0026rsquo;이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루www.acmicpc.net\n스도쿠를 받으면서, 공백의 위치만 따로 기억한다.\n이후, 첫번째 공백부터 1~9의 숫자로 채우며, 가로,세로,3*3사각형을 검사한다.\n이후 되는경우만 밀고나간다.\n##코드##\nimport sysinput=sys.stdin.readlinedef check(a,b,value): for i in range(9): #가로 if arr[a][i]==value: return False #세로 if arr[i][b]==value: return False #사각형 start_a=a//3*3 start_b=b//3*3 for i in range(start_a,start_a+3): for j in range(start_b,start_b+3): if arr[i][j]==value: return False return True def dfs(cnt=0): if cnt==len(blank): for ar in arr: print(* ar) exit() for i in range(1,10): a,b=blank[cnt] #세로,가로 if check(a,b,i): arr[a][b]=i dfs(cnt+1) arr[a][b]=0 returnarr=[] #스도쿠blank=[] for i in range(9): tmp=[*map(int,input().strip().split())] arr.append(tmp) for j in range(9): if tmp[j]==0: blank.append((i,j))dfs() 진짜 가면갈수록자괴감느낀다.\n이걸 도저히 이해못하겠는 복잡한 코드를 짜서 200ms대에 푸는 사람이 있다.\n봐도 이해를 못하겠다.\n","date":"9 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/094-%EB%B0%B1%EC%A4%80-2580-%EC%8A%A4%EB%8F%84%EC%BF%A0/","section":"Posts","summary":"https://www.acmicpc.net/problem/2580\n2580번: 스도쿠스도쿠는 18세기 스위스 수학자가 만든 ‘라틴 사각형’이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루www.acmicpc.net\n스도쿠를 받으면서, 공백의 위치만 따로 기억한다.\n이후, 첫번째 공백부터 1~9의 숫자로 채우며, 가로,세로,3*3사각형을 검사한다.\n이후 되는경우만 밀고나간다.\n##코드##\nimport sysinput=sys.stdin.readlinedef check(a,b,value): for i in range(9): #가로 if arr[a][i]==value: return False #세로 if arr[i][b]==value: return False #사각형 start_a=a//3*3 start_b=b//3*3 for i in range(start_a,start_a+3): for j in range(start_b,start_b+3): if arr[i][j]==value: return False return True def dfs(cnt=0): if cnt==len(blank): for ar in arr: print(* ar) exit() for i in range(1,10): a,b=blank[cnt] #세로,가로 if check(a,b,i): arr[a][b]=i dfs(cnt+1) arr[a][b]=0 returnarr=[] #스도쿠blank=[] for i in range(9): tmp=[*map(int,input().strip().split())] arr.append(tmp) for j in range(9): if tmp[j]==0: blank.append((i,j))dfs() 진짜 가면갈수록자괴감느낀다.\n","title":"백준 2580 스도쿠","type":"posts"},{"content":"https://www.acmicpc.net/problem/4179\n4179번: 불!입력의 첫째 줄에는 공백으로 구분된 두 정수 R과 C가 주어진다. 단, 1 ≤ R, C ≤ 1000 이다. R은 미로 행의 개수, C는 열의 개수이다. 다음 입력으로 R줄동안 각각의 미로 행이 주어진다. 각각의 문www.acmicpc.net\n처음부터 풀이기 보였다.\n모르는점은 불이랑 지훈이가 동시에도착하면 사냐 죽나였는데 죽는단다.\nmove 하나를 잘못입력해서 30여분 날렸다.\n동시에 도착하면 죽으므로, 모든 불의 위치를 먼져 deque에 넣는다. 이때, (i,j,-1) 마지막값을 -1로해준다\n그후 지훈이의 위치를 넣는다. 마지막 값에 시간을 넣을거므로 0으로 해준다.\n이후 bfs를진행하며, 지훈이의 x좌표가 0 또는 R 지훈이의 y좌표가 0또는 C이면, 시간을 계산해주고 종료한다\nor impossible\nmove를 잘 보자\nimport sysfrom collections import dequeinput=sys.stdin.readline move=((-1,0),(1,0),(0,-1),(0,1))def bfs(fire,jihun): que=deque() for f in fire: que.append(f) que.append(jihun) while que: i,j,t=que.popleft() for a,b in move: tmp_i,tmp_j=i+a,j+b if 0\u0026lt;=tmp_i\u0026lt;R and 0\u0026lt;=tmp_j\u0026lt;C and arr[tmp_i][tmp_j]==\u0026#39;.\u0026#39;: if t==-1: que.append((tmp_i,tmp_j,t)) arr[tmp_i][tmp_j]=\u0026#39;F\u0026#39; else: if tmp_i==R-1 or tmp_i==0 or tmp_j==C-1 or tmp_j==0 : # for k in arr: # print(k) print(t+2) exit() que.append((tmp_i,tmp_j,t+1)) arr[tmp_i][tmp_j]=\u0026#39;J\u0026#39; print(\u0026#34;IMPOSSIBLE\u0026#34;) R,C=map(int,input().split()) arr=[]for _ in range(R): arr.append(list(input().strip()))fire=[] for i in range(R): for j in range(C): if arr[i][j]==\u0026#39;J\u0026#39;: arr[i][j]=\u0026#39;@\u0026#39; jihun=((i,j,0)) if i==0 or j==0 or i==R-1 or j==C-1: print(1) exit() elif arr[i][j]==\u0026#39;F\u0026#39;: fire.append((i,j,-1)) bfs(fire,jihun) ","date":"7 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/093-%EB%B0%B1%EC%A4%804179%EB%B6%88/","section":"Posts","summary":"https://www.acmicpc.net/problem/4179\n4179번: 불!입력의 첫째 줄에는 공백으로 구분된 두 정수 R과 C가 주어진다. 단, 1 ≤ R, C ≤ 1000 이다. R은 미로 행의 개수, C는 열의 개수이다. 다음 입력으로 R줄동안 각각의 미로 행이 주어진다. 각각의 문www.acmicpc.net\n처음부터 풀이기 보였다.\n모르는점은 불이랑 지훈이가 동시에도착하면 사냐 죽나였는데 죽는단다.\nmove 하나를 잘못입력해서 30여분 날렸다.\n동시에 도착하면 죽으므로, 모든 불의 위치를 먼져 deque에 넣는다. 이때, (i,j,-1) 마지막값을 -1로해준다\n그후 지훈이의 위치를 넣는다. 마지막 값에 시간을 넣을거므로 0으로 해준다.\n","title":"백준4179불!","type":"posts"},{"content":"https://www.acmicpc.net/problem/2023\n2023번: 신기한 소수수빈이가 세상에서 가장 좋아하는 것은 소수이고, 취미는 소수를 가지고 노는 것이다. 요즘 수빈이가 가장 관심있어 하는 소수는 7331이다. 7331은 소수인데, 신기하게도 733도 소수이고, 73도 소수www.acmicpc.net\n와 이전에 풀때 포기했던 문젠데, 백트래킹을 해보고 왔더니 정말 쉬운 문제였다.\n먼져, 첫자리는 소수여야하므로 2,3,5,7이외의 수는 없다.\n2,3,5,7을 시작으로 하는 n자리의 숫자를 찾으면 되는데,\n한단계씩 지나갈때마다 그 단계의 신기한 소수판별을 하고, 아니면 더이상 탐색하지 않는다.\n신기한 소수를 판별할 때, 그 자리만 판별한다.\nex 길이가 3일때, 길이가 1,2인 경우는 앞에서 했으므로, 3인 경우만 판별하면 끝\n기분좋게푼 문제였다.\ndef check(strnum): num=int(strnum) if num%2==0: return False for i in range(3,int(num**0.5)+1,2): if num%i==0: return False return Truefirst=[\u0026#39;2\u0026#39;,\u0026#39;3\u0026#39;,\u0026#39;5\u0026#39;,\u0026#39;7\u0026#39;,] def dfs(strnum): # str if len(strnum)==n: print(strnum) return for i in (1,3,5,7,9): tmp=strnum+str(i) if check(tmp): dfs(tmp)n=int(input())for start in first: dfs(start) ","date":"5 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/092-%EB%B0%B1%EC%A4%80-2023-%EC%8B%A0%EA%B8%B0%ED%95%9C-%EC%86%8C%EC%88%98/","section":"Posts","summary":"https://www.acmicpc.net/problem/2023\n2023번: 신기한 소수수빈이가 세상에서 가장 좋아하는 것은 소수이고, 취미는 소수를 가지고 노는 것이다. 요즘 수빈이가 가장 관심있어 하는 소수는 7331이다. 7331은 소수인데, 신기하게도 733도 소수이고, 73도 소수www.acmicpc.net\n와 이전에 풀때 포기했던 문젠데, 백트래킹을 해보고 왔더니 정말 쉬운 문제였다.\n먼져, 첫자리는 소수여야하므로 2,3,5,7이외의 수는 없다.\n2,3,5,7을 시작으로 하는 n자리의 숫자를 찾으면 되는데,\n한단계씩 지나갈때마다 그 단계의 신기한 소수판별을 하고, 아니면 더이상 탐색하지 않는다.\n신기한 소수를 판별할 때, 그 자리만 판별한다.\n","title":"백준 2023 신기한 소수","type":"posts"},{"content":"https://www.acmicpc.net/problem/1991\n1991번: 트리 순회첫째 줄에는 이진 트리의 노드의 개수 N(1 ≤ N ≤ 26)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 노드와 그의 왼쪽 자식 노드, 오른쪽 자식 노드가 주어진다. 노드의 이름은 A부터 차례대로 알파www.acmicpc.net\n트리를 처음 접했다.\n유튜브에서 트리구조 강의를 보고 풀었다.\n처음으로 클래스를 사용했다.\n계속보다보니 신기하다.\n전위순회= 루트-왼쪽-오른쪽\n중위순회=왼쪽-루트-오른쪽\n후위순회=왼쪽-오른쪽-루트\nimport sysinput=sys.stdin.readlineclass Node(): def __init__(self,current_node,left_node,right_node): self.current=current_node self.left=left_node self.right=right_nodedef 전위순회(node): print(node.current,end=\u0026#39;\u0026#39;) if node.left !=\u0026#39;.\u0026#39;: 전위순회(tree[node.left]) if node.right !=\u0026#39;.\u0026#39;: 전위순회(tree[node.right])def 중위순회(node): if node.left !=\u0026#39;.\u0026#39;: 중위순회(tree[node.left]) print(node.current,end=\u0026#39;\u0026#39;) if node.right !=\u0026#39;.\u0026#39;: 중위순회(tree[node.right])def 후위순회(node): if node.left !=\u0026#39;.\u0026#39;: 후위순회(tree[node.left]) if node.right !=\u0026#39;.\u0026#39;: 후위순회(tree[node.right]) print(node.current,end=\u0026#39;\u0026#39;)n=int(input()) tree={}for _ in range(n): current_node,left_node,right_node=input().strip().split() tree[current_node]=Node(current_node,left_node,right_node)전위순회(tree[\u0026#34;A\u0026#34;]) print()중위순회(tree[\u0026#34;A\u0026#34;])print()후위순회(tree[\u0026#34;A\u0026#34;]) ","date":"4 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/091-%EB%B0%B1%EC%A4%80-1991-%ED%8A%B8%EB%A6%AC-%EC%88%9C%ED%9A%8C/","section":"Posts","summary":"https://www.acmicpc.net/problem/1991\n1991번: 트리 순회첫째 줄에는 이진 트리의 노드의 개수 N(1 ≤ N ≤ 26)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 노드와 그의 왼쪽 자식 노드, 오른쪽 자식 노드가 주어진다. 노드의 이름은 A부터 차례대로 알파www.acmicpc.net\n트리를 처음 접했다.\n유튜브에서 트리구조 강의를 보고 풀었다.\n처음으로 클래스를 사용했다.\n계속보다보니 신기하다.\n전위순회= 루트-왼쪽-오른쪽\n중위순회=왼쪽-루트-오른쪽\n후위순회=왼쪽-오른쪽-루트\nimport sysinput=sys.stdin.readlineclass Node(): def __init__(self,current_node,left_node,right_node): self.current=current_node self.left=left_node self.right=right_nodedef 전위순회(node): print(node.current,end='') if node.left !='.': 전위순회(tree[node.left]) if node.right !='.': 전위순회(tree[node.right])def 중위순회(node): if node.left !='.': 중위순회(tree[node.left]) print(node.current,end='') if node.right !='.': 중위순회(tree[node.right])def 후위순회(node): if node.left !='.': 후위순회(tree[node.left]) if node.right !='.': 후위순회(tree[node.right]) print(node.current,end='')n=int(input()) tree={}for _ in range(n): current_node,left_node,right_node=input().strip().split() tree[current_node]=Node(current_node,left_node,right_node)전위순회(tree[\"A\"]) print()중위순회(tree[\"A\"])print()후위순회(tree[\"A\"])","title":"백준 1991 트리 순회","type":"posts"},{"content":"https://www.acmicpc.net/problem/1261\n1261번: 알고스팟첫째 줄에 미로의 크기를 나타내는 가로 크기 M, 세로 크기 N (1 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 미로의 상태를 나타내는 숫자 0과 1이 주어진다. 0은 빈 방을 의미하고, 1은 벽을 의미www.acmicpc.net\n이래서 사람들이 비싼돈내고 알고리즘강의를 듣는구나 싶다.\n\u0026lsquo;가중치가 있는 경로찾기\u0026rsquo; 이다.\n벽을 지나갈때는 1의 가중치가, 아니면 가중치가 없다고 생각한다. 풀이는 bfs와 거의 같다.\n다른점은, 최대한 벽을 부수지 않아야되므로,\n1의 가중치가 있는점을 탐색하기 전에, 가중치가 없는 모든 노드를 탐색해야만 한다.\n내머리로는 도저희 몰라서 힌트를 얻었다.\n바로, 가중치가 0일때는 appendleft()를 해서 큐의 앞에 넣고, 1일때는 append()로 뒤에 넣는다.\n이러면, 어떻게던 가중치가 0인 노드들부터 탐색하게 된다. 이후에도 이를 반복하며 목표에 도달하면, 이는 가중치가 가장 적은 상태에서 목표에 도착하는 것이다.\nque자체에 부순 벽 갯수를 넣은 풀이\nimport sysfrom collections import dequeinput=sys.stdin.readline moves=[(-1,0),(1,0),(0,-1),(0,1)]def bfs(start_i=0,start_j=0): que=deque() que.append((start_i,start_j,0)) while que: i,j,wall=que.popleft() for a,b in moves: tmp_i,tmp_j=i+a,j+b if 0\u0026lt;=tmp_i\u0026lt;m and 0\u0026lt;=tmp_j\u0026lt;n and graph[tmp_i][tmp_j]!=-1: if tmp_i==m-1 and tmp_j==n-1: graph[tmp_i][tmp_j]=-1 print(wall) exit() elif graph[tmp_i][tmp_j]==\u0026#39;0\u0026#39;: graph[tmp_i][tmp_j]=-1 que.appendleft((tmp_i,tmp_j,wall)) elif graph[tmp_i][tmp_j]==\u0026#39;1\u0026#39;: graph[tmp_i][tmp_j]=-1 que.append((tmp_i,tmp_j,wall+1)) n,m=map(int,input().split())if n==m==1: print(0) exit() graph=[list(input().strip()) for _ in range(m)]bfs() dist[i][j]에 \u0026ldquo;[i][j]까지 도달하기에 최소한으로 부순 벽의 수\u0026rdquo; 를 저장한 풀이\nimport sysfrom collections import dequeinput=sys.stdin.readline moves=[(-1,0),(1,0),(0,-1),(0,1)]def bfs(start_i=0,start_j=0): que=deque() que.append((start_i,start_j)) while que: i,j=que.popleft() for a,b in moves: tmp_i,tmp_j=i+a,j+b if 0\u0026lt;=tmp_i\u0026lt;m and 0\u0026lt;=tmp_j\u0026lt;n and graph[tmp_i][tmp_j]!=-1: if tmp_i==m-1 and tmp_j==n-1: dist[tmp_i][tmp_j]=dist[i][j] return elif graph[tmp_i][tmp_j]==\u0026#39;0\u0026#39;: graph[tmp_i][tmp_j]=-1 dist[tmp_i][tmp_j]=dist[i][j] que.appendleft((tmp_i,tmp_j)) elif graph[tmp_i][tmp_j]==\u0026#39;1\u0026#39;: graph[tmp_i][tmp_j]=-1 dist[tmp_i][tmp_j]=dist[i][j]+1 que.append((tmp_i,tmp_j)) n,m=map(int,input().split()) dist=[[0 for _ in range(n)]for _ in range(m)] graph=[list(input().strip()) for _ in range(m)]bfs()print(dist[m-1][n-1]) ","date":"3 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/090-%EB%B0%B1%EC%A4%80-1261-%EC%95%8C%EA%B3%A0%EC%8A%A4%ED%8C%9F/","section":"Posts","summary":"https://www.acmicpc.net/problem/1261\n1261번: 알고스팟첫째 줄에 미로의 크기를 나타내는 가로 크기 M, 세로 크기 N (1 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 미로의 상태를 나타내는 숫자 0과 1이 주어진다. 0은 빈 방을 의미하고, 1은 벽을 의미www.acmicpc.net\n이래서 사람들이 비싼돈내고 알고리즘강의를 듣는구나 싶다.\n‘가중치가 있는 경로찾기’ 이다.\n벽을 지나갈때는 1의 가중치가, 아니면 가중치가 없다고 생각한다. 풀이는 bfs와 거의 같다.\n다른점은, 최대한 벽을 부수지 않아야되므로,\n1의 가중치가 있는점을 탐색하기 전에, 가중치가 없는 모든 노드를 탐색해야만 한다.\n","title":"백준 1261 알고스팟","type":"posts"},{"content":"https://www.acmicpc.net/problem/9663\n9663번: N-QueenN-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다. N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.www.acmicpc.net\n벽느낀다. ㅋㅋㅋㅋ\n처음에는 2차원배열을 통해 풀었는데, 시간초과가 났다.\n다른사람의 풀이를 보고 힌트를 얻었다.\ndp[i]=j 의 뜻은, i번째 줄의 j번째 칸에 퀸을 놓는다는 소리이다.\n모든 열에는 퀸이 하나씩 들어가야 하므로, 0열부터 n가지의 경우를 모두 탐색한다.\n이후 그 다음 열을 순차적으로 탐색하며, 안되는 경우\n(가로는 짜피 순차적으로하기때문에 겹치지 않으므로, 세로와 대각선만 생각)\n즉 이전 dp[i-1]의 값이 지금dp[i] 와 같거나, 대각선을 방지하기 위해 같은 기울기가 아니면\n(지금열-이전열)== |지금행-이전행| 이 True라는건 기울기가 같다는 뜻!!\n거기서부터의 경우를 베제한다.\n만일 깊이가 7인 곳에 도달했으면, answer에 1을 더해준다.\ndef check(depth): for i in range(depth): if dp[depth]==dp[i] or (depth-i==abs(dp[depth]-dp[i])): return False return Truedef dfs(depth): global answer if depth==n: answer+=1 return for i in range(n): dp[depth]=i if check(depth): dfs(depth+1) answer=0 n=int(input())# 어떻게 되든 한 줄에 2개이상 들어갈 수 없음 dp=[0 for _ in range(n)] #dp[i]=j i번째 줄에 j를 놓는다.dfs(0)print(answer) ","date":"2 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/089-%EB%B0%B1%EC%A4%80-9663-n-queen/","section":"Posts","summary":"https://www.acmicpc.net/problem/9663\n9663번: N-QueenN-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다. N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.www.acmicpc.net\n벽느낀다. ㅋㅋㅋㅋ\n처음에는 2차원배열을 통해 풀었는데, 시간초과가 났다.\n다른사람의 풀이를 보고 힌트를 얻었다.\ndp[i]=j 의 뜻은, i번째 줄의 j번째 칸에 퀸을 놓는다는 소리이다.\n모든 열에는 퀸이 하나씩 들어가야 하므로, 0열부터 n가지의 경우를 모두 탐색한다.\n이후 그 다음 열을 순차적으로 탐색하며, 안되는 경우\n","title":"백준 9663 n-queen","type":"posts"},{"content":"https://www.acmicpc.net/problem/2293\n2293번: 동전 1첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다.www.acmicpc.net\n처음에 ㅈ밥으로생각했는데, 예상외로 어려웠다.\n먼져 동전을 저장하고, dp배열을 생성한다.\n여기서 내가 이해가 안됬던 부분은, 사용한 동전의 구성이 같은데, 순서만 다른 것은 같은 경우이다. 라는 문장이다\n예로 동전이 1,2 가 있다면,\n3을만들수 있는 경우는 1+1+1 , 1+2, 2+1 ,3 총 4가지이다.\n여기서 중복되는 1+2,2+1중 하나를 제거하면 3가지가 나와야 한다.\n정답을 찾아봤는데, 이걸 어떻게 제거하는지 이해가 되지 않았다.\n답은 coin과 dp 2중 반복으로 만들어서 해결한다.\n앞서예시 3 (1,2)를 예로 들자면,\n먼져 coin의 1만 사용해서 만들 수 있는 경우를 구한다.\n1만으로 3까지의 숫자를 만드는 경우는 1+1+1 하나밖에 없다. [1,1,1,1]\n다음에는 2로 만드는 경우이다.\n2로 2를 만드는 경우의 수는 1이고, 3을 만드는 경우의 수는 1+2 1이다.\n그러므로 배열은 [1,1,2,2]가 된다.\n이 경우에, 1은 생각하지 않으므로, 2+1의 경우는 아예 계산을 하지 않게 되고, 1+2의 경우만 계산이 된다.\n참고로, dp[0]이 1인 이유는, 동전으로 dp[동전] 을 만드는 경우의 수는 항상 1이므로, 이를 계산하기 위해서이다.\n다음에 다시 풀어봐야겠다.\nimport sysinput=sys.stdin.readlinen,k=map(int,input().split()) #동전수 #목표 coins=[int(input())for _ in range(n)] #동전dp=[0 for _ in range(k+1)] dp[0]=1 #coin에 해당하는 값에 1을 더해줄 용도for coin in coins: for i in range(1,len(dp)): if i-coin\u0026gt;=0: dp[i]+=dp[i-coin]print(dp[k]) ","date":"1 May 2022","externalUrl":null,"permalink":"/posts/2022-tistory/088-%EB%B0%B1%EC%A4%80-2293-%EB%8F%99%EC%A0%841/","section":"Posts","summary":"https://www.acmicpc.net/problem/2293\n2293번: 동전 1첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다.www.acmicpc.net\n처음에 ㅈ밥으로생각했는데, 예상외로 어려웠다.\n먼져 동전을 저장하고, dp배열을 생성한다.\n여기서 내가 이해가 안됬던 부분은, 사용한 동전의 구성이 같은데, 순서만 다른 것은 같은 경우이다. 라는 문장이다\n예로 동전이 1,2 가 있다면,\n3을만들수 있는 경우는 1+1+1 , 1+2, 2+1 ,3 총 4가지이다.\n","title":"백준 2293 동전1","type":"posts"},{"content":"https://www.acmicpc.net/problem/14226\n14226번: 이모티콘영선이는 매우 기쁘기 때문에, 효빈이에게 스마일 이모티콘을 S개 보내려고 한다. 영선이는 이미 화면에 이모티콘 1개를 입력했다. 이제, 다음과 같은 3가지 연산만 사용해서 이모티콘을 S개 만www.acmicpc.net\n거의 정답보고풀었다.\n주의할점은 , 개수만 생각하면 안된다는 것이다.\n백준 질문계시판에서 본 글인데, 우리가 임의의 숫자까지 갈 수 있는 최적의 경로가, 정답까지의 최적의 경로라고는 할 수 없다.https://www.acmicpc.net/board/view/30100\n보고 머리가 띵했다.\n먼져 2차원배열의방식이 있다.\ndp[i][j]=i를만들때 클립보드가 j인 경우까지 드는 최소시간을 뜻한다.\n3가지 경우로 bfs를 진행하면 된다.\n#코드\nimport sysinput=sys.stdin.readlinefrom collections import dequen=int(input()) #dp=[i][j]=i를만들었을때, 클립보드가j일때의 횟수dp=[[-1 for _ in range(n*2)]for _ in range(n*2)] que=deque()que.append((1,0))dp[1][0]=0while que: tmp,clip=que.popleft() value=dp[tmp][clip] if tmp==n: print(value) exit() #1개 삭제하는 경우 if tmp\u0026gt;0 and dp[tmp-1][clip]==-1: dp[tmp-1][clip]=value+1 que.append((tmp-1,clip)) #클립보드에 복사하는 경우 if dp[tmp][tmp]==-1: dp[tmp][tmp]=value+1 que.append((tmp,tmp)) #클립보드에서 붙여넣기하는 경우 if tmp+clip\u0026lt;len(dp) and dp[tmp+clip][clip]==-1: dp[tmp+clip][clip]=value+1 que.append((tmp+clip,clip)) 더 좋은 방법이 있었다.\n중복을 허용하지 않기위해 2차원 배열을 만들었는데, 사실 dict로 중복값이 있는지도 판별할 수 있다.\n푸는 방법은 위와 거의 동일하다.\nimport sysinput=sys.stdin.readlinefrom collections import dequen=int(input()) visited={}que=deque()que.append((1,0))visited[(1,0)]=0while que: tmp,clip=que.popleft() #만일 정답에도달하면 출력 if tmp==n: print(visited[tmp,clip]) exit() #tmp, tmp이면, tmp인상태에서 클립보드에 복사한거 #따라서 복사하기전+1 if (tmp,tmp) not in visited.keys(): visited[(tmp,tmp)]=visited[(tmp,clip)]+1 que.append((tmp,tmp)) ## 만약 현재상태에서 붙여넣기한 상태를 방문하지 않았으면, ## 다음 상태는 현재 수,클립보드 +1 if (tmp+clip,clip) not in visited.keys(): visited[(tmp+clip,clip)]=visited[(tmp,clip)]+1 que.append((tmp+clip,clip)) if tmp\u0026gt;0 and(tmp-1,clip) not in visited.keys(): visited[(tmp-1,clip)]= visited[(tmp,clip)]+1 que.append((tmp-1,clip)) 난 아직도 내가 이해했는지도 모르겠다. 일단별표\n","date":"30 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/087-%EB%B0%B1%EC%A4%80-14226-%EC%9D%B4%EB%AA%A8%ED%8B%B0%EC%BD%98/","section":"Posts","summary":"https://www.acmicpc.net/problem/14226\n14226번: 이모티콘영선이는 매우 기쁘기 때문에, 효빈이에게 스마일 이모티콘을 S개 보내려고 한다. 영선이는 이미 화면에 이모티콘 1개를 입력했다. 이제, 다음과 같은 3가지 연산만 사용해서 이모티콘을 S개 만www.acmicpc.net\n거의 정답보고풀었다.\n주의할점은 , 개수만 생각하면 안된다는 것이다.\n백준 질문계시판에서 본 글인데, 우리가 임의의 숫자까지 갈 수 있는 최적의 경로가, 정답까지의 최적의 경로라고는 할 수 없다.https://www.acmicpc.net/board/view/30100\n보고 머리가 띵했다.\n먼져 2차원배열의방식이 있다.\ndp[i][j]=i를만들때 클립보드가 j인 경우까지 드는 최소시간을 뜻한다.\n3가지 경우로 bfs를 진행하면 된다.\n","title":"백준 14226 이모티콘","type":"posts"},{"content":"https://www.acmicpc.net/problem/13913\n13913번: 숨바꼭질 4수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일www.acmicpc.net\ndp로생각했었는데, 사실 bfs였다.\n먼져, -1로 채워진 빈 배열을 만들고, 현재위치를 0으로 초기화시켜준다.\n이후 현재위치에서 x+1,x-1,x*2로 확장하며 bfs를 진행한다.\n마지막 경로를 알기 위해 배열을 하나 더 만들고, 이 배열에 현재위치의 전위치를 기억시켜준다.\n만일 k에 도달했으면, k까지의 시간을 출력하고,\n경로를 쭉 따라가면서 시작위치(-1)이 나올때까지 왔던길을 되짚는다.\n이후 뒤집고 출력하면 끝!!\n재밌는문제였다.\nfrom collections import dequedef bfs(n): que=deque() que.append(n) arr[n]=0 while que: now=que.popleft() for next in (now+1,now-1,now*2): if 0\u0026lt;=next\u0026lt;len(arr) and arr[next]==-1: arr[next]=arr[now]+1 last_visit[next]=now if next==k: return que.append((next)) n,k=map(int,input().split())if n==k: print(0) print(n) exit()arr=[-1 for _ in range(200001)] last_visit=arr.copy()bfs(n)print(arr[k])tmp=last_visit[k]answer_list=[k] while True: answer_list.append(tmp) tmp=last_visit[tmp] if tmp==-1: answer_list.reverse() breakprint(* answer_list) ","date":"29 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/086-%EB%B0%B1%EC%A4%80-13913-%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%884/","section":"Posts","summary":"https://www.acmicpc.net/problem/13913\n13913번: 숨바꼭질 4수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일www.acmicpc.net\ndp로생각했었는데, 사실 bfs였다.\n먼져, -1로 채워진 빈 배열을 만들고, 현재위치를 0으로 초기화시켜준다.\n이후 현재위치에서 x+1,x-1,x*2로 확장하며 bfs를 진행한다.\n마지막 경로를 알기 위해 배열을 하나 더 만들고, 이 배열에 현재위치의 전위치를 기억시켜준다.\n만일 k에 도달했으면, k까지의 시간을 출력하고,\n","title":"백준 13913 숨바꼭질4","type":"posts"},{"content":"https://www.acmicpc.net/problem/2146\n2146번: 다리 만들기여러 섬으로 이루어진 나라가 있다. 이 나라의 대통령은 섬을 잇는 다리를 만들겠다는 공약으로 인기몰이를 해 당선될 수 있었다. 하지만 막상 대통령에 취임하자, 다리를 놓는다는 것이 아깝다www.acmicpc.net\n어제 이걸못풀어서 접을까 생각했다.\n일단, 모든 섬을 구분한다. (bfs1)\n섬의 가장자리(edge)좌표를 모두 구한다.\nedge를 바탕으로 (bfs2) 를 진행하며, 바다를 그 섬으로 메워나간다\nbridge배열을 만들어서, 메운 위치에 1씩 +하며 메운 점 갯수를 센다.\n메워나가다가, 만일 자기 섬도 아니고 바다도 아닌 다른 섬을 만나면 이제 다리가 이어진것이다.\n그러면 bridge의 현위치+만난위치가 경로이다.\n여기서 한참을 헤멨는데,\n당연히 먼져 도달한 것이 최소경로라고 생각했다. 하지만\u0026hellip;\u0026hellip;\n5\n1 0 0 0 1\n0 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n0 1 0 0 1\n다음과같은 반례가 있다. 최소경로는 4,2 4,3 을 이은 2이지만,\n문제는 먼져 왼쪽위와 오른쪽위가 디큐에 들어가므로, 맨 아랫쪽보다 먼져 만나게 된다.\n그래서 answer과 만났을때의 합을 계속 비교하며, 최솟값을 찾는 식으로 풀었다.\n다시풀으라고하면 또 잊어버려서 헤멜거같다.\nimport sysinput=sys.stdin.readlinefrom collections import deque moves=[(-1,0),(1,0),(0,-1),(0,1)]def bfs1(i,j,num): #섬클러스터링 que=deque() arr[i][j]=num que.append((i,j)) while que: i,j=que.popleft() for a,b in moves: tmp_i,tmp_j=i+a,j+b if 0\u0026lt;=tmp_i\u0026lt;n and 0\u0026lt;=tmp_j\u0026lt;n : if arr[tmp_i][tmp_j]==1: arr[tmp_i][tmp_j]=num que.append((tmp_i,tmp_j)) elif arr[tmp_i][tmp_j]==0: edge.append((i,j,num,0)) returndef dfs2(): #다리놓기 global answer queque=deque(edge) while queque: i,j,num,cnt=queque.popleft() for a,b in moves: tmp_i,tmp_j=i+a,j+b if 0\u0026lt;=tmp_i\u0026lt;n and 0\u0026lt;=tmp_j\u0026lt;n: if arr[tmp_i][tmp_j]==0: arr[tmp_i][tmp_j]=num brigde[tmp_i][tmp_j]=cnt+1 queque.append((tmp_i,tmp_j,num,cnt+1)) elif arr[tmp_i][tmp_j]!=num: answer=min(answer,cnt+brigde[tmp_i][tmp_j]) answer=1e+10n=int(input()) arr=[ [* map (int,input().split())]for _ in range(n)] brigde=[[False for _ in range(n)]for _ in range(n)]num=2edge=[] for i in range(n): for j in range(n): if arr[i][j]==1: bfs1(i,j,num) num+=1 else: continue edge=list(set(edge))dfs2()print(answer) ","date":"29 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/085-%EB%B0%B1%EC%A4%80-2146-%EB%8B%A4%EB%A6%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/2146\n2146번: 다리 만들기여러 섬으로 이루어진 나라가 있다. 이 나라의 대통령은 섬을 잇는 다리를 만들겠다는 공약으로 인기몰이를 해 당선될 수 있었다. 하지만 막상 대통령에 취임하자, 다리를 놓는다는 것이 아깝다www.acmicpc.net\n어제 이걸못풀어서 접을까 생각했다.\n일단, 모든 섬을 구분한다. (bfs1)\n섬의 가장자리(edge)좌표를 모두 구한다.\nedge를 바탕으로 (bfs2) 를 진행하며, 바다를 그 섬으로 메워나간다\nbridge배열을 만들어서, 메운 위치에 1씩 +하며 메운 점 갯수를 센다.\n메워나가다가, 만일 자기 섬도 아니고 바다도 아닌 다른 섬을 만나면 이제 다리가 이어진것이다.\n","title":"백준 2146 다리 만들기","type":"posts"},{"content":"https://www.acmicpc.net/problem/16947\n16947번: 서울 지하철 2호선첫째 줄에 역의 개수 N(3 ≤ N ≤ 3,000)이 주어진다. 둘째 줄부터 N개의 줄에는 역과 역을 연결하는 구간의 정보가 주어진다. 같은 구간이 여러 번 주어지는 경우는 없고, 역은 1번부터 N번까지 번호www.acmicpc.net\n존나열심히풀었다. 결국맞았다.\n근데 기분이썩는다.\n내 풀이방식\n그래프를 계속 돌면서, 시작한 위치로 돌아오는 경우(싸이클인 경우)를 찾는다.\n찾으면서, 만일 cycle이 아니면 방문처리를 풀고, 맞으면 모두 방문처리한다.\n이후, answer을 만들고\n순환선에서 밖이랑 연결된 정점은 간선이 3 이상일 것이므로,\n내가 찾은 cycle을 돌면서, 간선이 3 이상인 정점을 찾는다.\n이 정점에서부턴 트리 구조일 것이므로, 절대 순환되지 않는다.\n내가 찾은 정점들로부터, bfs를 돌면서 각 역까지의 거리를 answer[역]에 저장한다.\n이러면 answer엔 순환선인곳엔 0, 순환선이 아닌곳엔 각 역까지의 거리가\n1번부터 끝번까지 차례대로 저장될 것이다.\nprint(* answer[1:])을 한다. (처음에 arr을 맞추기위해 일부러 0번 역을 넘었으므로\n그니깐 맞아서 행복했다.\n시간이 1200초가 걸렸다.\nimport sysinput=sys.stdin.readlinesys.setrecursionlimit(10**7) from collections import dequedef dfs(i,last_i,start): # 순환확인 #현재위치,이전위치,시작위치 global flag for j in arr[i]: if j==start and j!= last_i: flag=True return if not visited[j]: visited[j]=True dfs(j,i,start) if flag: return visited[j]=False return s=deque()def bfs(i): s.append((i,0)) while s: tmp,v=s.popleft() for i in arr[tmp]: if not visited[i]: visited[i]=True s.append((i,v+1)) answer[i]=v+1 n=int(input()) arr=[[]for _ in range(n+1)]visited=[False for _ in range(n+1)] visited1=[False for _ in range(n+1)]for _ in range(n): a,b=map(int,input().split()) arr[a].append(b) arr[b].append(a) #입력 flag=Falsefor i in range(1,n+1): visited[i]=True dfs(i,None,i) if flag: break else: visited[i]=Falseanswer=[0 for _ in range(n+1)] for i in range(1,n+1): if visited[i] and len(arr[i])\u0026gt;2: bfs(i) print(* answer[1:]) 이후, 다른사람들의 풀이를 보는데, 파이썬으로 80초에 푸는 사람이 있더라\n코드를 따라 쳐봤는데도, 이해가 안된다.\n\u0026#34;\u0026#34;\u0026#34;정점의 개수 == 간선의 개수 + 1 이면 트리 형태이다.트리 형태에서 간선이 한 개 추가되면 사이클은 단 한 개 존재한다. 사이클 찾기: 임의의 한 정점에서 dfs를 한다.방문한 정점들을 스택에 저장하고, 방문한 적이 있는 정점 X를 방문하게 될 경우 X가 pop될 때까지 현재 스택에 있는 요소들을 pop한 것이 사이클이다.\u0026#34;\u0026#34;\u0026#34;import sys sys.setrecursionlimit(int(5e3))def input(): return sys.stdin.readline().rstrip()def main(): n = int(input()) graph = [[] for _ in range(n + 1)] for edge in range(n): a, b = map(int, input().split()) graph[a].append(b) graph[b].append(a) visited = [False] * (n + 1) visit_stack = [] def find_cycle(curr_node: int, prev_node: int) -\u0026gt; int: if visited[curr_node]: return curr_node visited[curr_node] = True visit_stack.append(curr_node) for next_node in graph[curr_node]: if next_node != prev_node: cycle_end = find_cycle(next_node, curr_node) if cycle_end: return cycle_end visit_stack.pop() return 0 end_point = find_cycle(1, 0) cycle_lp = visit_stack.index(end_point) cycle_set = set(visit_stack[cycle_lp:]) dists = [-1] * (n + 1) def calc_dist(curr_node: int, prev_node: int, dist: int): dists[curr_node] = dist for next_node in graph[curr_node]: if next_node not in cycle_set and next_node != prev_node: calc_dist(next_node, curr_node, dist + 1) for node in cycle_set: calc_dist(node, 0, 0) print(*dists[1:]) if __name__ == \u0026#39;__main__\u0026#39;: main() ","date":"28 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/084-%EB%B0%B1%EC%A4%80-16947-%EC%84%9C%EC%9A%B8-%EC%A7%80%ED%95%98%EC%B2%A0-2%ED%98%B8%EC%84%A0-%EC%96%B8%EC%A0%A0%EA%B0%84%EC%88%98%EC%A0%95%EC%98%88%EC%A0%95/","section":"Posts","summary":"https://www.acmicpc.net/problem/16947\n16947번: 서울 지하철 2호선첫째 줄에 역의 개수 N(3 ≤ N ≤ 3,000)이 주어진다. 둘째 줄부터 N개의 줄에는 역과 역을 연결하는 구간의 정보가 주어진다. 같은 구간이 여러 번 주어지는 경우는 없고, 역은 1번부터 N번까지 번호www.acmicpc.net\n존나열심히풀었다. 결국맞았다.\n근데 기분이썩는다.\n내 풀이방식\n그래프를 계속 돌면서, 시작한 위치로 돌아오는 경우(싸이클인 경우)를 찾는다.\n찾으면서, 만일 cycle이 아니면 방문처리를 풀고, 맞으면 모두 방문처리한다.\n이후, answer을 만들고\n순환선에서 밖이랑 연결된 정점은 간선이 3 이상일 것이므로,\n","title":"백준 16947 서울 지하철 2호선 (언젠간수정예정)","type":"posts"},{"content":"https://www.acmicpc.net/problem/16929\n16929번: Two Dots첫째 줄에 게임판의 크기 N, M이 주어진다. 둘째 줄부터 N개의 줄에 게임판의 상태가 주어진다. 게임판은 모두 점으로 가득차 있고, 게임판의 상태는 점의 색을 의미한다. 점의 색은 알파벳 대문www.acmicpc.net\n사이클이 있는지를 확인하는 문제\n처음엔, 진짜 무식한 방식으로 접근했다.\narr의 모든 점에 대하여 dfs 탐색을 하는데, \u0026ldquo;깊이가 2 를 넘은 상태에서, 원래의 점과 만나는 경우\u0026rdquo; 를 성공조건으로 삼았다.\n답은 맞게 나왔는데, 시간이 100ms넘게 걸렸다.\n그 후, 다시 생각을 해보니,\n어느 점에서 dfs를 진행하던간에 만일 이미 방문한 점과 만난다면, 사이클이 존재하는 것이다.\n문제는 a-\u0026gt;b-\u0026gt;a 처럼 그냥 제자리를 도는 경우인데, 이를 해결하기 위하여 이전 dfs의 시작점을 함수에 넣고,\n만일 이 dfs함수의 다음 진행점이\n\u0026ldquo;같은 색깔이고, 방문했으며, arr의 크기를 벗어나지 않고, 이전 방문지점이 아니면\u0026rdquo; 성공이도록 했다.\n이러니 시간이 70초대까지 줄어들었다.\n여기서 굳이 방문한 위치를 다시 지워줄필요가 없다고 깨닫고, 배열에서 초기 시작점을 탐색할 때 방문하지 않은\n지점만 탐색했더니, 마지막으로 72초대가 나왔다.\n난진짜빡대가리인듯 ㅋㅋㅋㅋㅋㅋㅋ\n##코드(마지막)##\nimport sysinput =sys.stdin.readlinemoves=[(-1,0),(1,0),(0,-1),(0,1)] def dfs(i,j,c,last_i=None,last_j=None,depth=1): for a,b in moves: tmp_i=i+a tmp_j=j+b if depth\u0026gt;3: if 0\u0026lt;=tmp_i\u0026lt;n and 0\u0026lt;=tmp_j\u0026lt;m and arr[tmp_i][tmp_j]==c and visited[tmp_i][tmp_j] and (tmp_i != last_i or tmp_j !=last_j): print(\u0026#34;Yes\u0026#34;) exit() if 0\u0026lt;=tmp_i\u0026lt;n and 0\u0026lt;=tmp_j\u0026lt;m and c==arr[tmp_i][tmp_j] and not visited[tmp_i][tmp_j]: visited[tmp_i][tmp_j]=True dfs(tmp_i,tmp_j,c,last_i=i,last_j=j,depth= depth+1) returnn,m=map(int,input().split()) arr=[list(input().strip())for _ in range(n)] visited=[[False for _ in range(m)]for _ in range(n)]c_list=[]for i in range(n): for j in range(m): if not visited[i][j]: visited[i][j]=True dfs(i,j,c=arr[i][j]) print(\u0026#39;No\u0026#39;) ","date":"26 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/083-%EB%B2%A1%EC%A4%80-16929-two-dots/","section":"Posts","summary":"https://www.acmicpc.net/problem/16929\n16929번: Two Dots첫째 줄에 게임판의 크기 N, M이 주어진다. 둘째 줄부터 N개의 줄에 게임판의 상태가 주어진다. 게임판은 모두 점으로 가득차 있고, 게임판의 상태는 점의 색을 의미한다. 점의 색은 알파벳 대문www.acmicpc.net\n사이클이 있는지를 확인하는 문제\n처음엔, 진짜 무식한 방식으로 접근했다.\narr의 모든 점에 대하여 dfs 탐색을 하는데, “깊이가 2 를 넘은 상태에서, 원래의 점과 만나는 경우” 를 성공조건으로 삼았다.\n답은 맞게 나왔는데, 시간이 100ms넘게 걸렸다.\n그 후, 다시 생각을 해보니,\n","title":"벡준 16929 two dots","type":"posts"},{"content":"https://www.acmicpc.net/problem/14500\n14500번: 테트로미노폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변www.acmicpc.net\n머리가 나쁘면 손이 고생한다.\n1.가장 직관적인 풀이\n모든 경우의수를 다 벡터에 넣고, 이를 반복하면서 구한다.\n조금머리쓴거 잘 생각해보면 엿을(ㅗ) 을 제외한 모든 경우의 수는, 깊이가 4인 dfs이다.\n따라서 각 점에 대하여 깊이가 4인 dfs의 최댓값을 구하고, 총 4방향의 가능한 엿에 대해 최댓값을 구한다.\n조금더머리쓴거 (여기서부터는 다른사람코드를 참조했다) 좀 더 잘 생각해보면, 엿을 구하기 위해 따로 함수를 만들필요없이, dfs에 포함시킬 수 있다.\n깊이가 2인 dfs에 대하여, 다음 지점을 방문처리만 하고, 현 지점에서 dfs를 진행한다면\nex)\n깊이가 2인 상태\nColumn 1 Column 2 Column 3 0 0 0 1 1 (현위치) 0 0 0 0 방문처리만 하고 현위치에서 구함 (깊이 3)\nColumn 1 Column 2 Column 3 0 0 0 1 1(현위치) 1 (방문처리) 0 0 0 가능한경우\nColumn 1 Column 2 Column 3 0 0 (가능) 0 1 1(현위치) 1 (방문처리) 0 0 (가능) 0 모든 엿모양을 구할 수 있다.\n최적 3번에서 하나만 추가해주면 된다.\n3번을 파이썬으로 구현하면 시간초과가 나고, pypy로 구현하면 2400초 정도가 걸린다.\n이 시간을 줄이기 위해서 트릭을 쓴다.\n먼져, 받은 arr의 최댓값을 max_val을 구해놓는다.\n그리고, dfs를 수행하면서, v+(max_val * (4-depth)) 즉\n현재의 값에서 앞으로 남은 기회가 모두 최댓값이여도 현재의 정답보다 작을 경우에, 바로 return시킨다.\n이르면 200초대로 확 줄일 수 있다. 이런생각을 어케하는지 모르겠다.\n4번코드\nimport sysinput =sys.stdin.readlinemoves=[(-1,0),(1,0),(0,-1),(0,1)] def dfs(i,j,depth=1,v=0): global answer global max_val if answer \u0026gt;= v+max_val*(4-depth): return if depth==4: answer=max(answer,v) return for move in moves: tmp_i= i+move[0] tmp_j= j+move[1] if 0\u0026lt;=tmp_i\u0026lt;n and 0\u0026lt;=tmp_j\u0026lt;m and not visited[tmp_i][tmp_j]: if depth==2: #볼록모양 visited[tmp_i][tmp_j]=True dfs(i,j,depth+1,v+arr[tmp_i][tmp_j]) visited[tmp_i][tmp_j]=False visited[tmp_i][tmp_j]=True dfs(tmp_i,tmp_j,depth+1,v+arr[tmp_i][tmp_j]) visited[tmp_i][tmp_j]=False return answer=0n,m=map(int,input().split()) arr=[[* map(int,input().split())]for _ in range(n)]max_val=max(map(max,arr)) visited=[[False for _ in range(m)]for _ in range(n)]for i in range(n): for j in range(m): visited[i][j]=True dfs(i,j,1,v=arr[i][j]) visited[i][j]=Falseprint(answer) ","date":"25 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/082-%EB%B0%B1%EC%A4%80-14500-%ED%85%8C%ED%8A%B8%EB%A1%9C%EB%AF%B8%EB%85%B8/","section":"Posts","summary":"https://www.acmicpc.net/problem/14500\n14500번: 테트로미노폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변www.acmicpc.net\n머리가 나쁘면 손이 고생한다.\n1.가장 직관적인 풀이\n모든 경우의수를 다 벡터에 넣고, 이를 반복하면서 구한다.\n조금머리쓴거 잘 생각해보면 엿을(ㅗ) 을 제외한 모든 경우의 수는, 깊이가 4인 dfs이다.\n따라서 각 점에 대하여 깊이가 4인 dfs의 최댓값을 구하고, 총 4방향의 가능한 엿에 대해 최댓값을 구한다.\n","title":"백준 14500 테트로미노","type":"posts"},{"content":"https://www.acmicpc.net/problem/1707\n1707번: 이분 그래프입력은 여러 개의 테스트 케이스로 구성되어 있는데, 첫째 줄에 테스트 케이스의 개수 K가 주어진다. 각 테스트 케이스의 첫째 줄에는 그래프의 정점의 개수 V와 간선의 개수 E가 빈 칸을 사이에www.acmicpc.net\n뭔 개소린지 모르겠어서 인터넷을 찾아보았다.\n이분그래프는\n​이렇게 빨간색은 빨간색끼리 연걸 x, 검은색은 검은색끼리 연결 x 하는 그래프를 말한다.\n인접한 두 점이 같은 색깔이라면, 이분그래프가 아니다.\n만일 어디에도 연결되지 않은 점이라면, 이는 빨간, 검정 두개다 될 수 있다.\n모든 정점이 서로 연결되지 않았어도, 이분그래프이다.\n이 3가지를 생각한다.\ndfs든 bfs든 원리는 똑같다.\n아직 방문하지 않은 노드를 탐색하면서, (비연결 그래프인 경우도 고려)\n방문하지 않았다면, 레벨에 따라 다른색을 칠한다 ex 지금 빨이라면 이 다음레벨은 검, 그다음은 빨 \u0026hellip;..\n이미 방문한 노드와 연결된다면, 현재노드색==방문한노드색이라면 인접한 정점이 같은색인것이다.\n그런경우엔 싹다종료하고 no출력\n끝까지했는데 종료조건을 만족못했으면 yes출력\n#bfs\nimport sysinput=sys.stdin.readlinefrom collections import dequedef bfs(node): global flag s=deque() s.append(node) visited[node]=\u0026#39;white\u0026#39; while s: tmp=s.popleft() color=visited[tmp] for i in graph[tmp]: if not visited[i]: visited[i]=[\u0026#39;white\u0026#39;,\u0026#39;black\u0026#39;][color==\u0026#39;white\u0026#39;] s.append(i) else: if color == visited[i]: flag=True return for _ in range(int(input())): v,e=map(int,input().split()) graph=[[]for _ in range(v+1)] visited=[False for _ in range(v+1)] for _ in range(e): a,b=map(int,input().split()) graph[a].append(b) graph[b].append(a) flag=False for i in range(1,v+1): if not visited[i]: bfs(i) if flag: break print(\u0026#39;NO\u0026#39; if flag else \u0026#39;YES\u0026#39;) #dfs\n주의할점: 재귀로푸는거니깐 recursionlimit설정안하면 오류난다.\nimport syssys.setrecursionlimit(10**6)input=sys.stdin.readline from collections import dequedef dfs(node,value): global flag visited[node]=value for i in graph[node]: if not visited[i]: dfs(i,-value) else: if value==visited[i]: flag=True if flag: return for _ in range(int(input())): v,e=map(int,input().split()) graph=[[]for _ in range(v+1)] visited=[False for _ in range(v+1)] for _ in range(e): a,b=map(int,input().split()) graph[a].append(b) graph[b].append(a) flag=False for i in range(1,v+1): if not visited[i]: dfs(i,1) if flag: break print(\u0026#39;NO\u0026#39; if flag else \u0026#39;YES\u0026#39;) ","date":"23 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/081-%EB%B0%B1%EC%A4%80-1707-%EC%9D%B4%EB%B6%84-%EA%B7%B8%EB%9E%98%ED%94%84/","section":"Posts","summary":"https://www.acmicpc.net/problem/1707\n1707번: 이분 그래프입력은 여러 개의 테스트 케이스로 구성되어 있는데, 첫째 줄에 테스트 케이스의 개수 K가 주어진다. 각 테스트 케이스의 첫째 줄에는 그래프의 정점의 개수 V와 간선의 개수 E가 빈 칸을 사이에www.acmicpc.net\n뭔 개소린지 모르겠어서 인터넷을 찾아보았다.\n이분그래프는\n​이렇게 빨간색은 빨간색끼리 연걸 x, 검은색은 검은색끼리 연결 x 하는 그래프를 말한다.\n인접한 두 점이 같은 색깔이라면, 이분그래프가 아니다.\n만일 어디에도 연결되지 않은 점이라면, 이는 빨간, 검정 두개다 될 수 있다.\n","title":"백준 1707 이분 그래프","type":"posts"},{"content":"https://www.acmicpc.net/problem/12865\n12865번: 평범한 배낭첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)www.acmicpc.net\n첫 풀이\n2차원 배열을 이용해서 풀음\ndp[i][j]= i번째 물건까지 봤을때, 무계 j를 만족하는 가장 큰 가치\n소스\nimport sysinput=sys.stdin.readlinen,k=map(int,input().split()) wv=[[0,0]]+[[*map (int,input().split())]for _ in range(n)] dp=[[0 for _ in range(k+1)]for _ in range(n+1)] #dp[i][j]=i번째 물건까지 봤을때, 무계가 j인 배낭의 최대가치for i in range(1,n+1): for j in range(1,k+1): if j-wv[i][0]\u0026gt;=0: dp[i][j]=max(dp[i-1][j-wv[i][0]]+wv[i][1], dp[i-1][j]) else: dp[i][j]=dp[i-1][j]print(dp[-1][-1]) 그런데 다른사람들 풀이를 보다 보니, 2배는 효율적인 알고리즘이 있었다.\n2차원 배열이 아니라 1차원 배열로 푸는 방식인데,\n뒤에서부터 업데이트하면서 앞의 값을 쓰고, 계속 갱신시켜나가는 방법이다.\n진짜 부족함을 느낀다.\nimport sysinput=sys.stdin.readlinen,k=map(int,input().split()) dp=[0 for _ in range(k+1)]# dp[i]= 무계가 i일때의 최댓값for _ in range(n): w,v=map(int,input().split()) for i in range(k,w-1,-1): #이전값들을 써먹기 위해뒤에서부터 돌면서 #만일 w\u0026gt;=k이면, 반복문안돔 dp[i]=max(dp[i],dp[i-w]+v) # 무계가 i일때의 최댓값은 # {지금 주어진물건을 포함하지 않았을때의 최댓값과 # 무계가 i-w 즉 지금물건을 포함할 수 있는 경우 +지금물건의 가치} # 의 최댓값이다.print(dp[-1]) 예제\n4 7 #n,k6 13 4 8 3 6 5 12\n초기 dp\n인덱스는 무계를 나타냄\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 Column 7 Column 8 0 0 0 0 0 0 0 0 6 13일때\n7,8번째의 값이 변경됨\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 Column 7 Column 8 0 0 0 0 0 0 0 13 Column 1 Column 2 Column 3 Column 4 Column 5 Column 6 Column 7 Column 8 0 0 0 0 0 0 13 13 4 8일때\n총 8,7,6,5를 돌면서 업데이트 (8,7은 13이 더 크니깐, 6,5만 업데이트)\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 Column 7 Column 8 0 0 0 0 0 8 13 13 Column 1 Column 2 Column 3 Column 4 Column 5 Column 6 Column 7 Column 8 0 0 0 0 8 8 13 13 3,6일때\n마찬가지로 8에서 3까지 도는데, dp[8-3]+6이 14로 13보다 크므로, dp[8]업데이트\n3업데이트\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 Column 7 Column 8 0 0 0 0 8 8 13 14 Column 1 Column 2 Column 3 Column 4 Column 5 Column 6 Column 7 Column 8 0 0 0 6 8 8 13 14 5 12일때\n마찬가지로 8에서 5까지 도는데, dp[5-5]+12=0+12=12이므로,\n5번째 인덱스 변경됨\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 Column 7 Column 8 0 0 0 6 8 12 13 14 우리가 찾는건 dp[i-1]\n","date":"22 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/080-%EB%B0%B1%EC%A4%80-12865-%ED%8F%89%EB%B2%94%ED%95%9C%EB%B2%A0%EB%82%AD/","section":"Posts","summary":"https://www.acmicpc.net/problem/12865\n12865번: 평범한 배낭첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)www.acmicpc.net\n첫 풀이\n2차원 배열을 이용해서 풀음\ndp[i][j]= i번째 물건까지 봤을때, 무계 j를 만족하는 가장 큰 가치\n소스\nimport sysinput=sys.stdin.readlinen,k=map(int,input().split()) wv=[[0,0]]+[[*map (int,input().split())]for _ in range(n)] dp=[[0 for _ in range(k+1)]for _ in range(n+1)] #dp[i][j]=i번째 물건까지 봤을때, 무계가 j인 배낭의 최대가치for i in range(1,n+1): for j in range(1,k+1): if j-wv[i][0]\u003e=0: dp[i][j]=max(dp[i-1][j-wv[i][0]]+wv[i][1], dp[i-1][j]) else: dp[i][j]=dp[i-1][j]print(dp[-1][-1]) 그런데 다른사람들 풀이를 보다 보니, 2배는 효율적인 알고리즘이 있었다.\n","title":"백준 12865 평범한베낭","type":"posts"},{"content":"https://www.acmicpc.net/problem/7562\n7562번: 나이트의 이동체스판 위에 한 나이트가 놓여져 있다. 나이트가 한 번에 이동할 수 있는 칸은 아래 그림에 나와있다. 나이트가 이동하려고 하는 칸이 주어진다. 나이트는 몇 번 움직이면 이 칸으로 이동할 수www.acmicpc.net\n그냥 bfs문제임\n근데에러나서한참고심했음\n그냥 생각해야되는건, deque를 전역변수로 썼을때, deque에 아직 처리되지않은것들이 남아있다는거임\n그것만생각하면 쉬움\n##코드##\nimport sysfrom collections import dequeinput=sys.stdin.readline moves=[(-2,1),(-2,-1),(-1,2),(-1,-2),(1,2),(1,-2),(2,-1),(2,1)] def bfs(a,b,l,goal_i,goal_j): s.append((a,b,0)) arr[a][b]=1 while s: i,j,v=s.popleft() for move in moves: aa,bb=move ti,tj=i+aa,j+bb if ti==goal_i and tj==goal_j: print(v+1) return if 0\u0026lt;=ti\u0026lt;l and 0\u0026lt;=tj\u0026lt;l and arr[ti][tj]==0: arr[ti][tj]=1 s.append((ti,tj,v+1)) for _ in range(int(input())): s=deque() l=int(input()) arr=[[0 for _ in range(l)]for _ in range(l)] a,b=map(int,input().split()) goal_i,goal_j=map(int,input().split()) if a==goal_i and b==goal_j: print(0) continue bfs(a,b,l,goal_i,goal_j) ","date":"21 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/079-%EB%B0%B1%EC%A4%80-7562-%EB%82%98%EC%9D%B4%ED%8A%B8%EC%9D%98-%EC%9D%B4%EB%8F%99/","section":"Posts","summary":"https://www.acmicpc.net/problem/7562\n7562번: 나이트의 이동체스판 위에 한 나이트가 놓여져 있다. 나이트가 한 번에 이동할 수 있는 칸은 아래 그림에 나와있다. 나이트가 이동하려고 하는 칸이 주어진다. 나이트는 몇 번 움직이면 이 칸으로 이동할 수www.acmicpc.net\n그냥 bfs문제임\n근데에러나서한참고심했음\n그냥 생각해야되는건, deque를 전역변수로 썼을때, deque에 아직 처리되지않은것들이 남아있다는거임\n그것만생각하면 쉬움\n##코드##\nimport sysfrom collections import dequeinput=sys.stdin.readline moves=[(-2,1),(-2,-1),(-1,2),(-1,-2),(1,2),(1,-2),(2,-1),(2,1)] def bfs(a,b,l,goal_i,goal_j): s.append((a,b,0)) arr[a][b]=1 while s: i,j,v=s.popleft() for move in moves: aa,bb=move ti,tj=i+aa,j+bb if ti==goal_i and tj==goal_j: print(v+1) return if 0\u003c=ti\u003cl and 0\u003c=tj\u003cl and arr[ti][tj]==0: arr[ti][tj]=1 s.append((ti,tj,v+1)) for _ in range(int(input())): s=deque() l=int(input()) arr=[[0 for _ in range(l)]for _ in range(l)] a,b=map(int,input().split()) goal_i,goal_j=map(int,input().split()) if a==goal_i and b==goal_j: print(0) continue bfs(a,b,l,goal_i,goal_j)","title":"백준 7562 나이트의 이동","type":"posts"},{"content":"https://www.acmicpc.net/problem/14888\n14888번: 연산자 끼워넣기첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, \u0026hellip;, AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수,www.acmicpc.net\n처음에 permu어쩌구를 통해 모든 기호의 배열을 구해서 풀었다.\nfrom itertools import permutationsimport sysinput=sys.stdin.readline n=int(input())a=[*map(int,input().split())]q,w,e,r=map(int,input().split()) tmp_sign=\u0026#39;+\u0026#39;*q+\u0026#39;-\u0026#39;*w+\u0026#39;*\u0026#39;*e+\u0026#39;/\u0026#39;*rsigns=list(permutations(tmp_sign,n-1)) signs=list(set(signs))max_answer=-1000000000min_answer=1000000000 for sign in signs: answer=a[0] for i in range(n-1): if sign[i]==\u0026#39;+\u0026#39;: answer+=a[i+1] elif sign[i]==\u0026#39;-\u0026#39;: answer-=a[i+1] elif sign[i]==\u0026#39;*\u0026#39;: answer*=a[i+1] elif sign[i]==\u0026#39;/\u0026#39;: # answer//=a[i+1] if answer\u0026gt;=0: answer//=a[i+1] else: answer= -(-answer//a[i+1]) min_answer=min(min_answer,answer) max_answer=max(max_answer,answer)print(max_answer,min_answer,sep=\u0026#39;\\n\u0026#39;) 근데 다른 풀이를 보니 그럴거없이\n그냥 dfs?로 4경위의 수를 모두 구해서 풀면된다.\n만일 주어진 기호를 다 썼다면, 그 기호를 뺀 다른기호만 생각한다.\nimport sysinput=sys.stdin.readlinedef solve(idx,value): global max_value global min_value if idx==n: max_value=max(max_value,value) min_value=min(min_value,value) return for i in range(4): if sign[i]==0: continue if i==0: sign[i]-=1 tmp=value+a[idx] elif i==1: sign[i]-=1 tmp=value-a[idx] elif i==2: sign[i]-=1 tmp=value*a[idx] else : sign[i]-=1 if value\u0026gt;0: tmp=value//a[idx] else: tmp=-(-value//a[idx]) solve(idx+1,tmp) sign[i]+=1 n=int(input())a=[*map(int,input().split())]sign=[*map(int,input().split())] max_value=-1000000000min_value=1000000000 solve(1,a[0]) print(max_value,min_value,sep=\u0026#39;\\n\u0026#39;) 이러니깐 시간차이가 거의 10배가난다.\n다시 돌아오지 않고 계산만 하면 더빠를듯\n","date":"21 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/078-%EB%B0%B1%EC%A4%80-14888-%EC%97%B0%EC%82%B0%EC%9E%90-%EB%81%BC%EC%9B%8C%EB%84%A3%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/14888\n14888번: 연산자 끼워넣기첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, …, AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수,www.acmicpc.net\n처음에 permu어쩌구를 통해 모든 기호의 배열을 구해서 풀었다.\nfrom itertools import permutationsimport sysinput=sys.stdin.readline n=int(input())a=[*map(int,input().split())]q,w,e,r=map(int,input().split()) tmp_sign='+'*q+'-'*w+'*'*e+'/'*rsigns=list(permutations(tmp_sign,n-1)) signs=list(set(signs))max_answer=-1000000000min_answer=1000000000 for sign in signs: answer=a[0] for i in range(n-1): if sign[i]=='+': answer+=a[i+1] elif sign[i]=='-': answer-=a[i+1] elif sign[i]=='*': answer*=a[i+1] elif sign[i]=='/': # answer//=a[i+1] if answer\u003e=0: answer//=a[i+1] else: answer= -(-answer//a[i+1]) min_answer=min(min_answer,answer) max_answer=max(max_answer,answer)print(max_answer,min_answer,sep='\\n') 근데 다른 풀이를 보니 그럴거없이\n","title":"백준 14888 연산자 끼워넣기","type":"posts"},{"content":"https://www.acmicpc.net/problem/17114\n17114번: 하이퍼 토마토첫 줄에는 문제의 설명에서 창고의 크기를 나타내는 자연수 m, n, o, p, q, r, s, t, u, v, w가 주어진다. 단, 1 ≤ mnopqrstuvw ≤ 106 이다. 둘째 줄부터는 창고에 저장된 토마토들의 정보가 주어진다. 창www.acmicpc.net\n토마토3\nㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\n오기생겨서 1시간은 쓴듯하다.\n다 아는 개념이여도, 변수가많아지니 헷갈린다. 변수명 아무렇게나 짓다가 변수겹처서 틀린것도 셀 수 없다.\n그냥 재미용\nmove1=[1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] move2=[0,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] move3=[0,0,0,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] move4=[0,0,0,0,0,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0] move5=[0,0,0,0,0,0,0,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0] move6=[0,0,0,0,0,0,0,0,0,0,1,-1,0,0,0,0,0,0,0,0,0,0] move7=[0,0,0,0,0,0,0,0,0,0,0,0,1,-1,0,0,0,0,0,0,0,0] move8=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,0,0,0,0,0,0] move9=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,0,0,0,0] move10=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,0,0] move11=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1] # def bfs(m, n, o, p, q, r, s, t, u, v, w):def bfs(): while que: ww,vv,uu,tt,ss,rr,qq,pp,oo,nn,mm,value=que.popleft() for i in range(22): mmm= mm+move1[i] nnn= nn+move2[i] ooo= oo+move3[i] ppp= pp+move4[i] qqq= qq+move5[i] rrr= rr+move6[i] sss= ss+move7[i] ttt= tt+move8[i] uuu= uu+move9[i] vvv= vv+move10[i] www= ww+move11[i] if 0\u0026lt;=mmm\u0026lt;m and 0\u0026lt;=nnn\u0026lt;n and 0\u0026lt;=ooo\u0026lt;o and 0\u0026lt;=ppp\u0026lt;p and 0\u0026lt;=qqq\u0026lt;q and 0\u0026lt;=rrr\u0026lt;r and \\ 0\u0026lt;=sss\u0026lt;s and 0\u0026lt;=ttt\u0026lt;t and 0\u0026lt;=uuu\u0026lt;u and 0\u0026lt;=vvv\u0026lt;v and 0\u0026lt;=www\u0026lt;w and\\ arr[www][vvv][uuu][ttt][sss][rrr][qqq][ppp][ooo][nnn][mmm] ==\u0026#39;0\u0026#39;: que.append([www,vvv,uuu,ttt,sss,rrr,qqq,ppp,ooo,nnn,mmm,value+1]) arr[www][vvv][uuu][ttt][sss][rrr][qqq][ppp][ooo][nnn][mmm]=\u0026#39;-1\u0026#39; for ww in range(w): for vv in range(v): for uu in range(u): for tt in range(t): for ss in range(s): for rr in range(r): for qq in range(q): for pp in range(p): for oo in range(o): for nn in range(n): for mm in range(m): if arr[ww][vv][uu][tt][ss][rr][qq][pp][oo][nn][mm]==\u0026#39;0\u0026#39;: print(-1) return print(value) import sysinput=sys.stdin.readlinefrom collections import deque m, n, o, p, q, r, s, t, u, v, w=map(int,input().split())arr=[ [[[[[[[[[list(input().split()) for _ in range(n) ] for _ in range(o) ] for _ in range(p) ] for _ in range(q) ] for _ in range(r) ] for _ in range(s) ] for _ in range(t) ] for _ in range(u) ] for _ in range(v) ] for _ in range(w) ]que=deque()for ww in range(w): for vv in range(v): for uu in range(u): for tt in range(t): for ss in range(s): for rr in range(r): for qq in range(q): for pp in range(p): for oo in range(o): for nn in range(n): for mm in range(m): # print(\u0026#39;test\u0026#39;,ww,vv,uu,tt,ss,rr,qq,pp,oo,nn,mm) if arr[ww][vv][uu][tt][ss][rr][qq][pp][oo][nn][mm]==\u0026#39;1\u0026#39;: que.append([ww,vv,uu,tt,ss,rr,qq,pp,oo,nn,mm,0]) arr[ww][vv][uu][tt][ss][rr][qq][pp][oo][nn][mm]=\u0026#39;-1\u0026#39; bfs() 이렇게짜면 답은 맞지만 파이썬이 견디질 못한다.\npypy로 하면 시간초과를 없앨 수 있다.\n근데 ㅆ@발 ㅈ같은거는\n나보다 훨씬 짧은코딩으로 훨씬 빠르계 게산하는 ㅁㅊㄴ들이 있다.\n코드를 봐도 이해가 안된다.\n난평생가도 모를것같다.\n나중에 숙련도좀 올리고 한번 시도해봐야겠다. 이러고 잊어버리겠지만\n","date":"20 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/077-%EB%B0%B1%EC%A4%80-17114-%ED%95%98%EC%9D%B4%ED%8D%BC-%ED%86%A0%EB%A7%88%ED%86%A0/","section":"Posts","summary":"https://www.acmicpc.net/problem/17114\n17114번: 하이퍼 토마토첫 줄에는 문제의 설명에서 창고의 크기를 나타내는 자연수 m, n, o, p, q, r, s, t, u, v, w가 주어진다. 단, 1 ≤ mnopqrstuvw ≤ 106 이다. 둘째 줄부터는 창고에 저장된 토마토들의 정보가 주어진다. 창www.acmicpc.net\n토마토3\nㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\n오기생겨서 1시간은 쓴듯하다.\n다 아는 개념이여도, 변수가많아지니 헷갈린다. 변수명 아무렇게나 짓다가 변수겹처서 틀린것도 셀 수 없다.\n그냥 재미용\nmove1=[1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] move2=[0,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] move3=[0,0,0,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] move4=[0,0,0,0,0,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0] move5=[0,0,0,0,0,0,0,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0] move6=[0,0,0,0,0,0,0,0,0,0,1,-1,0,0,0,0,0,0,0,0,0,0] move7=[0,0,0,0,0,0,0,0,0,0,0,0,1,-1,0,0,0,0,0,0,0,0] move8=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,0,0,0,0,0,0] move9=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,0,0,0,0] move10=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,0,0] move11=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1] # def bfs(m, n, o, p, q, r, s, t, u, v, w):def bfs(): while que: ww,vv,uu,tt,ss,rr,qq,pp,oo,nn,mm,value=que.popleft() for i in range(22): mmm= mm+move1[i] nnn= nn+move2[i] ooo= oo+move3[i] ppp= pp+move4[i] qqq= qq+move5[i] rrr= rr+move6[i] sss= ss+move7[i] ttt= tt+move8[i] uuu= uu+move9[i] vvv= vv+move10[i] www= ww+move11[i] if 0\u003c=mmm\u003cm and 0\u003c=nnn\u003cn and 0\u003c=ooo\u003co and 0\u003c=ppp\u003cp and 0\u003c=qqq\u003cq and 0\u003c=rrr\u003cr and \\ 0\u003c=sss\u003cs and 0\u003c=ttt\u003ct and 0\u003c=uuu\u003cu and 0\u003c=vvv\u003cv and 0\u003c=www\u003cw and\\ arr[www][vvv][uuu][ttt][sss][rrr][qqq][ppp][ooo][nnn][mmm] =='0': que.append([www,vvv,uuu,ttt,sss,rrr,qqq,ppp,ooo,nnn,mmm,value+1]) arr[www][vvv][uuu][ttt][sss][rrr][qqq][ppp][ooo][nnn][mmm]='-1' for ww in range(w): for vv in range(v): for uu in range(u): for tt in range(t): for ss in range(s): for rr in range(r): for qq in range(q): for pp in range(p): for oo in range(o): for nn in range(n): for mm in range(m): if arr[ww][vv][uu][tt][ss][rr][qq][pp][oo][nn][mm]=='0': print(-1) return print(value) import sysinput=sys.stdin.readlinefrom collections import deque m, n, o, p, q, r, s, t, u, v, w=map(int,input().split())arr=[ [[[[[[[[[list(input().split()) for _ in range(n) ] for _ in range(o) ] for _ in range(p) ] for _ in range(q) ] for _ in range(r) ] for _ in range(s) ] for _ in range(t) ] for _ in range(u) ] for _ in range(v) ] for _ in range(w) ]que=deque()for ww in range(w): for vv in range(v): for uu in range(u): for tt in range(t): for ss in range(s): for rr in range(r): for qq in range(q): for pp in range(p): for oo in range(o): for nn in range(n): for mm in range(m): # print('test',ww,vv,uu,tt,ss,rr,qq,pp,oo,nn,mm) if arr[ww][vv][uu][tt][ss][rr][qq][pp][oo][nn][mm]=='1': que.append([ww,vv,uu,tt,ss,rr,qq,pp,oo,nn,mm,0]) arr[ww][vv][uu][tt][ss][rr][qq][pp][oo][nn][mm]='-1' bfs() 이렇게짜면 답은 맞지만 파이썬이 견디질 못한다.\n","title":"백준 17114 하이퍼 토마토","type":"posts"},{"content":"https://www.acmicpc.net/problem/7569\n7569번: 토마토첫 줄에는 상자의 크기를 나타내는 두 정수 M,N과 쌓아올려지는 상자의 수를 나타내는 H가 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M ≤ 100, 2 ≤ N ≤ 100,www.acmicpc.net\n전의 토마토와 완전히 똑같은 방식이다.\n다만, 이번에는 z축이 추가됬다.\ni,j,k로 인덱싱하고,\n움직이는방향을 [[-1,0,0],[0,-1,0],[0,0,-1],[1,0,0],[0,1,0],[0,0,1]]\n6방향으로 bfs를 진행하면 된다.\n진행할때마다 얼마나 날짜가 지났는지까지 세주면 완벽\n마지막에, 하나씩탐색하다가 0이 있으면 -1출력하고 종료\nimport sysinput=sys.stdin.readlinefrom collections import deque moves=[[-1,0,0],[0,-1,0],[0,0,-1],[1,0,0],[0,1,0],[0,0,1]]def bfs(q): day=0 while q: i,j,k,v=q.popleft() for move in moves: a,b,c=move ti=a+i tj=b+j tk=c+k if 0\u0026lt;=ti\u0026lt;h and 0\u0026lt;=tj\u0026lt;n and 0\u0026lt;=tk\u0026lt;m and arr[ti][tj][tk]==\u0026#39;0\u0026#39;: q.append([ti,tj,tk,v+1]) arr[ti][tj][tk]=\u0026#39;-1\u0026#39; for i in range(h): for j in range(n): for k in range(m): if arr[i][j][k]==\u0026#39;0\u0026#39;: print(-1) return print(v) m,n,h=map(int,input().split()) arr=[[list(input().strip().split())for _ in range(n)]for _ in range(h)]q=deque() answer=0for i in range(h): for j in range(n): for k in range(m): if arr[i][j][k]==\u0026#39;1\u0026#39;: q.append((i,j,k,0)) arr[i][j][k]=\u0026#39;-1\u0026#39; bfs(q) ","date":"20 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/076-%EB%B0%B1%EC%A4%80-7569-%ED%86%A0%EB%A7%88%ED%86%A02/","section":"Posts","summary":"https://www.acmicpc.net/problem/7569\n7569번: 토마토첫 줄에는 상자의 크기를 나타내는 두 정수 M,N과 쌓아올려지는 상자의 수를 나타내는 H가 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M ≤ 100, 2 ≤ N ≤ 100,www.acmicpc.net\n전의 토마토와 완전히 똑같은 방식이다.\n다만, 이번에는 z축이 추가됬다.\ni,j,k로 인덱싱하고,\n움직이는방향을 [[-1,0,0],[0,-1,0],[0,0,-1],[1,0,0],[0,1,0],[0,0,1]]\n6방향으로 bfs를 진행하면 된다.\n진행할때마다 얼마나 날짜가 지났는지까지 세주면 완벽\n마지막에, 하나씩탐색하다가 0이 있으면 -1출력하고 종료\nimport sysinput=sys.stdin.readlinefrom collections import deque moves=[[-1,0,0],[0,-1,0],[0,0,-1],[1,0,0],[0,1,0],[0,0,1]]def bfs(q): day=0 while q: i,j,k,v=q.popleft() for move in moves: a,b,c=move ti=a+i tj=b+j tk=c+k if 0\u003c=ti\u003ch and 0\u003c=tj\u003cn and 0\u003c=tk\u003cm and arr[ti][tj][tk]=='0': q.append([ti,tj,tk,v+1]) arr[ti][tj][tk]='-1' for i in range(h): for j in range(n): for k in range(m): if arr[i][j][k]=='0': print(-1) return print(v) m,n,h=map(int,input().split()) arr=[[list(input().strip().split())for _ in range(n)]for _ in range(h)]q=deque() answer=0for i in range(h): for j in range(n): for k in range(m): if arr[i][j][k]=='1': q.append((i,j,k,0)) arr[i][j][k]='-1' bfs(q)","title":"백준 7569 토마토2","type":"posts"},{"content":"https://www.acmicpc.net/problem/2178\n2178번: 미로 탐색첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.www.acmicpc.net\n그냥 전형적인 bfs문제\nbfs를 진행하면서 level까지 업데이트하고,\nn-1,m-1에 도달하면, level+1을 출력\n##코드##\nfrom collections import dequeimport sysinput=sys.stdin.readlinedef bfs(i=0,j=0): arr[i][j]=\u0026#39;0\u0026#39; s=deque() s.append([i,j,1]) while s: i,j,v=s.popleft() for move in moves: a,b=move tmp_i=i+a tmp_j=j+b if 0\u0026lt;=tmp_i\u0026lt;n and 0\u0026lt;=tmp_j\u0026lt;m and arr[tmp_i][tmp_j]==\u0026#39;1\u0026#39;: # print(tmp_i,tmp_j,v) if tmp_i==n-1 and tmp_j==m-1: print(v+1) return s.append([tmp_i,tmp_j,v+1]) arr[tmp_i][tmp_j]=\u0026#39;0\u0026#39; n,m=map(int,input().split()) arr=[list(input().strip()) for _ in range(n)]moves=[[-1,0],[1,0],[0,-1],[0,1]] bfs() ","date":"19 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/075-%EB%B0%B1%EC%A4%80-2178-%EB%AF%B8%EB%A1%9C-%ED%83%90%EC%83%89/","section":"Posts","summary":"https://www.acmicpc.net/problem/2178\n2178번: 미로 탐색첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.www.acmicpc.net\n그냥 전형적인 bfs문제\nbfs를 진행하면서 level까지 업데이트하고,\nn-1,m-1에 도달하면, level+1을 출력\n##코드##\nfrom collections import dequeimport sysinput=sys.stdin.readlinedef bfs(i=0,j=0): arr[i][j]='0' s=deque() s.append([i,j,1]) while s: i,j,v=s.popleft() for move in moves: a,b=move tmp_i=i+a tmp_j=j+b if 0\u003c=tmp_i\u003cn and 0\u003c=tmp_j\u003cm and arr[tmp_i][tmp_j]=='1': # print(tmp_i,tmp_j,v) if tmp_i==n-1 and tmp_j==m-1: print(v+1) return s.append([tmp_i,tmp_j,v+1]) arr[tmp_i][tmp_j]='0' n,m=map(int,input().split()) arr=[list(input().strip()) for _ in range(n)]moves=[[-1,0],[1,0],[0,-1],[0,1]] bfs()","title":"백준 2178 미로 탐색","type":"posts"},{"content":"https://www.acmicpc.net/problem/2667\n2667번: 단지번호붙이기\u0026lt;그림 1\u0026gt;과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여www.acmicpc.net\n흔한 탐색문제\nbfs로풀음\n2차원 배열 apartment를 돌면서,\n만약 apartment[i][j[]가 \u0026lsquo;1\u0026rsquo;인 경우에 bfs를 실행한다.\n여기서 한번에 몇집이나 탐색하는지만 세어주면 된다.\n##코드##\nimport sysinput=sys.stdin.readlinefrom collections import deque#bfs moves=[[-1,0],[1,0],[0,-1],[0,1]]def bfs(i,j): s=deque() s.append([i,j]) apartment[i][j]=0 cnt=1 while s: i,j=s.popleft() for move in moves: a,b=move tmp_i,tmp_j=i+a,j+b # print(tmp_i,tmp_j) if (0\u0026lt;=tmp_i\u0026lt;n)and(0\u0026lt;=tmp_j\u0026lt;n)and(apartment[tmp_i][tmp_j]==\u0026#39;1\u0026#39;): s.append([tmp_i,tmp_j]) apartment[tmp_i][tmp_j]=\u0026#39;0\u0026#39; cnt+=1 return cnt n=int(input())apartment=[list(input())for _ in range(n)]l=[] for i in range(n): for j in range(n): if apartment[i][j]!=\u0026#39;0\u0026#39;: l.append(bfs(i,j))l.sort()print(len(l))for ll in l: print(ll) ","date":"19 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/074-%EB%B0%B1%EC%A4%80-2667-%EB%8B%A8%EC%A7%80%EB%B2%88%ED%98%B8%EB%B6%99%EC%9D%B4%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/2667\n2667번: 단지번호붙이기\u003c그림 1\u003e과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여www.acmicpc.net\n흔한 탐색문제\nbfs로풀음\n2차원 배열 apartment를 돌면서,\n만약 apartment[i][j[]가 ‘1’인 경우에 bfs를 실행한다.\n여기서 한번에 몇집이나 탐색하는지만 세어주면 된다.\n##코드##\nimport sysinput=sys.stdin.readlinefrom collections import deque#bfs moves=[[-1,0],[1,0],[0,-1],[0,1]]def bfs(i,j): s=deque() s.append([i,j]) apartment[i][j]=0 cnt=1 while s: i,j=s.popleft() for move in moves: a,b=move tmp_i,tmp_j=i+a,j+b # print(tmp_i,tmp_j) if (0\u003c=tmp_i\u003cn)and(0\u003c=tmp_j\u003cn)and(apartment[tmp_i][tmp_j]=='1'): s.append([tmp_i,tmp_j]) apartment[tmp_i][tmp_j]='0' cnt+=1 return cnt n=int(input())apartment=[list(input())for _ in range(n)]l=[] for i in range(n): for j in range(n): if apartment[i][j]!='0': l.append(bfs(i,j))l.sort()print(len(l))for ll in l: print(ll)","title":"백준 2667 단지번호붙이기","type":"posts"},{"content":"https://www.acmicpc.net/problem/13023\n13023번: ABCDE문제의 조건에 맞는 A, B, C, D, E가 존재하면 1을 없으면 0을 출력한다.www.acmicpc.net\n처음 읽었을 때는 뭔 개소린진 모르겠었는데, 검색해보니 아주 간단한 문제였다.\nA는 B와 친구다. B는 C와 친구다. C는 D와 친구다. D는 E와 친구다. 를 만족하는지 찾는다는 뜻은,\na부터 시작해서 깊이가 4 (총 5개) 인 배열이 있는지 묻는 것이다.\n먼져, input을 받아 그래프를 만든다. (친구비를 내지 않는이상 쌍방향그래프일거다)\n친구관계가없는 찐따는 당연히 포함되지 않을 것이므로,\n친구관계가 하나라도 존재하는 정점을 모두 돌면서, 깊이가 4인 배열을 찾는다.\n그리고 하나라도 만족하면, 1을 출력하고 즉시 종료하면 된다.\n아이디어만 떠올리면 쉬운 문제였다.\nimport sysinput=sys.stdin.readlinedef dfs(tmp=[]): if len(tmp)==5: print(1) exit() for i in graph[tmp[-1]]: # ttt=(graph[tmp[-1]]) #가장 최근에 추가한 친구의 친구를찾자! if i not in tmp: dfs(tmp+[i]) return n,m=map(int,input().split())graph=[[] for _ in range(n)]for _ in range(m): a,b=map(int,input().split()) graph[a].append(b) graph[b].append(a) for j in range(len(graph)): if graph[j]: #친구관계가 있는 사람만 탐색 dfs(tmp=[j])print(0) p.s\ndfs를 사용할때, 현재 dfs안에서 append를 하게되면 pop을 꼭해줘야한다.!!\n","date":"18 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/073-%EB%B0%B1%EC%A4%80-13023-abcde/","section":"Posts","summary":"https://www.acmicpc.net/problem/13023\n13023번: ABCDE문제의 조건에 맞는 A, B, C, D, E가 존재하면 1을 없으면 0을 출력한다.www.acmicpc.net\n처음 읽었을 때는 뭔 개소린진 모르겠었는데, 검색해보니 아주 간단한 문제였다.\nA는 B와 친구다. B는 C와 친구다. C는 D와 친구다. D는 E와 친구다. 를 만족하는지 찾는다는 뜻은,\na부터 시작해서 깊이가 4 (총 5개) 인 배열이 있는지 묻는 것이다.\n먼져, input을 받아 그래프를 만든다. (친구비를 내지 않는이상 쌍방향그래프일거다)\n친구관계가없는 찐따는 당연히 포함되지 않을 것이므로,\n친구관계가 하나라도 존재하는 정점을 모두 돌면서, 깊이가 4인 배열을 찾는다.\n","title":"백준 13023 ABCDE","type":"posts"},{"content":"https://www.acmicpc.net/problem/11724\n11724번: 연결 요소의 개수첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2) 둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주www.acmicpc.net\nbfs와 dfs중 하나를 선택해서 풀면 됨\n먼져 visited를 만든다.\n이후 모든 정점에 대하여 방문을 하지 않은 노드에서 탐색을 수행한다. 이때마다 1씩 정답을\n증가시킨다.\n주의할 점\n1.양뱡향 그래프\n컴퓨터는 숫자를 0부터 센다는걸 생각 이것만지키면 쉬움\n##코드\nfrom collections import dequeimport sysinput=sys.stdin.readlinedef bfs(i): stack=deque([i]) while stack: tmp=stack.popleft() for j in graph[tmp]: if not visited[j]: stack.append(j) visited[j]=True n,m=map(int,input().split())graph=[[] for _ in range(n+1)]for _ in range(m): u,v=map(int,input().split()) graph[u].append(v) graph[v].append(u) visited=[False for _ in range(n+1)]answer=0for i in range(1,n+1): if not visited[i]: bfs(i) answer+=1print(answer) ","date":"17 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/072-%EB%B0%B1%EC%A4%80-11724-%EC%97%B0%EA%B2%B0-%EC%9A%94%EC%86%8C%EC%9D%98-%EA%B0%9C%EC%88%98/","section":"Posts","summary":"https://www.acmicpc.net/problem/11724\n11724번: 연결 요소의 개수첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2) 둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주www.acmicpc.net\nbfs와 dfs중 하나를 선택해서 풀면 됨\n먼져 visited를 만든다.\n이후 모든 정점에 대하여 방문을 하지 않은 노드에서 탐색을 수행한다. 이때마다 1씩 정답을\n증가시킨다.\n주의할 점\n","title":"백준 11724 연결 요소의 개수","type":"posts"},{"content":"https://www.acmicpc.net/problem/11723`\n11723번: 집합첫째 줄에 수행해야 하는 연산의 수 M (1 ≤ M ≤ 3,000,000)이 주어진다. 둘째 줄부터 M개의 줄에 수행해야 하는 연산이 한 줄에 하나씩 주어진다.www.acmicpc.net\n와 이거때문에 하루종일 고민했다.\n간단하게 set을 이용해서 풀수도 있다. 다만 비트마스킹을 체험해보고싶었다.\n우리는 s를 집합이아니라, 2비트 숫자로 이용해서 풀거다.\nx라는 숫자가 집합에 있다는것은 , s의 x번째가 1이라는 것이다. 당연히 없으면 0\n만일 {1,3}이라면\ns=0b1010 #0없음, 1있음, 2없음, 3있음\n그리고, 문제의조건에 따라, 0은쓸일이없다.\n명령\n**add x:**집합에 x를 추가한다.\n비트 s의 x번째 원소를 1로 만들면된다.\n만일 {1,3}에 2를 추가한다는 뜻은:\n0b1010 을 0b1110으로 만든다는 뜻이다.\n1\u0026laquo; 2를 하면 0b0100이므로\n0b1010 | 0b0100을 하면 (or)\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 부 호 1 0 1 0 부 호 0 1 0 0 =\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 부 호 1 1 1 0 이 된다.\nremove x: 집합에서 x를 제거한다.\n비트 s의 x번째 원소를 0으로 만든다.\n~연산자를 사용하면 된다.\n~연산자는, 간단하게 1인비트는 0으로, 0인비트는 1로 바꾼다고 생각하면 된다.\n{1,2,3}에서 2를 제거한다면\n0b1110 \u0026amp; ~(1\u0026laquo;2)이다. = 0b1110 \u0026amp; ~(0b100)\n=0b1110 \u0026amp; ~(1b001)\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 부 호 1 0 1 0 부 호 0 0 1 1 =\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 부 호 1 0 1 0 ## TMI ##\n파이썬에서 bin(~(1\u0026laquo;2)를 계산하면 , -0b011이 아닌 -0b101가 나온다. 이것때문에 시간을 많이날렸다.\n간단하게 말하자면, -0b101은 사실 우리가 생각하는-0b011과 같다.\n-0b011은 컴퓨터가 인식하는 수이고, 출력할때는 우리에게 익숙한 2진수로 보여주기 때문이다.\n그냥 킨건끄고 끈건킨다고 생각하자\n(보수에 대해서는 나도 솔직히 이해했는지 모르겠다. 헷갈릴땐 구글에서 틸드연산자를 찾아보자)\ncheck****x : 집합에 x가 있는지없는지 확인한다.\n{1,2,3}에서 2가 있을까?\n0b1110 \u0026amp; 0b100 (s \u0026amp; 1\u0026laquo;2)\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 부 호 1 1 1 0 부 호 0 1 0 0 =\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 부 호 0 1 0 0 이렇게 0이아닌 결과값이 나오면 있고, 0이면 없는거다.\n{1,3}일경우\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 부 호 1 0 1 0 부 호 0 1 0 0 =\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 부 호 0 0 0 0 toggle x : x가있으면 x를 제거, 없으면 x를 추가\nxor의 개념을 알아야 한다. (베타적 논리합)\nColumn 1 Column 2 입력 출력 A B 0 0 0 1 1 0 1 1 비트값이 서로 다르면 1, 같으면 0을 출력한다.\n{1,2,3} , 2를 예로 들 때, 1\u0026laquo;2와 xor연산을 한다.\n0b1110 ^ 0b0100\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 부 호 1 1 1 0 부 호 0 1 0 0 =\nColumn 1 Column 2 Column 3 Column 4 Column 5 Column 6 부 호 1 0 1 0 2번째 원소가 1이므로,\n1^1은 을 통해 0으로 만든다.\nall: s를 {1~20} 으로 바꾼다.\n0b0을 0b1110로 바꾸는 거다.\n잘 생각해보면 0b1110은 0b10000 -1 이다.\n즉 0b101111111111111111111111로 바꾸기위해서는\ns=(1\u0026laquo;21)-1이 된다.\nempty\n0\n설명이필요?\n전체코드\ns=0b0import sysinput=sys.stdin.readlinefor _ in range(int(input())): order=input().split() cmd=order[0] if cmd==\u0026#39;add\u0026#39;: s= s| (1\u0026lt;\u0026lt;int(order[1])) elif cmd==\u0026#39;remove\u0026#39;: s= s\u0026amp; ~(1\u0026lt;\u0026lt;int(order[1])) elif cmd==\u0026#39;check\u0026#39;: if s \u0026amp; (1\u0026lt;\u0026lt;int(order[1])) ==0: print(0) else: print(1) elif cmd==\u0026#34;toggle\u0026#34;: s= s^(1\u0026lt;\u0026lt;int(order[1])) elif cmd==\u0026#34;all\u0026#34;: s= s= (1\u0026lt;\u0026lt;21)-1 elif cmd==\u0026#34;empty\u0026#34;: s=0 머리아프다\n","date":"16 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/071-%EB%B0%B1%EC%A4%80-11723-%EC%A7%91%ED%95%A9/","section":"Posts","summary":"https://www.acmicpc.net/problem/11723`\n11723번: 집합첫째 줄에 수행해야 하는 연산의 수 M (1 ≤ M ≤ 3,000,000)이 주어진다. 둘째 줄부터 M개의 줄에 수행해야 하는 연산이 한 줄에 하나씩 주어진다.www.acmicpc.net\n와 이거때문에 하루종일 고민했다.\n간단하게 set을 이용해서 풀수도 있다. 다만 비트마스킹을 체험해보고싶었다.\n우리는 s를 집합이아니라, 2비트 숫자로 이용해서 풀거다.\nx라는 숫자가 집합에 있다는것은 , s의 x번째가 1이라는 것이다. 당연히 없으면 0\n만일 {1,3}이라면\ns=0b1010 #0없음, 1있음, 2없음, 3있음\n그리고, 문제의조건에 따라, 0은쓸일이없다.\n명령\n**add x:**집합에 x를 추가한다.\n","title":"백준 11723 집합","type":"posts"},{"content":"https://www.acmicpc.net/problem/1182\n1182번: 부분수열의 합첫째 줄에 정수의 개수를 나타내는 N과 정수 S가 주어진다. (1 ≤ N ≤ 20, |S| ≤ 1,000,000) 둘째 줄에 N개의 정수가 빈 칸을 사이에 두고 주어진다. 주어지는 정수의 절댓값은 100,000을 넘지 않는다.www.acmicpc.net\n풀이 1:\ndfs로 모든 수열을 구한다음, 0이되는경우를 센다\nn,s=map(int,input().split())nums=[* map(int,input().split())] visit=[False for _ in range(n)]cnt=0tmp=[]def dfs(start=0): global cnt if tmp: if sum(tmp)==s: cnt+=1 if len(tmp)==n: return for i in range(start,len(nums)): if not visit[i]: tmp.append(nums[i]) dfs(i+1) tmp.pop() visitdfs()print(cnt) 풀이2:\n각각의 원소를 포함/포함안하는 2가지 방향으로 뻗어나가는 dfs\nn,s=map(int,input().split())nums=[* map(int,input().split())]cnt=0 def dfs(v=0,depth=0): global cnt if depth==n: return v+=nums[depth] if v==s:cnt+=1 dfs(v,depth+1) #포함하는경우 dfs(v-nums[depth],depth+1) #포함안하는경우(다시빼줌)dfs()print(cnt) 어렵ㄷ\n","date":"15 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/070-%EB%B0%B1%EC%A4%80-1182-%EB%B6%80%EB%B6%84%EC%88%98%EC%97%B4%EC%9D%98-%ED%95%A9/","section":"Posts","summary":"https://www.acmicpc.net/problem/1182\n1182번: 부분수열의 합첫째 줄에 정수의 개수를 나타내는 N과 정수 S가 주어진다. (1 ≤ N ≤ 20, |S| ≤ 1,000,000) 둘째 줄에 N개의 정수가 빈 칸을 사이에 두고 주어진다. 주어지는 정수의 절댓값은 100,000을 넘지 않는다.www.acmicpc.net\n풀이 1:\ndfs로 모든 수열을 구한다음, 0이되는경우를 센다\nn,s=map(int,input().split())nums=[* map(int,input().split())] visit=[False for _ in range(n)]cnt=0tmp=[]def dfs(start=0): global cnt if tmp: if sum(tmp)==s: cnt+=1 if len(tmp)==n: return for i in range(start,len(nums)): if not visit[i]: tmp.append(nums[i]) dfs(i+1) tmp.pop() visitdfs()print(cnt) 풀이2:\n","title":"백준 1182 부분수열의 합","type":"posts"},{"content":"https://www.acmicpc.net/problem/2529\n2529번: 부등호여러분은 제시된 부등호 관계를 만족하는 k+1 자리의 최대, 최소 정수를 첫째 줄과 둘째 줄에 각각 출력해야 한다. 단 아래 예(1)과 같이 첫 자리가 0인 경우도 정수에 포함되어야 한다. 모든 입력www.acmicpc.net\n글작성하다가날라가서 빡친다.\n문자열로만비교함\n배열을 dfs로 확장하다가,\n배열이 2 이상이면:\n배열의[-1][-2] 가 부등호[배열수-2)와 같지 않으면 가지친다.\nk+1까지 살아남은 배열을 모두 리스트에 넣고,\n가장작은값은 [0], 가장큰값은[-1]이 된다.\nimport sysinput=sys.stdin.readlinek=int(input())sign=input().split()ans_list=[] str_range=[str(x) for x in range(10)]def dfs(tmp): global min_ans global max_ans if len(tmp)\u0026gt;1: if sign[len(tmp)-2]==\u0026#39;\u0026lt;\u0026#39; and tmp[-2] \u0026gt; tmp[-1]: return if sign[len(tmp)-2]==\u0026#39;\u0026gt;\u0026#39; and tmp[-2] \u0026lt; tmp[-1]: return if len(tmp)==k+1: ans_list.append(\u0026#39;\u0026#39;.join(tmp)) return for i in str_range: if i not in tmp: tmp.append(i) dfs(tmp) tmp.pop() return dfs([])print(ans_list[-1],ans_list[0],sep=\u0026#39;\\n\u0026#39;) ","date":"13 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/069-%EB%B0%B1%EC%A4%80-2529-%EB%B6%80%EB%93%B1%ED%98%B8/","section":"Posts","summary":"https://www.acmicpc.net/problem/2529\n2529번: 부등호여러분은 제시된 부등호 관계를 만족하는 k+1 자리의 최대, 최소 정수를 첫째 줄과 둘째 줄에 각각 출력해야 한다. 단 아래 예(1)과 같이 첫 자리가 0인 경우도 정수에 포함되어야 한다. 모든 입력www.acmicpc.net\n글작성하다가날라가서 빡친다.\n문자열로만비교함\n배열을 dfs로 확장하다가,\n배열이 2 이상이면:\n배열의[-1][-2] 가 부등호[배열수-2)와 같지 않으면 가지친다.\nk+1까지 살아남은 배열을 모두 리스트에 넣고,\n가장작은값은 [0], 가장큰값은[-1]이 된다.\nimport sysinput=sys.stdin.readlinek=int(input())sign=input().split()ans_list=[] str_range=[str(x) for x in range(10)]def dfs(tmp): global min_ans global max_ans if len(tmp)\u003e1: if sign[len(tmp)-2]=='\u003c' and tmp[-2] \u003e tmp[-1]: return if sign[len(tmp)-2]=='\u003e' and tmp[-2] \u003c tmp[-1]: return if len(tmp)==k+1: ans_list.append(''.join(tmp)) return for i in str_range: if i not in tmp: tmp.append(i) dfs(tmp) tmp.pop() return dfs([])print(ans_list[-1],ans_list[0],sep='\\n')","title":"백준 2529 부등호","type":"posts"},{"content":"https://www.acmicpc.net/problem/14889\n14889번: 스타트와 링크예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.www.acmicpc.net\n맨처음에 itertools로풀었다.\n굳이 모든 콤비에 대하여 다 구할 필요 없이,\n콤비의 반만 계산하면 나머지는 스타트와 링크가 바뀐 경우이므로,\n반만 계산한다.\nfrom itertools import combinationsimport sysinput=sys.stdin.readline n=int(input())arr=[]for _ in range(n): arr.append([*map(int,input().split())])all_people=[x for x in range(n)] start_teams=list(combinations(all_people,n//2)) start_teams=start_teams[:len(start_teams)//2]all_people=set(all_people) answer=1e+10for start in start_teams: link=list(set(start)^all_people) start_score=0 link_score=0 for i in range(n//2): for j in range(n//2): start_score+=arr[start[i]][start[j]] link_score+=arr[link[i]][link[j]] tmp=abs(start_score-link_score) if tmp\u0026lt;answer: answer=tmpprint(answer) 문제는 dfs로 풀 때 정답코드를 변수명만 다르게해서 배껴도 문제가 생긴다는 점이다.\n1시간넘게 뻘짓했는데 도저히 모르겠어서 포기하고 질문올렸다.\n나중에수정예정\n시발 걍 정신없어서 개짓거리한거\ndfs로 푼 코드(완전탐색임)\nimport sysinput=sys.stdin.readlinen=int(input())answer=float(\u0026#34;inf\u0026#34;) arr=[list(map(int, input().split())) for _ in range(n)]def get_score(team): tmp=0 for i in team: for j in team: tmp+=arr[i][j] return tmpdef dfs(start,team): global answer if len(team)==n//2: other=[x for x in range(n) if x not in team] answer=min(abs(get_score(team)-get_score(other)),answer) return for i in range(start,n): dfs(i+1,team+[i]) dfs(0, []) print(answer) ","date":"13 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/068-%EB%B0%B1%EC%A4%80-14889-%EC%8A%A4%ED%83%80%ED%8A%B8%EC%99%80-%EB%A7%81%ED%81%AC/","section":"Posts","summary":"https://www.acmicpc.net/problem/14889\n14889번: 스타트와 링크예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.www.acmicpc.net\n맨처음에 itertools로풀었다.\n굳이 모든 콤비에 대하여 다 구할 필요 없이,\n콤비의 반만 계산하면 나머지는 스타트와 링크가 바뀐 경우이므로,\n반만 계산한다.\nfrom itertools import combinationsimport sysinput=sys.stdin.readline n=int(input())arr=[]for _ in range(n): arr.append([*map(int,input().split())])all_people=[x for x in range(n)] start_teams=list(combinations(all_people,n//2)) start_teams=start_teams[:len(start_teams)//2]all_people=set(all_people) answer=1e+10for start in start_teams: link=list(set(start)^all_people) start_score=0 link_score=0 for i in range(n//2): for j in range(n//2): start_score+=arr[start[i]][start[j]] link_score+=arr[link[i]][link[j]] tmp=abs(start_score-link_score) if tmp\u003canswer: answer=tmpprint(answer) 문제는 dfs로 풀 때 정답코드를 변수명만 다르게해서 배껴도 문제가 생긴다는 점이다.\n","title":"백준 14889 스타트와 링크","type":"posts"},{"content":"https://fuckingcomputer.tistory.com/manage/newpost/?type=post\u0026amp;returnURL=%2Fmanage%2Fposts%2F\nTISTORY나를 표현하는 블로그를 만들어보세요.www.tistory.com\n실버2인데 개어려움\n기본적으로, 마지막날+1이어도 허용이 되므로, 마지막날+1을 만드는 아이디어가 중요하다.\n다시풀라고해도 풀수있을지모르겠다.\n2가지방법으로 풀 수 있다.\ndfs로, 0부터 시작해서 포함안하는 경우와, 만일 포함가능하다면, 포함하는경우까지 생각해서 dfs를진행하고,\n마지막날+1까지 탐색했을때, 모든 경우의 수를 비교해 최대인 answer을 찾는다.\n##dfs코드##\ndef dfs(index,value=0): global answer if index==n: if answer\u0026lt;value: answer=value return dfs(index+1,value) # 포함안하고 그냥 넘어가는 경우 if index+arr[index][0]\u0026lt;=n: dfs(index+arr[index][0],value+arr[index][1]) #포함하면 그다음가능한날짜##dfs 사용 n=int(input())arr=[]answer=0 #정답값for _ in range(n): arr.append([* map(int,input().split())])dfs(0)print(answer) 2.Dp\ndp를 처음부터 해결해나간다는 고정관념을 버려야한다.\n앞서와 똑같은 이유로,마지막날을 해결하기 위해서 n+1의 dp 배열을 만든다.\ndp[i]=뒤에서부터 i번째인덱스까지의 최댓값으로 정의\n이후, n-1(arr의 마지막값) 부터 거꾸로 dp를 돌면서,\ni+arr[i]가 n보다 큰 경우에는 실행불가이므로 dp[i]=dp[i+1]이 된다,(에러방지)\n나머지 경우에는\ndp[i]=max{포함하지않는경우, 포함하는경우}\n즉\ndp[i]=max(dp[i+1] , dp[i+arr[i][0]]+arr[i][1] 이 된다.\ndp[0]이 정답이다.\n## dpn=int(input())arr=[]answer=0 #정답값for _ in range(n): arr.append([* map(int,input().split())]) dp=[0 for _ in range(n+1)] #마지막날에 1일이든다면, 해결하기위해서 n+1#dp[i]=뒤에서부터 i까지갔을때 최댓값 for i in range(n-1,-1,-1): if i+arr[i][0]\u0026gt;n: #만일 n을 넘는다면, 에러방지 dp[i]=dp[i+1] #더이상암것도못하므로, 전값으로대체 continue else:dp[i]=max(dp[i+1],dp[i+arr[i][0]]+arr[i][1]) #포함안하는 경우(전의값), 포함하는경우(상담이 끝나는날짜의 값+현재값의 가치)print(dp[0]) ","date":"12 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/067-%EB%B0%B1%EC%A4%80-14051-%ED%87%B4%EC%82%AC/","section":"Posts","summary":"https://fuckingcomputer.tistory.com/manage/newpost/?type=post\u0026returnURL=%2Fmanage%2Fposts%2F\nTISTORY나를 표현하는 블로그를 만들어보세요.www.tistory.com\n실버2인데 개어려움\n기본적으로, 마지막날+1이어도 허용이 되므로, 마지막날+1을 만드는 아이디어가 중요하다.\n다시풀라고해도 풀수있을지모르겠다.\n2가지방법으로 풀 수 있다.\ndfs로, 0부터 시작해서 포함안하는 경우와, 만일 포함가능하다면, 포함하는경우까지 생각해서 dfs를진행하고,\n마지막날+1까지 탐색했을때, 모든 경우의 수를 비교해 최대인 answer을 찾는다.\n##dfs코드##\ndef dfs(index,value=0): global answer if index==n: if answer\u003cvalue: answer=value return dfs(index+1,value) # 포함안하고 그냥 넘어가는 경우 if index+arr[index][0]\u003c=n: dfs(index+arr[index][0],value+arr[index][1]) #포함하면 그다음가능한날짜##dfs 사용 n=int(input())arr=[]answer=0 #정답값for _ in range(n): arr.append([* map(int,input().split())])dfs(0)print(answer) 2.Dp\ndp를 처음부터 해결해나간다는 고정관념을 버려야한다.\n앞서와 똑같은 이유로,마지막날을 해결하기 위해서 n+1의 dp 배열을 만든다.\n","title":"백준 14051 퇴사","type":"posts"},{"content":"https://www.acmicpc.net/problem/1759\n1759번: 암호 만들기첫째 줄에 두 정수 L, C가 주어진다. (3 ≤ L ≤ C ≤ 15) 다음 줄에는 C개의 문자들이 공백으로 구분되어 주어진다. 주어지는 문자들은 알파벳 소문자이며, 중복되는 것은 없다.www.acmicpc.net\n알고리즘생각만했는데 알고보니 브루트포스였다.\n먼져, 입력으로 문자열을 정렬해서 받고, 모음의 갯수를 비교할 set(aeiou)를 만들어준다.\ndfs를 통하여 모든 조합을 구하고, 만일 조합과 aeiou의 set이 1 이상이고 l-1 미만인 경우만\n(모음이 1개 이상이고, 모음이 아닌게 2개 이상일 경우):\n조합을 출력한다.\nl,c=map(int,input().split())aeiou=set(\u0026#39;aeiou\u0026#39;)alpha=sorted(input().split()) password=[]def dfs(start=0): if len(password)==l: set_password=set(password) if l-1\u0026gt;len(set_password \u0026amp; aeiou)\u0026gt;=1 : print(\u0026#39;\u0026#39;.join( password)) return for i in range(start,c): if alpha[i] not in password: password.append(alpha[i]) dfs(i+1) password.pop() returndfs() ##itertools사용코드##\nfrom itertools import combinationsl,c=map(int,input().split())aeiou=set(\u0026#39;aeiou\u0026#39;) alpha=sorted(input().split())password=combinations(alpha,l)for p in password: if l-1\u0026gt;len(set(p)\u0026amp;aeiou)\u0026gt;=1: print(\u0026#39;\u0026#39;.join(p)) 출력에 띄어쓰기가 없는걸 조심하자\n","date":"11 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/066-%EB%B0%B1%EC%A4%80-1759-%EC%95%94%ED%98%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/1759\n1759번: 암호 만들기첫째 줄에 두 정수 L, C가 주어진다. (3 ≤ L ≤ C ≤ 15) 다음 줄에는 C개의 문자들이 공백으로 구분되어 주어진다. 주어지는 문자들은 알파벳 소문자이며, 중복되는 것은 없다.www.acmicpc.net\n알고리즘생각만했는데 알고보니 브루트포스였다.\n먼져, 입력으로 문자열을 정렬해서 받고, 모음의 갯수를 비교할 set(aeiou)를 만들어준다.\ndfs를 통하여 모든 조합을 구하고, 만일 조합과 aeiou의 set이 1 이상이고 l-1 미만인 경우만\n(모음이 1개 이상이고, 모음이 아닌게 2개 이상일 경우):\n조합을 출력한다.\nl,c=map(int,input().split())aeiou=set('aeiou')alpha=sorted(input().split()) password=[]def dfs(start=0): if len(password)==l: set_password=set(password) if l-1\u003elen(set_password \u0026 aeiou)\u003e=1 : print(''.join( password)) return for i in range(start,c): if alpha[i] not in password: password.append(alpha[i]) dfs(i+1) password.pop() returndfs() ##itertools사용코드##\n","title":"백준 1759 암호 만들기","type":"posts"},{"content":"https://www.acmicpc.net/problem/6603\n6603번: 로또입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있다. 첫 번째 수는 k (6 \u0026lt; k \u0026lt; 13)이고, 다음 k개 수는 집합 S에 포함되는 수이다. S의 원소는 오름차순으로www.acmicpc.net\n그냥 구현하는문제인듯\n처음에한 뻘짓\n로또번호를 먼져 구하고, 그 다음에 모든경우의수 구하기\n머리가 나쁘면 손이고생함\nlotto_numbers=[]def make_lotto_numbers(start=0): if len(lotto_numbers)==k: make_lotto(lotto_numbers) return for i in range(start,k): if t_case[i] not in lotto_numbers: lotto_numbers.append(t_case[i]) make_lotto_numbers(start+1) lotto_numbers.pop() return lotto=[]def make_lotto(lotto_numbers,start=0): if len (lotto)==6: print(* lotto) return for i in range(start,k): if lotto_numbers[i] not in lotto: lotto.append(lotto_numbers[i]) make_lotto(lotto_numbers,i+1) lotto.pop() return while True: tmp=[*map(int,input().split())] if tmp[0]==0:exit() k,t_case=tmp[0],tmp[1:] make_lotto_numbers() print() 간단한 풀이\n그냥 처음부터 6자리 모든 경우의 수를 구하면 됨\n모듈을 쓴다면 더 간편\nlotto=[]def dfs(start=0): if len (lotto)==6: print(* lotto) return for i in range(start,len(s)): if s[i] not in lotto: lotto.append(s[i]) dfs(i+1) lotto.pop() while True: s=[*map(int,input().split())] if s[0]==0:exit() s=s[1:] dfs() print() 이터툴즈이용하기\nfrom itertools import combinationswhile True: s=[*map(int,input().split())] if s[0]==0:exit() s=s[1:] s=list(combinations(s,6)) for tmp in s: print(* tmp) print() 진짜 머리가모자라면 손이고생함\n","date":"11 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/065-%EB%B0%B1%EC%A4%80-6603-%EB%A1%9C%EB%98%90/","section":"Posts","summary":"https://www.acmicpc.net/problem/6603\n6603번: 로또입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있다. 첫 번째 수는 k (6 \u003c k \u003c 13)이고, 다음 k개 수는 집합 S에 포함되는 수이다. S의 원소는 오름차순으로www.acmicpc.net\n그냥 구현하는문제인듯\n처음에한 뻘짓\n로또번호를 먼져 구하고, 그 다음에 모든경우의수 구하기\n머리가 나쁘면 손이고생함\nlotto_numbers=[]def make_lotto_numbers(start=0): if len(lotto_numbers)==k: make_lotto(lotto_numbers) return for i in range(start,k): if t_case[i] not in lotto_numbers: lotto_numbers.append(t_case[i]) make_lotto_numbers(start+1) lotto_numbers.pop() return lotto=[]def make_lotto(lotto_numbers,start=0): if len (lotto)==6: print(* lotto) return for i in range(start,k): if lotto_numbers[i] not in lotto: lotto.append(lotto_numbers[i]) make_lotto(lotto_numbers,i+1) lotto.pop() return while True: tmp=[*map(int,input().split())] if tmp[0]==0:exit() k,t_case=tmp[0],tmp[1:] make_lotto_numbers() print() 간단한 풀이\n","title":"백준 6603 로또","type":"posts"},{"content":"https://www.acmicpc.net/problem/10971\n10971번: 외판원 순회 2첫째 줄에 도시의 수 N이 주어진다. (2 ≤ N ≤ 10) 다음 N개의 줄에는 비용 행렬이 주어진다. 각 행렬의 성분은 1,000,000 이하의 양의 정수이며, 갈 수 없는 경우는 0이 주어진다. W[i][j]는 도시 i에서 jwww.acmicpc.net\n몇문제안풀어봐서그런가 dfs개념이 너무 어려웠다.\n먼져, 입력을 받는다.\ndfs함수를 정의하는데, 이때 {\n아직 방문x \u0026amp; 현재까지의 값이 answer보다 작음\u0026amp; arr[가장최근도시][갈도시]가 0이 아닌 경우에만\ndfs를 수행하고, 아니면 무시한다.}\ndfstmp=[]def dfs(start,next,v=0): #시작도시, 최근방문도시, 지금까지의 value global answer #계속 업데이트할 정답값 if len (dfstmp)==n: if arr[next][start]!=0: #마지막도시에서 첫도시로 못가는경우 answer=min(answer,v+arr[next][start]) for i in range(n): if i not in dfstmp and arr[next][i]!=0 and v\u0026lt;answer: # 방문x \u0026amp; 갈수있는길 \u0026amp; 정답보다 현재까지의v가 작을때만 수행 dfstmp.append(i) dfs(start,i,v+arr[next][i]) dfstmp.pop() return n=int(input()) arr=[[*map(int,input().split())]for _ in range(n)]answer=1e+10 #초깃값 큰 수 for i in range(n): dfstmp.append(i) #각각의 도시를 시작으로 dfs수행 dfs(i,i) dfstmp.pop()print(answer) ","date":"11 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/064-%EB%B0%B1%EC%A4%80-10971-%EC%99%B8%ED%8C%90%EC%9B%90-%EC%88%9C%ED%9A%8C-2/","section":"Posts","summary":"https://www.acmicpc.net/problem/10971\n10971번: 외판원 순회 2첫째 줄에 도시의 수 N이 주어진다. (2 ≤ N ≤ 10) 다음 N개의 줄에는 비용 행렬이 주어진다. 각 행렬의 성분은 1,000,000 이하의 양의 정수이며, 갈 수 없는 경우는 0이 주어진다. W[i][j]는 도시 i에서 jwww.acmicpc.net\n몇문제안풀어봐서그런가 dfs개념이 너무 어려웠다.\n먼져, 입력을 받는다.\ndfs함수를 정의하는데, 이때 {\n아직 방문x \u0026 현재까지의 값이 answer보다 작음\u0026 arr[가장최근도시][갈도시]가 0이 아닌 경우에만\ndfs를 수행하고, 아니면 무시한다.}\ndfstmp=[]def dfs(start,next,v=0): #시작도시, 최근방문도시, 지금까지의 value global answer #계속 업데이트할 정답값 if len (dfstmp)==n: if arr[next][start]!=0: #마지막도시에서 첫도시로 못가는경우 answer=min(answer,v+arr[next][start]) for i in range(n): if i not in dfstmp and arr[next][i]!=0 and v\u003canswer: # 방문x \u0026 갈수있는길 \u0026 정답보다 현재까지의v가 작을때만 수행 dfstmp.append(i) dfs(start,i,v+arr[next][i]) dfstmp.pop() return n=int(input()) arr=[[*map(int,input().split())]for _ in range(n)]answer=1e+10 #초깃값 큰 수 for i in range(n): dfstmp.append(i) #각각의 도시를 시작으로 dfs수행 dfs(i,i) dfstmp.pop()print(answer)","title":"백준 10971 외판원 순회 2","type":"posts"},{"content":"https://www.acmicpc.net/problem/7576\n7576번: 토마토첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토www.acmicpc.net\n거의 처음으로풀어본 BFS문제라 좀 시행착오를 많이 겪었다.\n하다보면 나아지겠지\n먼져, 주어진 배열을 돌면서 stack에 원소가 1인 자릿값들을 넣는것부터 시작한다.\n토마토는 총 4방향으로 익기 시작하므로, moves=[[0,1],[0,-1],[1,0],[-1,0]] 를 선언해준다.\n이후 각각 BFS를 돌면서, 만일 원소가 -1이거나 1인 경우(이미 익은 경우) 를 제외하고, 계속 확장시킨다.\n더히상 갈데가 없을때까지 bfs를 돈 후, 다시 배열을 돌면서 0이 하나라도 있으면 -1을 리턴하면 된다.\n여기서 bfs의 level을 어떻게 구현해야하는지 몰라서 헤멨는데, 백준을 뒤져보다 좋은 방법을 발견했다.\n스택에 i,j값만 넣지 말고, 레벨을 같이 넣는 것이다.\n가장 처음엔 0단계이므로, i,j,0을 넣는다.\n이후 스택 각각의 원소들이 확장할때마다, level+1을 해 준다.\n다시 돌아가서, 배열에 0이 하나도없으면 마지막level을 출력한다.\n##코드##\nimport sysfrom collections import dequeinput=sys.stdin.readline def bfs(m,n,box,forbfs): moves=[[0,1],[0,-1],[1,0],[-1,0]] l=0 while forbfs: i,j,l=forbfs.popleft() for move in moves: tmp_i=i+move[0] tmp_j=j+move[1] if 0\u0026lt;=tmp_i\u0026lt;n and 0\u0026lt;=tmp_j\u0026lt;m: if box[tmp_i][tmp_j]==0: forbfs.append([tmp_i,tmp_j,l+1]) box[tmp_i][tmp_j]=1 for aa in range(n): for bb in range(m): if box[aa][bb]==0: return-1 return l m,n=map(int,input().split()) box=[[*map(int,input().strip().split())]for _ in range(n)]forbfs=deque() for i in range(n): for j in range(m): if box[i][j]==1: forbfs.append([i,j,0]) print(bfs(m,n,box,forbfs)) ","date":"9 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/063-%EB%B0%B1%EC%A4%80-7576-%ED%86%A0%EB%A7%88%ED%86%A0/","section":"Posts","summary":"https://www.acmicpc.net/problem/7576\n7576번: 토마토첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토www.acmicpc.net\n거의 처음으로풀어본 BFS문제라 좀 시행착오를 많이 겪었다.\n하다보면 나아지겠지\n먼져, 주어진 배열을 돌면서 stack에 원소가 1인 자릿값들을 넣는것부터 시작한다.\n토마토는 총 4방향으로 익기 시작하므로, moves=[[0,1],[0,-1],[1,0],[-1,0]] 를 선언해준다.\n이후 각각 BFS를 돌면서, 만일 원소가 -1이거나 1인 경우(이미 익은 경우) 를 제외하고, 계속 확장시킨다.\n","title":"백준 7576 토마토","type":"posts"},{"content":"https://www.acmicpc.net/problem/1003\n1003번: 피보나치 함수각 테스트 케이스마다 0이 출력되는 횟수와 1이 출력되는 횟수를 공백으로 구분해서 출력한다.www.acmicpc.net\ndp감찾을려고 풀어본 문제\n공책에 한번 써보면 쉽게풀림\n2차원 배열로 품\ndp[n]=[dp[n-1][0]+dp[n-2][0],dp[n-1][1]+dp[n-2][1]]\n##코드\ndp=[[0,0]for _ in range(41)]dp[0],dp[1]=[1,0],[0,1]for i in range(2,41): dp[i]=[dp[i-1][0]+dp[i-2][0],dp[i-1][1]+dp[i-2][1]] for _ in range(int(input())): print(* dp[int(input())]) ","date":"8 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/062-%EB%B0%B1%EC%A4%80-1003-%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98-%ED%95%A8%EC%88%98/","section":"Posts","summary":"https://www.acmicpc.net/problem/1003\n1003번: 피보나치 함수각 테스트 케이스마다 0이 출력되는 횟수와 1이 출력되는 횟수를 공백으로 구분해서 출력한다.www.acmicpc.net\ndp감찾을려고 풀어본 문제\n공책에 한번 써보면 쉽게풀림\n2차원 배열로 품\ndp[n]=[dp[n-1][0]+dp[n-2][0],dp[n-1][1]+dp[n-2][1]]\n##코드\ndp=[[0,0]for _ in range(41)]dp[0],dp[1]=[1,0],[0,1]for i in range(2,41): dp[i]=[dp[i-1][0]+dp[i-2][0],dp[i-1][1]+dp[i-2][1]] for _ in range(int(input())): print(* dp[int(input())])","title":"백준 1003 피보나치 함수","type":"posts"},{"content":"https://www.acmicpc.net/problem/10819\n10819번: 차이를 최대로첫째 줄에 N (3 ≤ N ≤ 8)이 주어진다. 둘째 줄에는 배열 A에 들어있는 정수가 주어진다. 배열에 들어있는 정수는 -100보다 크거나 같고, 100보다 작거나 같다.www.acmicpc.net\n오우야\n그냥 노가다문제이다.\n먼져, 배열에 들어있는 수로 만들수 있는, 길이가 n인 모든 배열을 구한다.\n그모든 배열에 대해서 |A[0] - A[1]| + |A[1] - A[2]| + \u0026hellip; + |A[N-2] - A[N-1]|를 구한다.\n최댓값을 찾는다.\n근데 거의 처음구현해봐서 좀 빡셌다.\n근데 노가다라서 어렵지는 않았다.\ndef dfs(arr): if len(answer_list)==n: count(answer_list) return for i in range(n): if not visited[i]: answer_list.append(arr[i]) visited[i]=True dfs(arr) answer_list.pop() visited[i]=False def count(arr): global answer tmp=0 for i in range(n-1): tmp += abs(arr[i]-arr[i+1]) if tmp\u0026gt;answer: answer=tmp return ###########################################################n=int(input()) arr=[*map(int,input().split())]##함수용answer_list=[] visited=[False for _ in range(n)]answer=0###dfs(arr)print(answer) ","date":"8 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/061-%EB%B0%B1%EC%A4%80-10819-%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EC%B5%9C%EB%8C%80%EB%A1%9C/","section":"Posts","summary":"https://www.acmicpc.net/problem/10819\n10819번: 차이를 최대로첫째 줄에 N (3 ≤ N ≤ 8)이 주어진다. 둘째 줄에는 배열 A에 들어있는 정수가 주어진다. 배열에 들어있는 정수는 -100보다 크거나 같고, 100보다 작거나 같다.www.acmicpc.net\n오우야\n그냥 노가다문제이다.\n먼져, 배열에 들어있는 수로 만들수 있는, 길이가 n인 모든 배열을 구한다.\n그모든 배열에 대해서 |A[0] - A[1]| + |A[1] - A[2]| + … + |A[N-2] - A[N-1]|를 구한다.\n최댓값을 찾는다.\n근데 거의 처음구현해봐서 좀 빡셌다.\n근데 노가다라서 어렵지는 않았다.\n","title":"백준 10819 차이를 최대로","type":"posts"},{"content":"https://www.acmicpc.net/problem/10972\n10972번: 다음 순열첫째 줄에 입력으로 주어진 순열의 다음에 오는 순열을 출력한다. 만약, 사전순으로 마지막에 오는 순열인 경우에는 -1을 출력한다.www.acmicpc.net\n실버3이지만 최근에 푼 문제중에 가장 어려웠다.\nc++에서는 이를 함수하나로 쉽게 구할 수 있다한다.\n도저희 모르겠어서 구글링했다.\n알고리즘은 이렇다.\n수열의 뒤에서부터, i \u0026gt;i-1인 경우를 찾는다.\ni-1을 x, i-2를 y라 정하자\n찾은 후, 다시 뒤에서부터 x보다 큰 값을 찾아 swap해준다.\n이후, y전까지의 배열은 그대로 놔두고, [:i]\ny부터 끝까지의 배열을 정렬한다. sorted([i:])\n이 둘을 합치면 된다.\n만일 i\u0026gt;i-1을 만족하는 i가 없다면, 이는 수열의 끝이다.\nn=int(input())arr=[* map(int,input().split())]for i in range(n-1,0,-1): if arr[i]\u0026lt;arr[i-1]:continue if arr[i]\u0026gt;arr[i-1]: for j in range(n-1,0,-1): if arr[j]\u0026gt;arr[i-1]: arr[i-1],arr[j]=arr[j],arr[i-1] answer_arr=arr[:i]+sorted(arr[i:]) print(* answer_arr) exit() print(-1) 솔직히 아직도 잘 모르겠다.\n다음에다시풀어봐야겠다.\n","date":"8 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/060-%EB%B0%B1%EC%A4%80-10972-%EB%8B%A4%EC%9D%8C-%EC%88%9C%EC%97%B4/","section":"Posts","summary":"https://www.acmicpc.net/problem/10972\n10972번: 다음 순열첫째 줄에 입력으로 주어진 순열의 다음에 오는 순열을 출력한다. 만약, 사전순으로 마지막에 오는 순열인 경우에는 -1을 출력한다.www.acmicpc.net\n실버3이지만 최근에 푼 문제중에 가장 어려웠다.\nc++에서는 이를 함수하나로 쉽게 구할 수 있다한다.\n도저희 모르겠어서 구글링했다.\n알고리즘은 이렇다.\n수열의 뒤에서부터, i \u003ei-1인 경우를 찾는다.\ni-1을 x, i-2를 y라 정하자\n찾은 후, 다시 뒤에서부터 x보다 큰 값을 찾아 swap해준다.\n이후, y전까지의 배열은 그대로 놔두고, [:i]\ny부터 끝까지의 배열을 정렬한다. sorted([i:])\n이 둘을 합치면 된다.\n","title":"백준 10972 다음 순열","type":"posts"},{"content":"https://www.acmicpc.net/problem/2812\n2812번: 크게 만들기N자리 숫자가 주어졌을 때, 여기서 숫자 K개를 지워서 얻을 수 있는 가장 큰 수를 구하는 프로그램을 작성하시오.www.acmicpc.net\n스택 알고리즘 강의를 들었던게 도움이 되었다.\n내 혼자힘으로 푼 몇안되는 골드문제중 1\n숫자를 문자 리스트로받아 하나씩 떨어뜨려놓는다. 2.반복문을 돌면서, 앞에부터 1개씩 스택에 추가한다.\n!!! 만일 추가할 숫자가 스택의 마지막(-1)보다 작다면, 스택마지막이 추가할 숫자보다 클때까지, 혹은 스택이 없을때까지, 혹은 k가 0일때까지 pop()해준다.\n총 k개까지 제거할 수 있으므로, pop할때마다 k를 1씩 빼주고 0이 되면 종료한다.!!!\n끝까지 다 돌았는데도 k가 남아있을수도 있다. 이러면 스택은 최소 비오름차순일 것이다. 그러므로 k번 pop을 해주면 된다.' 4.출력\n##\u0026lsquo;1\u0026rsquo;은 \u0026lsquo;2\u0026rsquo;보다 작으므로, 굳이 int변환을 해주지 않아도 계산이 가능하다##\n%%코드%%\nn,k =map(int,input().split())number=list(input())stack=[] for i in range(len(number)): tmp=number[i] while stack and stack[-1]\u0026lt;tmp and k\u0026gt;0: stack.pop() k-=1 if n==0:break stack.append(number[i])while k \u0026gt;0: stack.pop() k-=1print(\u0026#39;\u0026#39;.join(stack)) 뿌듯하다\n","date":"6 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/059-%EB%B0%B1%EC%A4%80-2812-%ED%81%AC%EA%B2%8C-%EB%A7%8C%EB%93%A4%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/2812\n2812번: 크게 만들기N자리 숫자가 주어졌을 때, 여기서 숫자 K개를 지워서 얻을 수 있는 가장 큰 수를 구하는 프로그램을 작성하시오.www.acmicpc.net\n스택 알고리즘 강의를 들었던게 도움이 되었다.\n내 혼자힘으로 푼 몇안되는 골드문제중 1\n숫자를 문자 리스트로받아 하나씩 떨어뜨려놓는다. 2.반복문을 돌면서, 앞에부터 1개씩 스택에 추가한다.\n!!! 만일 추가할 숫자가 스택의 마지막(-1)보다 작다면, 스택마지막이 추가할 숫자보다 클때까지, 혹은 스택이 없을때까지, 혹은 k가 0일때까지 pop()해준다.\n총 k개까지 제거할 수 있으므로, pop할때마다 k를 1씩 빼주고 0이 되면 종료한다.!!!\n","title":"백준 2812 크게 만들기","type":"posts"},{"content":"https://www.acmicpc.net/problem/15666\n15666번: N과 M (12)한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해www.acmicpc.net\n비슷한 문제가 너무 많아서 이것만 풀었다.\n지금 깨달았는데, 이것도 dfs문제인거 같다.\n풀이는따로안적어야지\nanswer=[]def mn12(start=0): if len(answer)==m: print(* answer) return for i in range(start,len(n_list)): answer.append(n_list[i]) mn12(i) answer.pop() n,m=map(int,input().split()) n_list=sorted(list(set([* map(int,input().split())])))mn12() ","date":"6 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/058-%EB%B0%B1%EC%A4%80-15666-n%EA%B3%BC-m-12/","section":"Posts","summary":"https://www.acmicpc.net/problem/15666\n15666번: N과 M (12)한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해www.acmicpc.net\n비슷한 문제가 너무 많아서 이것만 풀었다.\n지금 깨달았는데, 이것도 dfs문제인거 같다.\n풀이는따로안적어야지\nanswer=[]def mn12(start=0): if len(answer)==m: print(* answer) return for i in range(start,len(n_list)): answer.append(n_list[i]) mn12(i) answer.pop() n,m=map(int,input().split()) n_list=sorted(list(set([* map(int,input().split())])))mn12()","title":"백준 15666 N과 M (12)","type":"posts"},{"content":"https://www.acmicpc.net/problem/15652\n15652번: N과 M (4)한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해www.acmicpc.net\n설명할게있나?\nanswer=[]def nm4(start=1): if len(answer)==m: print (*answer) return for i in range(start,n+1): answer.append(i) nm4(i) answer.pop()n,m=map(int,input().split())nm4() ","date":"6 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/057-%EB%B0%B1%EC%A4%80-15652-n%EA%B3%BC-m-4/","section":"Posts","summary":"https://www.acmicpc.net/problem/15652\n15652번: N과 M (4)한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해www.acmicpc.net\n설명할게있나?\nanswer=[]def nm4(start=1): if len(answer)==m: print (*answer) return for i in range(start,n+1): answer.append(i) nm4(i) answer.pop()n,m=map(int,input().split())nm4()","title":"백준 15652 N과 M (4)","type":"posts"},{"content":"https://www.acmicpc.net/problem/6064\n6064번: 카잉 달력입력 데이터는 표준 입력을 사용한다. 입력은 T개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 입력 데이터의 수를 나타내는 정수 T가 주어진다. 각 테스트 데이터는 한 줄로 구성된다.www.acmicpc.net\n살짝 어려웠다.\nx의 값을 고정시켜놓는다는 아이디어가 중요하다.\nx는 m%x이므로, x에 아무리 m을 더해도 x값은 같다.\nx에 m을 계속 더하면서, x%n이 y%n(0일경우도대비)이 되는 x값을 구한다.\n만일 x가 m*n보다 커진다면, 이는 무한루프이므로 -1을 출력한다.\n##처음코드##\nfor _ in range(int(input())): m,n,x,y=map(int,input().split()) pivot=x%m if pivot==0:pivot=m tmp=pivot while True: a=tmp%n if not a: a=n if a==y: print(tmp) break tmp+=m if tmp\u0026gt;(m*n): print(-1) break ##함수사용 깔끔 코드##\ndef kain(m,n,x,y): while x\u0026lt;m*n: if x%n==y%n : print(x) return x+=m print(-1) return for _ in range(int(input())): m,n,x,y=map(int,input().split()) kain( m,n,x,y) 난웰케 머리가 나쁠까\n","date":"5 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/056-%EB%B0%B1%EC%A4%80-6064-%EC%B9%B4%EC%9E%89-%EB%8B%AC%EB%A0%A5/","section":"Posts","summary":"https://www.acmicpc.net/problem/6064\n6064번: 카잉 달력입력 데이터는 표준 입력을 사용한다. 입력은 T개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 입력 데이터의 수를 나타내는 정수 T가 주어진다. 각 테스트 데이터는 한 줄로 구성된다.www.acmicpc.net\n살짝 어려웠다.\nx의 값을 고정시켜놓는다는 아이디어가 중요하다.\nx는 m%x이므로, x에 아무리 m을 더해도 x값은 같다.\nx에 m을 계속 더하면서, x%n이 y%n(0일경우도대비)이 되는 x값을 구한다.\n만일 x가 m*n보다 커진다면, 이는 무한루프이므로 -1을 출력한다.\n##처음코드##\nfor _ in range(int(input())): m,n,x,y=map(int,input().split()) pivot=x%m if pivot==0:pivot=m tmp=pivot while True: a=tmp%n if not a: a=n if a==y: print(tmp) break tmp+=m if tmp\u003e(m*n): print(-1) break ##함수사용 깔끔 코드##\n","title":"백준 6064 카잉 달력","type":"posts"},{"content":"https://www.acmicpc.net/problem/1260\n1260번: DFS와 BFS첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사www.acmicpc.net\n문제 자체는 아무런트릭이 없지만 그래프 탐색 알고리즘을 먼져 공부해야 풀 수 있는 문제\n깊이 우선 탐색(BFS -Depth-First Search)\n은 말 그대로 깊이를 우선하는 탐색이다.\n재귀함수를 이용하여 구현하며, 가장 깊은곳까지 탐색한 후 더이상의 노드가 없을 때,\n그제서야 그 전 단계의 다른 노드를 탐색한다.\n구글링을 통해 그림을 찾아왔다.\n코드## # def dfs(graph,v,visited): visited[v]=True print(v,end=\u0026#39; \u0026#39;) for i in graph[v]: if not visited[i]: dfs(graph, i , visited) 너비 우선 탐색(BFS, Breadth-First Search)\nDFS가 끝을 보고서야 돌아왔다면, 얘는 순차적이다.시작 노드에서 방문할 수 있는 노드를(깊이1) 모두 방문한 뒤, 그 후에 깊이 1인 모든 노드에서 방문가능한 모든 노드를 방문한다.\n##코드##\nfrom collections import deque#deque를 안쓰고 pop(0)을 활용해도 됨 def bfs(graph, start,visited): q=deque([start]) visited[start]=True while q: v=q.popleft() print(v,end=\u0026#39;-\u0026gt;\u0026#39;) for i in graph[v]: if not visited[i]: q.append(i) visited[i]=True 이 개념들을 바탕으로, 입력받아서 그래프를 구현 후 풀어나가자\n##전체문제풀이##\ndef dfs(graph,v,visit1): visit1[v]=True print(v,end=\u0026#39; \u0026#39;) for i in graph[v]: if not visit1[i]: dfs(graph,i,visit1) def bfs(graph, v, visit2): q=[] visit2[v]=True q.append(v) while q: tmp=q.pop(0) print(tmp, end=\u0026#39; \u0026#39;) for i in graph[tmp]: if not visit2[i]: q.append(i) visit2[i]=True n,m,v=map(int,input().split()) graph=[[]for _ in range(n+1)]for _ in range(m): a,b=map(int,input().split()) graph[a].append(b) graph[b].append(a)for g in graph: g.sort() visit1=[False for _ in range(n+1)]visit2=visit1.copy()dfs(graph,v,visit1)print() bfs(graph,v,visit2) ","date":"4 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/055-%EB%B0%B1%EC%A4%80-1260-dfs%EC%99%80bfs/","section":"Posts","summary":"https://www.acmicpc.net/problem/1260\n1260번: DFS와 BFS첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사www.acmicpc.net\n문제 자체는 아무런트릭이 없지만 그래프 탐색 알고리즘을 먼져 공부해야 풀 수 있는 문제\n깊이 우선 탐색(BFS -Depth-First Search)\n은 말 그대로 깊이를 우선하는 탐색이다.\n재귀함수를 이용하여 구현하며, 가장 깊은곳까지 탐색한 후 더이상의 노드가 없을 때,\n","title":"백준 1260 DFS와BFS","type":"posts"},{"content":"https://www.acmicpc.net/problem/2565\n2565번: 전깃줄첫째 줄에는 두 전봇대 사이의 전깃줄의 개수가 주어진다. 전깃줄의 개수는 100 이하의 자연수이다. 둘째 줄부터 한 줄에 하나씩 전깃줄이 A전봇대와 연결되는 위치의 번호와 B전봇대와 연결되는www.acmicpc.net\n예전에 비슷한 문제를 읽어봐서 쉽게 풀 수 있었다.\n문제는 헷갈리지만, 이를 요약하면,\n\u0026lsquo;가장 긴 부분 증가수열을 A를 찾아라\u0026rsquo; 로 요약할 수 있다.\n이후 전체 전깃줄에서 A에 속하지 않은 전깃줄을 모두 잘라내면 되므로\n전체길이-가장 긴 부분증가수열의 길이 가 답이다\n%%코드%%\nn=int(input())elect=sorted([list(map(int,input().split()))for _ in range(n)]) dp=[1 for _ in range(n)]for i in range(1,n): for j in range(i): if elect[i][1]\u0026gt;elect[j][1]: tmp=dp[j]+1 if tmp\u0026gt;dp[i]: dp[i]=tmpmax_dp=max(dp)print(n-max_dp) 모르는 알고리즘이면 유추하느라 고생깨나 했을거같다.\n","date":"4 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/054-%EB%B0%B1%EC%A4%80-2565-%EC%A0%84%EA%B9%83%EC%A4%84/","section":"Posts","summary":"https://www.acmicpc.net/problem/2565\n2565번: 전깃줄첫째 줄에는 두 전봇대 사이의 전깃줄의 개수가 주어진다. 전깃줄의 개수는 100 이하의 자연수이다. 둘째 줄부터 한 줄에 하나씩 전깃줄이 A전봇대와 연결되는 위치의 번호와 B전봇대와 연결되는www.acmicpc.net\n예전에 비슷한 문제를 읽어봐서 쉽게 풀 수 있었다.\n문제는 헷갈리지만, 이를 요약하면,\n‘가장 긴 부분 증가수열을 A를 찾아라’ 로 요약할 수 있다.\n이후 전체 전깃줄에서 A에 속하지 않은 전깃줄을 모두 잘라내면 되므로\n전체길이-가장 긴 부분증가수열의 길이 가 답이다\n%%코드%%\nn=int(input())elect=sorted([list(map(int,input().split()))for _ in range(n)]) dp=[1 for _ in range(n)]for i in range(1,n): for j in range(i): if elect[i][1]\u003eelect[j][1]: tmp=dp[j]+1 if tmp\u003edp[i]: dp[i]=tmpmax_dp=max(dp)print(n-max_dp) 모르는 알고리즘이면 유추하느라 고생깨나 했을거같다.\n","title":"백준 2565 전깃줄","type":"posts"},{"content":"t=int(input())for _ in range(t): answer=0 n,m=map(int ,input().split()) for i in range(n,m+1): for j in str(i): if j==\u0026#39;0\u0026#39;: answer+=1 print(answer) https://www.acmicpc.net/problem/11170\n11170번: 0의 개수N부터 M까지의 수들을 종이에 적었을 때 종이에 적힌 0들을 세는 프로그램을 작성하라. 예를 들어, N, M이 각각 0, 10일 때 0을 세면 0에 하나, 10에 하나가 있으므로 답은 2이다.www.acmicpc.net\n브루트포스\n걍 0세서\n구하면됨\n끝\n##코드##\n","date":"3 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/053-%EB%B0%B1%EC%A4%80-11170-0%EC%9D%98-%EA%B0%9C%EC%88%98/","section":"Posts","summary":"t=int(input())for _ in range(t): answer=0 n,m=map(int ,input().split()) for i in range(n,m+1): for j in str(i): if j=='0': answer+=1 print(answer) https://www.acmicpc.net/problem/11170\n11170번: 0의 개수N부터 M까지의 수들을 종이에 적었을 때 종이에 적힌 0들을 세는 프로그램을 작성하라. 예를 들어, N, M이 각각 0, 10일 때 0을 세면 0에 하나, 10에 하나가 있으므로 답은 2이다.www.acmicpc.net\n브루트포스\n걍 0세서\n구하면됨\n끝\n##코드##\n","title":"백준 11170 0의 개수","type":"posts"},{"content":"https://www.acmicpc.net/problem/1748\n1748번: 수 이어 쓰기 1첫째 줄에 N(1 ≤ N ≤ 100,000,000)이 주어진다.www.acmicpc.net\n웬진모르겠지만 브루트포스에 섞여있어서,\n1부터 n까지 모든 수의 len(str())을 더해서 풀었는데, 시간초과가 났다.\n그래서방법을 갈구하다가,\n자리의 규칙을 찾아냈다.\n1+2+\u0026hellip;.+9=9 (10^0)(91)\n10+11\u0026hellip;..+9=180 (10^1)(92)\n101+102+\u0026hellip;..(999)=2700 (10^2)(93)\n예를 들어 n이 998이라면, 3자리이므로\n먼져 999까지의 모든 숫자를 더해준다 9+180+2700=2889\n여기서 999와 998의 차이를 구한다\n999 (10^3-1) - 998=1\n그후, 이 차이에 있는 숫자들은 모두 3자리이므로,\n999까지 모두 생각한 결과에서, 차이에 있는 숫자들의 갯수 * 3을 빼준다.\n2889-3*1=2886\n문제는 엄청 쉬워보이고 코드도 간단한데 구현하기 꽤 까다로웠다.\n##코드##\nn=input()ss=len(n) #몇자리수인지answer=0for i in range(ss): answer+= 9*(10**i)*(i+1)answer -= ((i+1)* ((10**ss-1)-int(n) ))print(answer) ","date":"2 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/052-%EB%B0%B1%EC%A4%80-1748-%EC%88%98-%EC%9D%B4%EC%96%B4%EC%93%B0%EA%B8%B0-1/","section":"Posts","summary":"https://www.acmicpc.net/problem/1748\n1748번: 수 이어 쓰기 1첫째 줄에 N(1 ≤ N ≤ 100,000,000)이 주어진다.www.acmicpc.net\n웬진모르겠지만 브루트포스에 섞여있어서,\n1부터 n까지 모든 수의 len(str())을 더해서 풀었는데, 시간초과가 났다.\n그래서방법을 갈구하다가,\n자리의 규칙을 찾아냈다.\n1+2+….+9=9 (10^0)(91)\n10+11…..+9=180 (10^1)(92)\n101+102+…..(999)=2700 (10^2)(93)\n예를 들어 n이 998이라면, 3자리이므로\n먼져 999까지의 모든 숫자를 더해준다 9+180+2700=2889\n여기서 999와 998의 차이를 구한다\n999 (10^3-1) - 998=1\n그후, 이 차이에 있는 숫자들은 모두 3자리이므로,\n999까지 모두 생각한 결과에서, 차이에 있는 숫자들의 갯수 * 3을 빼준다.\n","title":"백준 1748 수 이어쓰기 1","type":"posts"},{"content":"https://www.acmicpc.net/problem/1107\n1107번: 리모컨첫째 줄에 수빈이가 이동하려고 하는 채널 N (0 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 고장난 버튼의 개수 M (0 ≤ M ≤ 10)이 주어진다. 고장난 버튼이 있는 경우에는 셋째 줄에는 고장난 버튼www.acmicpc.net\n풀이방법이 도저히 생각이 안나서 힌트를 얻었더니 그냥 노가다문제였다.\n그냥 1부터 500000*2의 수까지 모두 돌면서, 만들수 있으면 n과의 거리를 구하고, 만들수없으면 넘어간다.\n앞의 조건 \u0026lsquo;고장난버튼이있는 경우에는 셋제줄에 고장난버튼이 주어진다\u0026rsquo; 때문에 고생했다.\n고장난버튼이 0일때 입력을 받으면 eof에러가 나므로, 이경우만 예외처리해주면 된다.\n노가다풀이\nn=int(input())m=int(input())if m: nobutton=input().split()else: nobutton=[]answer=abs(n-100) # +또는-만눌렀을때for i in range(1000001): flag=True press_cnt=0 for j in str(i): press_cnt+=1 if j in nobutton: flag=False break if flag: tmp=press_cnt+abs(n-i) if tmp\u0026lt;answer: answer=tmp print(answer) ","date":"2 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/051-%EB%B0%B1%EC%A4%80-1107-%EB%A6%AC%EB%AA%A8%EC%BB%A8/","section":"Posts","summary":"https://www.acmicpc.net/problem/1107\n1107번: 리모컨첫째 줄에 수빈이가 이동하려고 하는 채널 N (0 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 고장난 버튼의 개수 M (0 ≤ M ≤ 10)이 주어진다. 고장난 버튼이 있는 경우에는 셋째 줄에는 고장난 버튼www.acmicpc.net\n풀이방법이 도저히 생각이 안나서 힌트를 얻었더니 그냥 노가다문제였다.\n그냥 1부터 500000*2의 수까지 모두 돌면서, 만들수 있으면 n과의 거리를 구하고, 만들수없으면 넘어간다.\n앞의 조건 ‘고장난버튼이있는 경우에는 셋제줄에 고장난버튼이 주어진다’ 때문에 고생했다.\n고장난버튼이 0일때 입력을 받으면 eof에러가 나므로, 이경우만 예외처리해주면 된다.\n","title":"백준 1107 리모컨","type":"posts"},{"content":"https://www.acmicpc.net/problem/1476\n1476번: 날짜 계산준규가 사는 나라는 우리가 사용하는 연도와 다른 방식을 이용한다. 준규가 사는 나라에서는 수 3개를 이용해서 연도를 나타낸다. 각각의 수는 지구, 태양, 그리고 달을 나타낸다. 지구를 나타www.acmicpc.net\n문제는 긴데 요약하면 너무 간단하다.\nyear%15가 e, year%28이 s, year%19가 m인\nyear을 찾으면된다.\n만일 15의 배수이면, 0이되므로 0일때만 예외처리해주면됨\n","date":"2 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/050-%EB%B0%B1%EC%A4%80-1476-%EB%82%A0%EC%A7%9C-%EA%B3%84%EC%82%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/1476\n1476번: 날짜 계산준규가 사는 나라는 우리가 사용하는 연도와 다른 방식을 이용한다. 준규가 사는 나라에서는 수 3개를 이용해서 연도를 나타낸다. 각각의 수는 지구, 태양, 그리고 달을 나타낸다. 지구를 나타www.acmicpc.net\n문제는 긴데 요약하면 너무 간단하다.\nyear%15가 e, year%28이 s, year%19가 m인\nyear을 찾으면된다.\n만일 15의 배수이면, 0이되므로 0일때만 예외처리해주면됨\n","title":"백준 1476 날짜 계산","type":"posts"},{"content":"https://www.acmicpc.net/problem/3085\n3085번: 사탕 게임예제 3의 경우 4번 행의 Y와 C를 바꾸면 사탕 네 개를 먹을 수 있다.www.acmicpc.net\n와 나는 알고리즘생각하느라 잠을못잘뻔했는데\n알고보니 그딴거없이 하나씩 다 해보는게 브루트포스였다.\n먼져, 2차원 보드에서 연속되는 값의 최댓값을 셀 함수 check(board) 를 만든다.\n모든 행, 열 을 비교하며 가장 많이 연속되는 값의 개수를 리턴한다..\n메인에서\n우리는 굳이 4방향을 다 바꿀 필요 없이, 각 원소의 오른쪽과 아래만 바꾸면 모든 경우를 다 시험 가능하다.\n각 행, 열의 마지막 원소는 바꿀 필요 없으므로 예외처리를 한다.\n먼져, 모든 원소의 오른쪽 원소와 바꿔가며, 그 상태에서의 연속되는 수를 센다.(check)\n그 후 바꾼 원소를 제자리에 돌려놓는는다.\n그 후, 모든 원소의 아래쪽 원소와 바꿔가며, 그 상테에서 연속되는 수를 센다.(check)\n그 후 제자리에 놓는다.\n각각의 check값을 리턴하여, 가장 큰 수를 구하면 된다.\n1시간정도 계속 코드를 짰는데 계속 틀렸습니다가 나오는 바람에, 너무 화가나서 정답 코드랑 비교해봤더니,\n변수이름 하나에 r이 붙어있었다. 그거잡을려고 40분동안 뻘짓한듯 하다.\n##코드##\ndef check(board): n=len(board) answer=1 for i in range (n): cnt=1 for j in range(1,n): #열탐색 if board[i][j]==board[i][j-1]: cnt+=1 else: cnt=1 if cnt\u0026gt;answer: answer=cnt cnt=1 for j in range(1,n): #행탐색 if board[j][i]==board[j-1][i]: cnt+=1 else: cnt=1 if cnt\u0026gt;answer: answer=cnt return answer n=int(input())board=[] board=[list(input())for _ in range(n)]real_ans=0for i in range(n): for j in range(n): if i+1\u0026lt;n: #i+1에서 오류방지 # 오른쪽과 바꿈 board[i][j],board[i+1][j]=board[i+1][j],board[i][j] temp=check(board) # 탐색하여 최댓값구함 if temp\u0026gt;real_ans: real_ans=temp #이전최댓값보다 크면 갱신 board[i][j],board[i+1][j]=board[i+1][j],board[i][j] #보드 초기화 if j+1\u0026lt;n: #j+1에서 오류방지 #아랫쪽과 바꿈 board[i][j],board[i][j+1]=board[i][j+1],board[i][j] temp=check(board) if temp\u0026gt;real_ans: #위와 동일 real_ans=temp real_ans=max(check(board),real_ans) board[i][j],board[i][j+1]=board[i][j+1],board[i][j] #보드 초기화 print(real_ans) ","date":"1 April 2022","externalUrl":null,"permalink":"/posts/2022-tistory/049-%EB%B0%B1%EC%A4%80-3085-%EC%82%AC%ED%83%95-%EA%B2%8C%EC%9E%84/","section":"Posts","summary":"https://www.acmicpc.net/problem/3085\n3085번: 사탕 게임예제 3의 경우 4번 행의 Y와 C를 바꾸면 사탕 네 개를 먹을 수 있다.www.acmicpc.net\n와 나는 알고리즘생각하느라 잠을못잘뻔했는데\n알고보니 그딴거없이 하나씩 다 해보는게 브루트포스였다.\n먼져, 2차원 보드에서 연속되는 값의 최댓값을 셀 함수 check(board) 를 만든다.\n모든 행, 열 을 비교하며 가장 많이 연속되는 값의 개수를 리턴한다..\n메인에서\n우리는 굳이 4방향을 다 바꿀 필요 없이, 각 원소의 오른쪽과 아래만 바꾸면 모든 경우를 다 시험 가능하다.\n각 행, 열의 마지막 원소는 바꿀 필요 없으므로 예외처리를 한다.\n","title":"백준 3085 사탕 게임","type":"posts"},{"content":"https://www.acmicpc.net/problem/1920\n1920번: 수 찾기첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들www.acmicpc.net\n진짜 기초개념인 이분탐색 문제였는데, 정신없는상테해서하느라 30분은 헤멘 듯 하다.\n따로 설명은 안하겠다.\ndef bs(i,a,start,end): if start\u0026gt;end: return 0 pivot=(end+start)//2 if i == a[pivot]: return 1 if i \u0026lt;a[pivot]: return bs(i,a,start,pivot-1) else: return bs(i,a,pivot+1,end)n=int(input()) a=sorted([* map(int,input().split())])m=int(input()) mm=[* map(int,input().split())]for i in mm: print(bs(i,a,0,len(a)-1)) ","date":"31 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/048-%EB%B0%B1%EC%A4%80-1920-%EC%88%98-%EC%B0%BE%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/1920\n1920번: 수 찾기첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들www.acmicpc.net\n진짜 기초개념인 이분탐색 문제였는데, 정신없는상테해서하느라 30분은 헤멘 듯 하다.\n따로 설명은 안하겠다.\ndef bs(i,a,start,end): if start\u003eend: return 0 pivot=(end+start)//2 if i == a[pivot]: return 1 if i \u003ca[pivot]: return bs(i,a,start,pivot-1) else: return bs(i,a,pivot+1,end)n=int(input()) a=sorted([* map(int,input().split())])m=int(input()) mm=[* map(int,input().split())]for i in mm: print(bs(i,a,0,len(a)-1))","title":"백준 1920 수 찾기","type":"posts"},{"content":"https://www.acmicpc.net/problem/17404\n17404번: RGB거리 2첫째 줄에 집의 수 N(2 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나www.acmicpc.net\n처음에 엄청 헤멨다.\n처음 집을 칠하는 경우가 3가지니 3가지 경우의 수를 두고 dp를 구하는 것까진 생각했으나, 코드가 너무 복잡했다.\n처음 집 0을 칠하는 경우를 예로 들면\n두번째 집에서는 0을 칠하지 못하므로 이를 제외한경우의 수를 구하는데\u0026hellip;\u0026hellip;\u0026hellip;..\n\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\n그냥 너무 복잡했다.\n그러다가 나온 결론이\n엄청 큰 값을 줘버려서 0 이외의 다른 숫자들은 선택조차 못하게 하자! 였다.\ndp[i][n]=max(dp[i-1][1],dp[i-1][2])+cost[i][n]인데,\n만일 처음에 0번집 외의 집을 엄청 큰 값을 주면,\n두번째 1,2 번 집에서는 무조건 0번집에서 시작한 결과로 이어질 것이고,\n두번째의 0번집에선 첫번째의 엄청 큰 값을 계승할 수밖에 없으므로\n자연스럽게 3번째 집을 구할 때 제외되게 된다.\n그리고 마지막 집은 0과 달라야 하므로, 마지막 집의 1,2중 최솟값이 리스트에 추가된다.\n이를 0, 1,2 각각에 대해 구하고, 이 모든 최솟값 중 최솟값을 구하면, 답이 나온다.\nn=int(input())cost=[]for _ in range(n): cost.append([* map(int,input().split())])answer=[]for i in range(3): n_list=[0,1,2] n_list.remove(i) dp=[[0,0,0] for _ in range(n)] dp[0][i]=cost[0][i] for num in n_list: dp[0][num]=5000 for j in range(1,len(dp)): dp[j][0]=min(dp[j-1][1],dp[j-1][2])+cost[j][0] dp[j][1]=min(dp[j-1][0],dp[j-1][2])+cost[j][1] dp[j][2]=min(dp[j-1][0],dp[j-1][1])+cost[j][2] a=min(dp[n-1][n_list[0]],dp[n-1][n_list[1]]) answer.append(a) print(min(answer)) 숏코딩 봤는데 은근 정상적인 코드인데 이해할수가 없다.\n다음에 생각나면 한번 다시봐야겠다.\n숏코딩 코드(내꺼아님)\nn=int(input())l=[list(map(int,input().split())) for _ in range(n)]s=9**9 for k in range(3): d=[[0,0,0]for _ in range(n)] d[-1]=list(l[-1]) d[-1][k]=9**9 for i in range(n-1): x=n-2-i for j in range(3): d[x][j]=l[x][j]+min(d[x+1][j-1],d[x+1][j-2]) s=min(s,d[0][k]) print(s) ","date":"30 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/047-%EB%B0%B1%EC%A4%80-17404-rgb%EA%B1%B0%EB%A6%AC-2/","section":"Posts","summary":"https://www.acmicpc.net/problem/17404\n17404번: RGB거리 2첫째 줄에 집의 수 N(2 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나www.acmicpc.net\n처음에 엄청 헤멨다.\n처음 집을 칠하는 경우가 3가지니 3가지 경우의 수를 두고 dp를 구하는 것까진 생각했으나, 코드가 너무 복잡했다.\n처음 집 0을 칠하는 경우를 예로 들면\n두번째 집에서는 0을 칠하지 못하므로 이를 제외한경우의 수를 구하는데………..\n","title":"백준 17404 RGB거리 2","type":"posts"},{"content":"https://www.acmicpc.net/problem/13398\n13398번: 연속합 2첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.www.acmicpc.net\n도저히 나의 편협한 사고방식으로는 이해되지 않아\n힌트를 얻었다. 생각보다 간단하다.\n먼져,수열 a를 받는다.\n그후, 2차원 dp배열을 생성한다.\ndp[i][0]은 i를 포함하는 수열 중 가장 큰 수열의 합이다.\ndp[i][1]은 i를 포함하는 수열에서 하나를 뺐을 때 가장 큰 수열의 합이다.\ndp[i][0]은 그냥 그 전의값이 0보다 클 때만 더하고, 아니면 a[i]를 반환하면 된다.\ndp[i][0]= max(dp[i-1].0)+a[i]\ndp[i][1]은 그 전의값+a[i]와, 아무것도안뺐을때의값(dp[i-1][0] 즉, i를 제외했을 경우)\n중 큰 수를 반환하면 된다.\ndp[i][1]=max(dp[i-1][1]+a[i] , dp[i-1][0] )\n이와 같은 점화식이 나온다.\n마지막에 계속 틀렸습니다가 나와서 봤더니, 수열에서 수를 무조건 하나이상 선택해야 한단다.\n그런데, 모든 경우에서 수를 하나 이상 선택하지만, a[0]이 음수일때\ndp[0][1]은 수를 선택하지 않으므로 0이 된다.\n이를 방지하기 위해 모든 반복이 끝난후 dp[0][1]을 a[1]로 업데이트시켜줬더니 풀렸다.\n코드\nn=int(input())a=[*map (int,input().split())] #수열 dp=[[0,0] for _ in range(len(a))] #[0]은 아무것도안뺏을때, [1]은 뭐하나뺐을때dp[0][0]=a[0] for i in range(1,len(dp)): if dp[i-1][0]\u0026gt;=0: dp[i][0]=dp[i-1][0]+a[i] else: dp[i][0]=a[i] dp[i][1]=max(dp[i-1][1]+a[i],dp[i-1][0]) dp[0][1]=a[0] #맨 처음에 하나이상 선택해야 하므로 조정answer=max([max(x) for x in dp]) print(answer) ","date":"29 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/046-%EB%B0%B1%EC%A4%80-13398-%EC%97%B0%EC%86%8D%ED%95%A9-2/","section":"Posts","summary":"https://www.acmicpc.net/problem/13398\n13398번: 연속합 2첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.www.acmicpc.net\n도저히 나의 편협한 사고방식으로는 이해되지 않아\n힌트를 얻었다. 생각보다 간단하다.\n먼져,수열 a를 받는다.\n그후, 2차원 dp배열을 생성한다.\ndp[i][0]은 i를 포함하는 수열 중 가장 큰 수열의 합이다.\ndp[i][1]은 i를 포함하는 수열에서 하나를 뺐을 때 가장 큰 수열의 합이다.\ndp[i][0]은 그냥 그 전의값이 0보다 클 때만 더하고, 아니면 a[i]를 반환하면 된다.\n","title":"백준 13398 연속합 2","type":"posts"},{"content":"https://www.acmicpc.net/problem/11054\n11054번: 가장 긴 바이토닉 부분 수열첫째 줄에 수열 A의 크기 N이 주어지고, 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ N ≤ 1,000, 1 ≤ Ai ≤ 1,000)www.acmicpc.net\n엄청끙끙대다가 접근방식을 봤는데 생각보다 너무 간단했다.\n수열 a의 모든 원소 i에 대해서,\na[i]까지의 증가하는 수열과a[j]까지의 감소하는 수열을 구한다.\n이후 이 둘을 더하고 1을빼주면 된다.(a가 중복되므로)\n나는 그냥 반복을 2번돌려서 풀었다.\n증가하는 수열을 모두 구한 후에, 감소하는 수열을 모두 구했다.\nn=int(input())a=[* map(int,input().split())]dp=[[1,1] for _ in range(n)] for i in range(1,len(dp)): max_len=0 for j in range(i): if a[j]\u0026lt;a[i]: max_len=max(max_len,dp[j][0]) dp[i][0]+=max_lenfor q in range(n-1,-1,-1): min_len=0 for k in range(n-1,q,-1): if a[q]\u0026gt;a[k]: min_len=max(min_len,dp[k][1]) dp[q][1]+=min_lenanswer=1 for dd in dp: answer=max(answer,sum(dd))print(answer-1) 정답을 훑어보는 도중, 반복문 한번으로 깔끔하게 처리하는 방식을 발견했다.\n먼저 a를 뒤집은 b를 만들고,\n한번 반복할때마다 a,b를 통해 dp_up,dp_down (i 까지의 증가수열, 감소수열) 을 업데이트한뒤\n정답 answer[i]에\ndp_up[i] 와 dp_down[n-i-1] (얘는 뒤집었으므로 원래위치는 n-i-1이 된다)\n를 더한 값을 업데이트시켜주면 된다.\nn=int(input())a=[* map(int,input().split())]b=a[::-1]dp_up=[1 for _ in range(n)] dp_down=dp_up.copy()for i in range(n): for j in range(i): if a[i]\u0026gt;a[j]: dp_up[i]=max(dp_up[i],dp_up[j]+1) if b[i]\u0026gt;b[j]: dp_down[i]=max(dp_down[i],dp_down[j]+1) answer=[dp_up[x]+dp_down[n-x-1] for x in range(n)]print(max(answer)-1) ","date":"28 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/045-%EB%B0%B1%EC%A4%80-11054-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EB%B0%94%EC%9D%B4%ED%86%A0%EB%8B%89-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4/","section":"Posts","summary":"https://www.acmicpc.net/problem/11054\n11054번: 가장 긴 바이토닉 부분 수열첫째 줄에 수열 A의 크기 N이 주어지고, 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ N ≤ 1,000, 1 ≤ Ai ≤ 1,000)www.acmicpc.net\n엄청끙끙대다가 접근방식을 봤는데 생각보다 너무 간단했다.\n수열 a의 모든 원소 i에 대해서,\na[i]까지의 증가하는 수열과a[j]까지의 감소하는 수열을 구한다.\n이후 이 둘을 더하고 1을빼주면 된다.(a가 중복되므로)\n나는 그냥 반복을 2번돌려서 풀었다.\n증가하는 수열을 모두 구한 후에, 감소하는 수열을 모두 구했다.\n","title":"백준 11054 가장 긴 바이토닉 부분 수열","type":"posts"},{"content":"https://www.acmicpc.net/problem/4358\n4358번: 생태학프로그램은 여러 줄로 이루어져 있으며, 한 줄에 하나의 나무 종 이름이 주어진다. 어떤 종 이름도 30글자를 넘지 않으며, 입력에는 최대 10,000개의 종이 주어지고 최대 1,000,000그루의 나무가 주어www.acmicpc.net\n이전에 이럴때 dict를활용하는 문제를 풀어서 쉬웠다.\n각각의 입력을 받고, dict에있으면 값에 1을 더해주고, 없으면 값을 1로 생성한다.\n입력이없으면 멈춘다.\n사전순을 위해 dict를 정렬해주고, print하면된다. (value는 모든 value의 합으로 나눠주고 100을 곱해준다)\n단하나까다로웠던게, 파이썬에서는 round가 정확하지 않다.\n따라서 :.4f를 이용해서 출력해야한다.\nimport sysd=dict()while True: i=sys.stdin.readline().strip() if not i: break if i in d : d[i]+=1 else: d[i]=1 all=sum(d.values())d=sorted(d.items())for i, j in d: print(f\u0026#39;{i} {j/all*100:.4f}\u0026#39;) ","date":"28 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/044-%EB%B0%B1%EC%A4%80-4358-%EC%83%9D%ED%83%9C%ED%95%99/","section":"Posts","summary":"https://www.acmicpc.net/problem/4358\n4358번: 생태학프로그램은 여러 줄로 이루어져 있으며, 한 줄에 하나의 나무 종 이름이 주어진다. 어떤 종 이름도 30글자를 넘지 않으며, 입력에는 최대 10,000개의 종이 주어지고 최대 1,000,000그루의 나무가 주어www.acmicpc.net\n이전에 이럴때 dict를활용하는 문제를 풀어서 쉬웠다.\n각각의 입력을 받고, dict에있으면 값에 1을 더해주고, 없으면 값을 1로 생성한다.\n입력이없으면 멈춘다.\n사전순을 위해 dict를 정렬해주고, print하면된다. (value는 모든 value의 합으로 나눠주고 100을 곱해준다)\n단하나까다로웠던게, 파이썬에서는 round가 정확하지 않다.\n따라서 :.4f를 이용해서 출력해야한다.\nimport sysd=dict()while True: i=sys.stdin.readline().strip() if not i: break if i in d : d[i]+=1 else: d[i]=1 all=sum(d.values())d=sorted(d.items())for i, j in d: print(f'{i} {j/all*100:.4f}')","title":"백준 4358 생태학","type":"posts"},{"content":"https://www.acmicpc.net/problem/11047\n11047번: 동전 0첫째 줄에 N과 K가 주어진다. (1 ≤ N ≤ 10, 1 ≤ K ≤ 100,000,000) 둘째 줄부터 N개의 줄에 동전의 가치 Ai가 오름차순으로 주어진다. (1 ≤ Ai ≤ 1,000,000, A1 = 1, i ≥ 2인 경우에 Ai는 Ai-1의 배수)www.acmicpc.net\ndp만풀다가 푸니깐 힐링된다.\nk=돈\ncnt=횟수\n먼져, 동전의 가치를 리스트로 저장해준다.\n그리고, 가장 큰 동전이 k보다 작을때까지 pop해준다.\n그후, (k//가장큰 동전) 을 cnt에 추가해주고 (동전을 몇번사용했는가)\n(k%가장큰동전)을 k로 선언한다. (동전을 최대한 쓰고 남은 돈)\n이를 0이될때까지 반복한다.\n(1, i ≥ 2인 경우에 Ai는 Ai-1의 배수) 라는 조건덕분에 그리디알고리즘이 성립된다.\n만일 a[3]을 쓸수 있다면, 어떠한 경우에도 a[2]를 쓰는 것보다 효율적이다.\n코드\nn,k=map(int,input().split())money=[]for _ in range(n): money.append(int(input()))cnt=0while k \u0026gt; 0: while money[-1] \u0026gt; k: money.pop() a,k=divmod(k,money[-1]) cnt+=a print(cnt) ","date":"27 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/043-%EB%B0%B1%EC%A4%80-11047-%EB%8F%99%EC%A0%84-0/","section":"Posts","summary":"https://www.acmicpc.net/problem/11047\n11047번: 동전 0첫째 줄에 N과 K가 주어진다. (1 ≤ N ≤ 10, 1 ≤ K ≤ 100,000,000) 둘째 줄부터 N개의 줄에 동전의 가치 Ai가 오름차순으로 주어진다. (1 ≤ Ai ≤ 1,000,000, A1 = 1, i ≥ 2인 경우에 Ai는 Ai-1의 배수)www.acmicpc.net\ndp만풀다가 푸니깐 힐링된다.\nk=돈\ncnt=횟수\n먼져, 동전의 가치를 리스트로 저장해준다.\n그리고, 가장 큰 동전이 k보다 작을때까지 pop해준다.\n그후, (k//가장큰 동전) 을 cnt에 추가해주고 (동전을 몇번사용했는가)\n(k%가장큰동전)을 k로 선언한다. (동전을 최대한 쓰고 남은 돈)\n","title":"백준 11047 동전 0","type":"posts"},{"content":"https://www.acmicpc.net/problem/2156\n2156번: 포도주 시식효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규www.acmicpc.net\n어려웠다.\ng[i]는 포도주의 양\ndp[i] 는 i를 마지막으로 마셨을 때 최대인 포도주값이다.\n맨 처음에 당연히 i-3+i-1과 i-2를 비교하면 된다고 생각했지만,\n사실 i-4+i-1이 i-3+i-1보다 클 수 있다.\nex)\nColumn 1 Column 2 7 2 2 1 1 2 2 1 (마지막 잔을 안 마시는 경우) 8 질문을 찾아보다가 https://raejoonee.tistory.com/15를 보고 반례를 찾을 수 있었다.\n점화식은\ndp[i]=max(dp[i-3]+dp[i-1]. dp[i-2],dp[i-4]+dp[i-1])+g[i]가 된다.\n마지막에는 마지막잔을 마시는경우, 안마시는 경우(그전의2개) 밖에 없으므로\ndp[n],dp[n-1]을 비교해주면 된다.\n코드\nn=int(input())g=[0]for _ in range(n): g.append(int(input()))if n==1: print(g[1])elif n==2: print(g[1]+g[2])elif n==3: print(max(max(g[1],g[2])+g[3],g[1]+g[2]))else: dp=g.copy() dp[2]+=dp[1] dp[3]+=max(dp[1],g[2]) for i in range(4,n+1): dp[i]=max(dp[i-3]+g[i-1],dp[i-2],dp[i-4]+g[i-1])+g[i] print(max(dp[n-1],dp[n])) ","date":"27 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/042-%EB%B0%B1%EC%A4%80-2156-%ED%8F%AC%EB%8F%84%EC%A3%BC-%EC%8B%9C%EC%8B%9D/","section":"Posts","summary":"https://www.acmicpc.net/problem/2156\n2156번: 포도주 시식효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규www.acmicpc.net\n어려웠다.\ng[i]는 포도주의 양\ndp[i] 는 i를 마지막으로 마셨을 때 최대인 포도주값이다.\n맨 처음에 당연히 i-3+i-1과 i-2를 비교하면 된다고 생각했지만,\n사실 i-4+i-1이 i-3+i-1보다 클 수 있다.\nex)\nColumn 1 Column 2 7 2 2 1 1 2 2 1 (마지막 잔을 안 마시는 경우) 8 질문을 찾아보다가 https://raejoonee.tistory.com/15를 보고 반례를 찾을 수 있었다.\n","title":"백준 2156 포도주 시식","type":"posts"},{"content":"ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nhtml을 열면 링크를 통해 들어갈 수 있다.\n그 후 문제의 html을 분석해보면\n의 주석을을 발결할 수 있고\n각각 base64로 디코딩하면\n문제의정답은\n알려줄수없다.\n하지만 힌트는 있다.\n힌트는 https://startlink.io/ 에 있는 ㅈㅎㅂㅎ\n가 된다.\nㅈㅎㅂㅎ는 전화번호였다.\n맨밑에전화번호를입력하면된다.\n","date":"26 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/041-%EB%B0%B1%EC%A4%80-12096-%EB%84%8C%EC%84%BC%EC%8A%A4/","section":"Posts","summary":"ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nhtml을 열면 링크를 통해 들어갈 수 있다.\n그 후 문제의 html을 분석해보면\n의 주석을을 발결할 수 있고\n각각 base64로 디코딩하면\n문제의정답은\n알려줄수없다.\n하지만 힌트는 있다.\n힌트는 https://startlink.io/ 에 있는 ㅈㅎㅂㅎ\n가 된다.\nㅈㅎㅂㅎ는 전화번호였다.\n맨밑에전화번호를입력하면된다.\n","title":"백준 12096 (넌센스)","type":"posts"},{"content":"https://www.acmicpc.net/problem/1990\n1990번: 소수인팰린드롬151은 소수이면서 동시에 팰린드롬이기 때문에 소수인 팰린드롬이다. 팰린드롬이란 앞으로 읽어나 뒤로 읽으나 같은 수를 말한다. 예를 들어 1234는 앞으로 읽으면 1234지만, 뒤로 읽으면 4321이 되www.acmicpc.net\n진짜 씨발 ㅈ같은 문제였다.\n처음에는 \u0026lsquo;골드치고 웰케 쉽지\u0026rsquo; 했는데 역시나 함정이 숨어있었다.\n가장 먼저, 체로 풀었는데 시간초과가 났다.\n그래서 a부터 b까지의 모든 펠린드롬을 n 구하고, 이를 int(n**0.5)+1 까지의 모든 수로 나눠서 판별했다.\n역시 시간초과가 났다.\n여기서부터 별짓을 다해봤다.\n짝수중에 소수는 2밖에 없으므로, 2로 먼져 나눠보고 3부터 2씩 증가시켜서 나눠보고,\n함수형으로도 명령형으로도 해보고\n뭔짓을해도 시간초과가나서 인터넷에 답을 찾아봤더니\n11의 배수 판정법은 홀수자리 숫자의합-짝수자리 숫자의 합이 11의 배수면 된단다.\n그래서 모든 길이가 짝수인 펠린드롭은 11의 배수란다.\n즉 최대길이가 100,000,000인데, 10,000,000을 넘는 펠린드롭 수는 없다.\n최대 펠린드롭수\n진짜 어? 이거 개쉬운데 하고 하나만 풀고 잘려했는데\n억울해서 잠을 설쳤다.\n코드\ndef sosu(x): if x%2==0:return for ii in range(3,int(x**0.5)+2): if x%ii==0: return print(x) a,b=map(int,input().split())if b\u0026gt;9999997: b=9999997 for i in range(a,b+1): if str(i)==str(i)[::-1]: sosu(i)print(-1) ","date":"26 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/040-%EB%B0%B1%EC%A4%80-1990-%EC%86%8C%EC%88%98%EC%9D%B8%ED%8E%A0%EB%A6%B0%EB%93%9C%EB%A1%AC/","section":"Posts","summary":"https://www.acmicpc.net/problem/1990\n1990번: 소수인팰린드롬151은 소수이면서 동시에 팰린드롬이기 때문에 소수인 팰린드롬이다. 팰린드롬이란 앞으로 읽어나 뒤로 읽으나 같은 수를 말한다. 예를 들어 1234는 앞으로 읽으면 1234지만, 뒤로 읽으면 4321이 되www.acmicpc.net\n진짜 씨발 ㅈ같은 문제였다.\n처음에는 ‘골드치고 웰케 쉽지’ 했는데 역시나 함정이 숨어있었다.\n가장 먼저, 체로 풀었는데 시간초과가 났다.\n그래서 a부터 b까지의 모든 펠린드롬을 n 구하고, 이를 int(n**0.5)+1 까지의 모든 수로 나눠서 판별했다.\n역시 시간초과가 났다.\n여기서부터 별짓을 다해봤다.\n짝수중에 소수는 2밖에 없으므로, 2로 먼져 나눠보고 3부터 2씩 증가시켜서 나눠보고,\n","title":"백준 1990 소수인펠린드롬","type":"posts"},{"content":"https://www.acmicpc.net/problem/11722\n11722번: 가장 긴 감소하는 부분 수열수열 A가 주어졌을 때, 가장 긴 감소하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 30, 10, 20, 20, 10} 인 경우에 가장 긴 감소하는 부분 수열은 A = {10, 30, 10, 20, 20, 10}www.acmicpc.net\n\u0026lsquo;가장 긴 증가하는 부분 수열\u0026rsquo; 의 반대로만 하면 된다.\nn=int(input())a=[* map(int,input().split())]dp=[1 for x in range(n)] for i in range(1,len(dp)): max_len=0 for j in range(i): if a[i]\u0026lt;a[j]: max_len=max(max_len, dp[j]) dp[i]+=max_len print(max(dp)) ","date":"25 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/039-%EB%B0%B1%EC%A4%80-11722-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EA%B0%90%EC%86%8C%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4/","section":"Posts","summary":"https://www.acmicpc.net/problem/11722\n11722번: 가장 긴 감소하는 부분 수열수열 A가 주어졌을 때, 가장 긴 감소하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 30, 10, 20, 20, 10} 인 경우에 가장 긴 감소하는 부분 수열은 A = {10, 30, 10, 20, 20, 10}www.acmicpc.net\n‘가장 긴 증가하는 부분 수열’ 의 반대로만 하면 된다.\nn=int(input())a=[* map(int,input().split())]dp=[1 for x in range(n)] for i in range(1,len(dp)): max_len=0 for j in range(i): if a[i]\u003ca[j]: max_len=max(max_len, dp[j]) dp[i]+=max_len print(max(dp))","title":"백준 11722 가장 긴 감소하는 부분 수열","type":"posts"},{"content":"https://www.acmicpc.net/problem/11055\n11055번: 가장 큰 증가 부분 수열수열 A가 주어졌을 때, 그 수열의 증가 부분 수열 중에서 합이 가장 큰 것을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 인 경우에 합이 가장 큰 증가 부분 수www.acmicpc.net\n역시 dp\n수열 A를 받는다.\nA를 베껴 dp를 만든다.\ndp[i]=A[i]를 끝으로 하는 수열의 가장 긴 길이이다.\nA[i]부터 왼쪽으로 탐색하면서\nA[i]보다 작은 값들중 가장 큰 A[?]를 구하고\ndp[?]에다가 a[i]를 더한 값이 dp[i]이다.\n이후 가장 큰 dp[?]를 구한다.\nn=int(input())a=[* map(int,input().split())]dp=a.copy() for i in range(1,len(dp)): max_cnt=0 for j in range(len(a[:i])): if a[j]\u0026lt;a[i]: max_cnt=max(max_cnt,dp[j]) dp[i]=dp[i]+max_cntprint(max(dp)) ","date":"25 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/038-%EB%B0%B1%EC%A4%80-11055-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%A6%9D%EA%B0%80-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4/","section":"Posts","summary":"https://www.acmicpc.net/problem/11055\n11055번: 가장 큰 증가 부분 수열수열 A가 주어졌을 때, 그 수열의 증가 부분 수열 중에서 합이 가장 큰 것을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 인 경우에 합이 가장 큰 증가 부분 수www.acmicpc.net\n역시 dp\n수열 A를 받는다.\nA를 베껴 dp를 만든다.\ndp[i]=A[i]를 끝으로 하는 수열의 가장 긴 길이이다.\nA[i]부터 왼쪽으로 탐색하면서\nA[i]보다 작은 값들중 가장 큰 A[?]를 구하고\n","title":"백준 11055 가장 큰 증가 부분 수열","type":"posts"},{"content":"https://www.acmicpc.net/problem/1932\n1932번: 정수 삼각형첫째 줄에 삼각형의 크기 n(1 ≤ n ≤ 500)이 주어지고, 둘째 줄부터 n+1번째 줄까지 정수 삼각형이 주어진다.www.acmicpc.net\n처음보는 구조라 당황했는데 dp개념만 알면 간단하다\n좀만 들여다보면 규칙이 나온다\n점화식\ndp[i][j]=\nj가 0이라면: dp[i][j]+=dp[i-1][j]\nj가 마지막수라면 : dp[i][j] += dp[i-1][j-1]\nelse: dp[i][j] += max(dp[i-1][j-1]. dp[i-1][j])\n마지막에 최댓값만 구해주면 된다\nn=int(input())dp=[[* map(int,input().split())] for _ in range(n)] for i in range(1,len(dp)): for j in range(len(dp[i])): if j==0: dp[i][j]+=dp[i-1][j] elif j==len(dp[i])-1: dp[i][j]+= dp[i-1][j-1] else: dp[i][j] += max(dp[i-1][j-1],dp[i-1][j])print(max(dp[n-1])) ","date":"25 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/037-%EB%B0%B1%EC%A4%80-1932-%EC%A0%95%EC%88%98-%EC%82%BC%EA%B0%81%ED%98%95/","section":"Posts","summary":"https://www.acmicpc.net/problem/1932\n1932번: 정수 삼각형첫째 줄에 삼각형의 크기 n(1 ≤ n ≤ 500)이 주어지고, 둘째 줄부터 n+1번째 줄까지 정수 삼각형이 주어진다.www.acmicpc.net\n처음보는 구조라 당황했는데 dp개념만 알면 간단하다\n좀만 들여다보면 규칙이 나온다\n점화식\ndp[i][j]=\nj가 0이라면: dp[i][j]+=dp[i-1][j]\nj가 마지막수라면 : dp[i][j] += dp[i-1][j-1]\nelse: dp[i][j] += max(dp[i-1][j-1]. dp[i-1][j])\n마지막에 최댓값만 구해주면 된다\nn=int(input())dp=[[* map(int,input().split())] for _ in range(n)] for i in range(1,len(dp)): for j in range(len(dp[i])): if j==0: dp[i][j]+=dp[i-1][j] elif j==len(dp[i])-1: dp[i][j]+= dp[i-1][j-1] else: dp[i][j] += max(dp[i-1][j-1],dp[i-1][j])print(max(dp[n-1]))","title":"백준 1932 정수 삼각형","type":"posts"},{"content":"https://www.acmicpc.net/problem/9465\n9465번: 스티커첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫째 줄에는 n (1 ≤ n ≤ 100,000)이 주어진다. 다음 두 줄에는 n개의 정수가 주어지며, 각 정수는 그 위치에 해당하는 스티커의www.acmicpc.net\n이번엔 세로로 dp를 돌려봤다.\ndp[0][i]=i번째의 위에 있는 스티커를 뗄 때의 최댓값\ndp[0][i]=i번째의 아래 있는 스티커를 뗄 때의 최댓값\n여기서 dp[0][i]를 뗄러면 dp[1][i-1]의 스티커를 뗐거나, dp[?][j-1]을 아예안 똈거나 둘 중하나이다.\n그럼 dp[?][i-2]의 최댓값과, dp[1][i-1]을 비교하여 이중 최댓값에 스티커의 값을 더해주면 된다.\n그런데 생각을 해보자\n만일 우리가 dp[0][i-2]를 뗐다면, 당연히 dp[1][i-1]을 떼고 그다음 dp[0][i]를 뗐을 것이다.\ndp[1][i-1]이 음수가 아닌 이상 이 값은 당연히 dp[0][i-2]보다 크므로,\n실질적으로 dp[1][i-1]과 dp[1][i-2]의 값중 큰 값만 비교하면 된다.\n이를 점화식으로 정리하면\ndp[0][i]=max(dp[1][i-1],dp[1][i-2])+ dp[0][i](해당 스티커값)\ndp[0][i]=max(dp[0][i-1],dp[0][i-2])+ dp[1][i](해당 스티커값)\n이 된다.\n코드\n처음에는 스티커의 가격과 dp배열을 따로 구성했으나, 다른 풀이를 보고 깔끔하게 바꿨다.\n첫 풀이\nt=int(input())for _ in range(t): n=int(input()) sticker=[] sticker.append([0]+(list(map(int,input().split())))) sticker.append([0]+(list(map(int,input().split())))) dp=[[0,0] for _ in range(n+1)] dp[1]=[sticker[0][1],sticker[1][1]] #dp[i][j]는 j로 끝났을때 점수의 최댓값 for i in range(2,len(dp)): dp[i][0]=max(dp[i-1][1],max(dp[i-2]))+sticker[0][i] dp[i][1]=max(dp[i-1][0],max(dp[i-2]))+sticker[1][i] print(max(dp[n])) 깔끔한 풀이\nfor _ in range(int(input())): n=int(input()) dp=[[0]+[* map(int,input().split())] for _ in range(2)] for i in range(2,n+1): max_0=max(dp[1][i-1],dp[1][i-2]) max_1=max(dp[0][i-1],dp[0][i-2]) dp[0][i]+=max_0 dp[1][i]+=max_1 print(max(dp[0][n],dp[1][n])) ","date":"25 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/036-%EB%B0%B1%EC%A4%80-9465-%EC%8A%A4%ED%8B%B0%EC%BB%A4/","section":"Posts","summary":"https://www.acmicpc.net/problem/9465\n9465번: 스티커첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫째 줄에는 n (1 ≤ n ≤ 100,000)이 주어진다. 다음 두 줄에는 n개의 정수가 주어지며, 각 정수는 그 위치에 해당하는 스티커의www.acmicpc.net\n이번엔 세로로 dp를 돌려봤다.\ndp[0][i]=i번째의 위에 있는 스티커를 뗄 때의 최댓값\ndp[0][i]=i번째의 아래 있는 스티커를 뗄 때의 최댓값\n여기서 dp[0][i]를 뗄러면 dp[1][i-1]의 스티커를 뗐거나, dp[?][j-1]을 아예안 똈거나 둘 중하나이다.\n그럼 dp[?][i-2]의 최댓값과, dp[1][i-1]을 비교하여 이중 최댓값에 스티커의 값을 더해주면 된다.\n","title":"백준 9465 스티커","type":"posts"},{"content":"https://www.acmicpc.net/problem/1309\n1309번: 동물원첫째 줄에 우리의 크기 N(1≤N≤100,000)이 주어진다.www.acmicpc.net\n역시 식만 잘 잡으면 됨\n칸이 2개 있으므로, 총 3가지 경우가 있음\n안넣는경우, 왼쪽, 오른쪽\n이를 dp[?][0],dp[?][0],dp[?][0] 로 정의함\n맨 첫줄은 다 한가지 경우만 있으므로 1임\n여서생각을 해보면\n맨 마지막줄에 아무것도 안넣을려면, 그 전에 어케됬든 상관 x\ndp[i][0]=sum(dp[i-1])\n왼쪽에 넣을려면, 그 전줄에 안넣거나 오른쪽에 넣어야됨\ndp[i][0]=dp[i-1][0]+dp[i-1][2]\n오른쪽에 넣을러면, 안넣거나 왼쪽\ndp[i][0]=dp[i-1][0]+dp[i-1][1]\n메손실을 방지하기위해 더할때마다 %9901을 하고,\ndp[i]를 모두 더해주면 됨\n여기서 dp[i][1]과 dp[i][2]는 모두 같으므로\n한번만해도 된다.\nn=int(input())m=9901dp=[[0,0,0] for _ in range(n+1)]dp[1]=[1,1,1] #안채울경우, 왼쪽에채울경우, 오른쪽에 채울 경우for i in range(2,len(dp)): dp[i][0]=sum(dp[i-1])%m dp[i][1]=dp[i][2]=sum(dp[i-1][:2])%mprint(sum(dp[n])%m) ","date":"24 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/035-%EB%B0%B1%EC%A4%80-1309-%EB%8F%99%EB%AC%BC%EC%9B%90/","section":"Posts","summary":"https://www.acmicpc.net/problem/1309\n1309번: 동물원첫째 줄에 우리의 크기 N(1≤N≤100,000)이 주어진다.www.acmicpc.net\n역시 식만 잘 잡으면 됨\n칸이 2개 있으므로, 총 3가지 경우가 있음\n안넣는경우, 왼쪽, 오른쪽\n이를 dp[?][0],dp[?][0],dp[?][0] 로 정의함\n맨 첫줄은 다 한가지 경우만 있으므로 1임\n여서생각을 해보면\n맨 마지막줄에 아무것도 안넣을려면, 그 전에 어케됬든 상관 x\ndp[i][0]=sum(dp[i-1])\n왼쪽에 넣을려면, 그 전줄에 안넣거나 오른쪽에 넣어야됨\ndp[i][0]=dp[i-1][0]+dp[i-1][2]\n오른쪽에 넣을러면, 안넣거나 왼쪽\ndp[i][0]=dp[i-1][0]+dp[i-1][1]\n메손실을 방지하기위해 더할때마다 %9901을 하고,\ndp[i]를 모두 더해주면 됨\n여기서 dp[i][1]과 dp[i][2]는 모두 같으므로\n","title":"백준 1309 동물원","type":"posts"},{"content":"https://www.acmicpc.net/problem/15988\n15988번: 1, 2, 3 더하기 3각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다.www.acmicpc.net\n이전의 1,2,3더하기와 완전히 같은 문제다.\n다른점이 있다면 dp의 길이가 1000001이라는 건데,\n짜피 계산은 컴퓨터가하니 알 바 아니고\ndp를 구할때마다 %m (문제조건)\n만 해주면 쉽게 풀린다.\nt=int(input())m=1000000009dp=[0 for _ in range(1000001)] #dp[i]=i자리 수를 1,2,3의 합으로 나타내는 방법dp[1]=1dp[2]=2dp[3]=4for i in range(4,len(dp)): dp[i]=(dp[i-1]+dp[i-2]+dp[i-3])%mfor _ in range(t): print(dp[int(input())]) ","date":"24 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/034-%EB%B0%B1%EC%A4%80-15998-123-%EB%8D%94%ED%95%98%EA%B8%B0-3/","section":"Posts","summary":"https://www.acmicpc.net/problem/15988\n15988번: 1, 2, 3 더하기 3각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다.www.acmicpc.net\n이전의 1,2,3더하기와 완전히 같은 문제다.\n다른점이 있다면 dp의 길이가 1000001이라는 건데,\n짜피 계산은 컴퓨터가하니 알 바 아니고\ndp를 구할때마다 %m (문제조건)\n만 해주면 쉽게 풀린다.\nt=int(input())m=1000000009dp=[0 for _ in range(1000001)] #dp[i]=i자리 수를 1,2,3의 합으로 나타내는 방법dp[1]=1dp[2]=2dp[3]=4for i in range(4,len(dp)): dp[i]=(dp[i-1]+dp[i-2]+dp[i-3])%mfor _ in range(t): print(dp[int(input())])","title":"백준 15998 1,2,3 더하기 3","type":"posts"},{"content":"https://www.acmicpc.net/problem/1149\n1149번: RGB거리첫째 줄에 집의 수 N(2 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나www.acmicpc.net\ndp 문제는 일단 식만 세우면 해결되는듯하다. 식세우기가어렵지만..\n먼져 cost로 각 집을 칠하는 값을 구해놓는다.\n그후 dp를 돌릴 배열을 선언한다.\ndp[1]은 아무 제약이 없으므로, cost[1]과 같다.\ndp[i][j]는 i번째의 집을 j색(0,1,2)으로 칠할 때의 최소비용을 뜻한다.\n우리는 두 집을 같은 색으로 칠할 수 없으므로,\n만일 마지막 집을 0으로 칠했다면, 막전 집은 1 또는 2여야만 한다.\nj=0이라면\n그러므로 dp[i-1][1], dp[i-1][2] 의 최솟값에 cost[i][0]을 더해준 것이 마지막을 0으로 칠했을 때의 최솟값이 된다.\n우리가 구하는 것은 dp[i]의 최솟값이므로, min()을 통해 구한다.\nDP\nColumn 1 Column 2 Column 3 0 1 2 cost[1][0] cost[1][1] cost[1][2] min(dp[1][1]+cost[2][1],dp[1][2]+cost[2][1]) min(dp[1][0]+cost[2][1],dp[1][2]+cost[2][1]) min(dp[1][0]+cost[2][1],dp[1][1]+cost[2][1]) n=int(input())cost=[[-1,-1,-1]]dp=[[0]*3 for _ in range(n+1)] ##dp[i][j]=i번째의 집을 j색으로 칠할때의 최소비용for _ in range(n): cost.append(list(map(int,input().split())))dp[1]=cost[1] for i in range(2,n+1): for j in range(3): if j==0: dp[i][j]=min(dp[i-1][1]+cost[i][j],dp[i-1][2]+cost[i][j]) elif j==1: dp[i][j]=min(dp[i-1][0]+cost[i][j],dp[i-1][2]+cost[i][j]) elif j==2: dp[i][j]=min(dp[i-1][0]+cost[i][j],dp[i-1][1]+cost[i][j]) print(min(dp[n])) ","date":"24 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/033-%EB%B0%B1%EC%A4%80-1149-rgb%EA%B1%B0%EB%A6%AC/","section":"Posts","summary":"https://www.acmicpc.net/problem/1149\n1149번: RGB거리첫째 줄에 집의 수 N(2 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나www.acmicpc.net\ndp 문제는 일단 식만 세우면 해결되는듯하다. 식세우기가어렵지만..\n먼져 cost로 각 집을 칠하는 값을 구해놓는다.\n그후 dp를 돌릴 배열을 선언한다.\ndp[1]은 아무 제약이 없으므로, cost[1]과 같다.\ndp[i][j]는 i번째의 집을 j색(0,1,2)으로 칠할 때의 최소비용을 뜻한다.\n우리는 두 집을 같은 색으로 칠할 수 없으므로,\n","title":"백준 1149 RGB거리","type":"posts"},{"content":"https://www.acmicpc.net/problem/2225\n2225번: 합분해첫째 줄에 답을 1,000,000,000으로 나눈 나머지를 출력한다.www.acmicpc.net\n살짝 꼬아서 낸 dp문제 원리는 오르막수와 같음\ndp[i][j]는 i개의 정수를 더해서 j가 되는 경우의 수로 정의한다.\n먼져, 직관적으로 0은 제외히고\n모든 dp[1][j]와 dp[i][0]는 모두 1이라는걸 알 수 있다. (한가지경우박에 없음)\n3,5를 예로 들면,\ndp[3][5]는 3개를 더해서 5가 되는 경우의 수이다.\n이는 dp[2][0] (마지막에 5를 더하는 경우)\ndp[2][1](마지막에 5-1을 더하는 경우)\ndp[2][2](마지막에 5-2를 더하는 경우)\ndp[2][3](마지막에 5-3을더하는 경우)\ndp[2][4])마지막에 5-4를 더하는 경우)\ndp[2][5](마지막에 5-5를 더하는 경우)\n의 합이라고 볼 수 있다.\n이를 점화식으로 쓰면\ndp[i][j]=dp[i-1][0]+dp[i-1][1]\u0026hellip;\u0026hellip; dp[i-1][j-1]+dp[i-1][j] 를 만들 수 있다.\n%%오르막 수와 같이\ndp[i-1][0]+dp[i-1][1]\u0026hellip;\u0026hellip; dp[i-1][j-1]를 dp[i][j-1]로 치환할 수 있으므로\ndp[i][j]=dp[i-1][j]+dp[i][j-1] 로 간단하게 만들 수 있다.%%\nn,k=map(int,input().split())dp=[[0]*(n+1) for _ in range(k+1)]dp[1]=[1]*(n+1) for i in range(2,k+1): for j in range(n+1): if j==0: dp[i][j]=1 continue dp[i][j]=dp[i-1][j]%1000000000+dp[i][j-1]%1000000000 print(dp[k][n]%1000000000) ","date":"24 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/032-%EB%B0%B1%EC%A4%80-2225-%ED%95%A9%EB%B6%84%ED%95%B4/","section":"Posts","summary":"https://www.acmicpc.net/problem/2225\n2225번: 합분해첫째 줄에 답을 1,000,000,000으로 나눈 나머지를 출력한다.www.acmicpc.net\n살짝 꼬아서 낸 dp문제 원리는 오르막수와 같음\ndp[i][j]는 i개의 정수를 더해서 j가 되는 경우의 수로 정의한다.\n먼져, 직관적으로 0은 제외히고\n모든 dp[1][j]와 dp[i][0]는 모두 1이라는걸 알 수 있다. (한가지경우박에 없음)\n3,5를 예로 들면,\ndp[3][5]는 3개를 더해서 5가 되는 경우의 수이다.\n이는 dp[2][0] (마지막에 5를 더하는 경우)\ndp[2][1](마지막에 5-1을 더하는 경우)\ndp[2][2](마지막에 5-2를 더하는 경우)\ndp[2][3](마지막에 5-3을더하는 경우)\ndp[2][4])마지막에 5-4를 더하는 경우)\n","title":"백준 2225 합분해","type":"posts"},{"content":"https://www.acmicpc.net/problem/11057\n11057번: 오르막 수오르막 수는 수의 자리가 오름차순을 이루는 수를 말한다. 이때, 인접한 수가 같아도 오름차순으로 친다. 예를 들어, 2234와 3678, 11119는 오르막 수이지만, 2232, 3676, 91111은 오르막 수가 아니다. 수www.acmicpc.net\n10884 쉬운 계단수에서 한단계 진화한 문제다.\n2차원 배열을 생성하여 풀었다.\ndp[i][j]는 길이가 i인 끝자리가 j로 끝나는 오르막 수의 개수를 의미한다.\n첫번째는 0이니깐 비워놓고,\n모든 한자리수는 오르막수이기 때문에 dp[1][0~9] 는 모두 1이다.\n2자리수부터가 문제인데,\ndp[2][0]은 00 하나밖에 존재할 수 없다.\ndp[2][1]은 01,11 2개가 존재할 수 있고,\ndp[2][2]는 02,12,22 3개가 존재 할 수 있다.\n3자리수부터는\ndp[3][0]=1 (000)\ndp[3][1]=3 (001,011,111)\ndp[3][2]=6 이다(002,012,112,022,122,222)\n즉 이를 직관적으로 이해해보면,\ni자리의 끝이 j로 끝나는 수는, i-1자리의 0,1, 2, 3 \u0026hellip;\u0026hellip; j 로 끝나는 수들의 합이 된다.\n이를 점화식으로 세워보면\n**dp[i][j]= sum(dp[i-1][0]+dp[i-1][1]+dp[i-1][2]\u0026hellip;.**dp[i-1][j-1]+dp[i-1][j])가 된다는 것을 알 수 있다.\n%%\n여기서 한 단계만 더 나아간다면,\ndp[i][j-1] 즉, i자리를 가진 끝이 j-1로 끝나는, dp[i][j]의 왼쪽에 있는 수는\n위에있는 점화식을 통하여\ndp[i][j-1]= sum(dp[i-1][0]+dp[i-1][1]+dp[i-1][2]\u0026hellip;.dp[i-1][j-1]) 임을 알 수 있다.\n**그러므로:**dp[i][j]= sum(dp[i-1][0]+dp[i-1][1]+dp[i-1][2]\u0026hellip;.dp[i-1][j-1]+dp[i-1][j])\n에서 sum(dp[i-1][0]+dp[i-1][1]+dp[i-1][2]\u0026hellip;.dp[i-1][j-1]) 까지를 dp[i][j-1]로 치환하면\ndp[i-1][j]만 남으므로\ndp[i][j] = dp[i-1][j]+dp[i][j-1]임을 알 수 있다.\nn=int(input())dp=[[0]*10 for _ in range(n+1)]dp[1]=[1]*10for i in range(2,n+1): for j in range(0,10): if j==0: dp[i][j]=1 continue dp[i][j]=dp[i-1][j]+dp[i][j-1] print(sum(dp[n])%10007) ","date":"24 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/031-%EB%B0%B1%EC%A4%80-11057-%EC%98%A4%EB%A5%B4%EB%A7%89-%EC%88%98/","section":"Posts","summary":"https://www.acmicpc.net/problem/11057\n11057번: 오르막 수오르막 수는 수의 자리가 오름차순을 이루는 수를 말한다. 이때, 인접한 수가 같아도 오름차순으로 친다. 예를 들어, 2234와 3678, 11119는 오르막 수이지만, 2232, 3676, 91111은 오르막 수가 아니다. 수www.acmicpc.net\n10884 쉬운 계단수에서 한단계 진화한 문제다.\n2차원 배열을 생성하여 풀었다.\ndp[i][j]는 길이가 i인 끝자리가 j로 끝나는 오르막 수의 개수를 의미한다.\n첫번째는 0이니깐 비워놓고,\n모든 한자리수는 오르막수이기 때문에 dp[1][0~9] 는 모두 1이다.\n2자리수부터가 문제인데,\ndp[2][0]은 00 하나밖에 존재할 수 없다.\n","title":"백준 11057 오르막 수","type":"posts"},{"content":"https://www.acmicpc.net/problem/1699\n1699번: 제곱수의 합어떤 자연수 N은 그보다 작거나 같은 제곱수들의 합으로 나타낼 수 있다. 예를 들어 11=32+12+12(3개 항)이다. 이런 표현방법은 여러 가지가 될 수 있는데, 11의 경우 11=22+22+12+12+12(5개 항)도 가능하다www.acmicpc.net\n의외로 정말 어려웠던 문제였다.\n푼 방법\n먼저, n까지의 제곱수를 구현해 놓는다.\n그다음 n까지의 리스트를 선언한다.\n반복문을 통해 리스트를 돌면서,\n만일 i가 제곱수이면, 값에 1을 넣는다.\n만일 i가 제곱수가 아니면, i보다 작은 모든 제곱수에 대하여\n(i-제곱수) + 1 을 구하고, ## 제곱수j에서 i를 만드는 최솟값 + 제곱수는 항상 1\n이중 min값을 구한다.\n이후 리스트의 n 번째 원소를 출력한다.\nn=int(input())squared_num=[x*x for x in range(1, int(n**0.5)+1)] #제곱수들 l=[x for x in range(n+1)] #입력값들for i in range(1,len(l)): min_list=[i] for j in squared_num: if i\u0026lt;j: break if i==j: min_list.append(1) break min_list.append(1+l[i-j]) l[i]=min(min_list) print(l[n]) 추가로 더 깔끔한 코드\nn=int(input())d=[0]*(n+1)d[1]=1for i in range(2,n+1): d[i]=d[i-1]+1 index=2 l=[d[i]] while True: under = i-(index*index) ## i와 제곱수의 차이 if under\u0026lt;0: break ## d[under은] 제곱수와의 차잇값을 만들수있는 최소갯수 # 제곱수는 항상 1번이면 만드므로 1더해주기 l.append(d[under]+1) index+=1 #다음 제곱수 d[i]=min(l)print(d[n]) 파이썬에서 풀면 시간을 엄청나게 잡아먹고,\n파이파이에서풀면 메모리를 엄청나게 잡아먹는다.\n","date":"22 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/030-%EB%B0%B1%EC%A4%80-1699-%EC%A0%9C%EA%B3%B1%EC%88%98%EC%9D%98-%ED%95%A9/","section":"Posts","summary":"https://www.acmicpc.net/problem/1699\n1699번: 제곱수의 합어떤 자연수 N은 그보다 작거나 같은 제곱수들의 합으로 나타낼 수 있다. 예를 들어 11=32+12+12(3개 항)이다. 이런 표현방법은 여러 가지가 될 수 있는데, 11의 경우 11=22+22+12+12+12(5개 항)도 가능하다www.acmicpc.net\n의외로 정말 어려웠던 문제였다.\n푼 방법\n먼저, n까지의 제곱수를 구현해 놓는다.\n그다음 n까지의 리스트를 선언한다.\n반복문을 통해 리스트를 돌면서,\n만일 i가 제곱수이면, 값에 1을 넣는다.\n만일 i가 제곱수가 아니면, i보다 작은 모든 제곱수에 대하여\n(i-제곱수) + 1 을 구하고, ## 제곱수j에서 i를 만드는 최솟값 + 제곱수는 항상 1\n","title":"백준 1699 제곱수의 합","type":"posts"},{"content":"https://www.acmicpc.net/problem/1912\n1912번: 연속합첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.www.acmicpc.net\n그냥 평범한 dp\nn[i]는 n[i]를 끝값으로 하는 가장 큰 수\n%%점화식%%\nn[i]={\nn[i-1]\u0026lt;=0 -\u0026gt; n[i]\nn[i-1]\u0026gt;0 -\u0026gt; n[i]+n[i-1]\n}\n만약 그전수가 0보다 작다면, 더해봤자 손해이므로 그냥 n[i]반환\n0보다 크다면, 그전에수에n[i]를 더한값 반환\n배열을 1개만 써도 상관없음\nn=int(input())num_list=list(map(int,input().split())) max_list=[num_list[0]]+[0 for _ in range(n-1)]for i in range(1,n): last_cnt=max_list[i-1] #이전의 숫자 if last_cnt\u0026lt;=0: max_list[i]=num_list[i] else: max_list[i]=num_list[i]+last_cnt print(max(max_list)) 골드찍었다!\n","date":"22 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/029-%EB%B0%B1%EC%A4%80-1912-%EC%97%B0%EC%86%8D%ED%95%A9/","section":"Posts","summary":"https://www.acmicpc.net/problem/1912\n1912번: 연속합첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.www.acmicpc.net\n그냥 평범한 dp\nn[i]는 n[i]를 끝값으로 하는 가장 큰 수\n%%점화식%%\nn[i]={\nn[i-1]\u003c=0 -\u003e n[i]\nn[i-1]\u003e0 -\u003e n[i]+n[i-1]\n}\n만약 그전수가 0보다 작다면, 더해봤자 손해이므로 그냥 n[i]반환\n0보다 크다면, 그전에수에n[i]를 더한값 반환\n배열을 1개만 써도 상관없음\nn=int(input())num_list=list(map(int,input().split())) max_list=[num_list[0]]+[0 for _ in range(n-1)]for i in range(1,n): last_cnt=max_list[i-1] #이전의 숫자 if last_cnt\u003c=0: max_list[i]=num_list[i] else: max_list[i]=num_list[i]+last_cnt print(max(max_list)) 골드찍었다!\n","title":"백준 1912 연속합","type":"posts"},{"content":"https://www.acmicpc.net/problem/14002\n14002번: 가장 긴 증가하는 부분 수열 4수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이www.acmicpc.net\n바로 전에 풀었던 가장 긴 증가하는 부분 수열처럼, 길이는 걍 DP돌려서 풀면 된다.\n근데 어떻게 수열을 하나씩 출력할까?\n나는 b를 각각 입력값에 대해[0,1]로 만들었다.\n첫번째 원소에는 이 원소를 끝으로 하는 가장 긴 수열\n두번째 원소에는 가장 긴 수열에서의 이 원소 바로 이전 원소를 넣는다.\n출력형식에 따라 가장 긴 수열을 출력하고,\n정답을 입력할 리스트를 만들고, 가장 긴 수열의 끝 a[b.index(max_b)] 원소를 넣는다.\n이후, b[i][1] 의 원소가 -1이 아니라면, a[b[i][1]]이 바로 그 전 숫자이고,\n-1이라면, 수열의 시작이다.\n-1이 나올때까지 반복하면, 리스트는 수열의 끝에서 수열의 시작까지 차례데로 담겨있게 될 것이고,\n이를 출력형식에 따라 reverse()한뒤 출력해주면 된다.\n근데 다른사람들 풀이를 보니깐 그냥 1차원 배열을 선언하고, 만일 수열의 최대 길이가 4라면, 왼쪽으로 탐색하면서 그 다음 수열이 3,2,1이 되도록 선언해서 풀었다.\n내 코드가 푸는시간은 더 짧긴 한데, 코드량이 2배많다.\nn=int(input())a=list(map(int,input().split()))b=[[0,-1] for _ in range(n)] for i in range(n): max_a=0 #최댓값 last_num=-1 #이전숫자 for j in range(i): #a[i]전까지 a[i]보다 작은수 비교 if a[j]\u0026lt;a[i]: if b[j][0]\u0026gt;max_a: max_a=b[j][0] last_num=j b[i]=[max_a+1,last_num] #[i까지의 최대길이, i이전의 수] answer=max(b)print(answer[0]) #여기서 배열 최댓값 반환 first_num=a[b.index(answer)]tmp=answer[1]ll=[first_num]while True: if tmp==-1:break ll.append(a[tmp]) tmp=b[tmp][1] ll.reverse() print(* ll) ","date":"22 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/028-%EB%B0%B1%EC%A4%80-14002-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-4/","section":"Posts","summary":"https://www.acmicpc.net/problem/14002\n14002번: 가장 긴 증가하는 부분 수열 4수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이www.acmicpc.net\n바로 전에 풀었던 가장 긴 증가하는 부분 수열처럼, 길이는 걍 DP돌려서 풀면 된다.\n근데 어떻게 수열을 하나씩 출력할까?\n나는 b를 각각 입력값에 대해[0,1]로 만들었다.\n","title":"백준 14002 가장 긴 증가하는 부분 수열 4","type":"posts"},{"content":"https://www.acmicpc.net/problem/11053\n11053번: 가장 긴 증가하는 부분 수열수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이www.acmicpc.net\n%%풀이%%\na는 입력받은 값을 저장하는 배열,\nb는\na[i]\nn[i]를 구하려면 n[0]부터 n[i-1] 중 n[i]보다 작은 수 중의 최댓값+=1이다.\n즉, n[i]의 왼쪽에있는 수를 비교하여 n[i]보다 작은 수를 찾고,\n이중 가장 큰 값(가장 길게 이어저온 배열)에 1을 추가하면 된다.\n점화식\nn[i]=max( n[:i] \u0026lt; n[i] )+1\n이후 배열 b의 가장 큰 값을 구하면 된다.\nn=int(input())a=list(map(int,input().split()))b=[0 for _ in range(n)] for i in range(n): max_a=0 #최댓값 for j in range(i): #a[i]전까지 a[i]보다 작은수 비교 if a[j]\u0026lt;a[i]: max_a=max(max_a,b[j]) b[i]=max_a+1 print(max(b)) ","date":"21 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/027-%EB%B0%B1%EC%A4%80-11053-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4/","section":"Posts","summary":"https://www.acmicpc.net/problem/11053\n11053번: 가장 긴 증가하는 부분 수열수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이www.acmicpc.net\n%%풀이%%\na는 입력받은 값을 저장하는 배열,\nb는\na[i]\nn[i]를 구하려면 n[0]부터 n[i-1] 중 n[i]보다 작은 수 중의 최댓값+=1이다.\n즉, n[i]의 왼쪽에있는 수를 비교하여 n[i]보다 작은 수를 찾고,\n","title":"백준 11053 가장 긴 증가하는 부분 수열","type":"posts"},{"content":"https://www.acmicpc.net/problem/2193\n2193번: 이친수0과 1로만 이루어진 수를 이진수라 한다. 이러한 이진수 중 특별한 성질을 갖는 것들이 있는데, 이들을 이친수(pinary number)라 한다. 이친수는 다음의 성질을 만족한다. 이친수는 0으로 시작하지 않www.acmicpc.net\ndp문제를 풀다보면 해결방안이 보인다.\n이친수는 2가지 경우가 있다. 0로끝나는경우, 1으로끝나는 경우\n0으로 끝나는 경우에는, 뒤에 0이오던 1이오던 상관없다\n1로 끝나는 경우에는, 뒤에 0만 와야한다.\n이를 식으로 표현하면\n(배열은 각각 2개의원소를 가지고 있는 2차원 배열)\nl[i][0]=i번쩨 자릿수가 0으로끝나는경우\nl[i][1]=i번째 자릿수가 1로 끝나는 경우\nn[i][0]=n[i-1][0]+n[i-1][1]\n0으로 끝나는 경우엔 그 전에 0으로끝나든 1로끝나든 상관x\nn[i][1]=n[i-1][0]\n1로 끝나는 경우엔 그 전이 0으로 끝날 때만 가능\n이렇게 배열을 구하고 l[n]을 모두 더하면 (0끝+1끝)\n답이나온다.\nㅎ\nn=int(input())l=[[0,0] for _ in range(n+2)] #끝0, 끝1l[1]=[0,1]l[2]=[1,0] for i in range(3,len(l)-1): l[i][0]=sum(l[i-1]) l[i][1]=l[i-1][0] print(sum(l[n])) ","date":"20 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/026-%EB%B0%B1%EC%A4%80-2193-%EC%9D%B4%EC%B9%9C%EC%88%98/","section":"Posts","summary":"https://www.acmicpc.net/problem/2193\n2193번: 이친수0과 1로만 이루어진 수를 이진수라 한다. 이러한 이진수 중 특별한 성질을 갖는 것들이 있는데, 이들을 이친수(pinary number)라 한다. 이친수는 다음의 성질을 만족한다. 이친수는 0으로 시작하지 않www.acmicpc.net\ndp문제를 풀다보면 해결방안이 보인다.\n이친수는 2가지 경우가 있다. 0로끝나는경우, 1으로끝나는 경우\n0으로 끝나는 경우에는, 뒤에 0이오던 1이오던 상관없다\n1로 끝나는 경우에는, 뒤에 0만 와야한다.\n이를 식으로 표현하면\n(배열은 각각 2개의원소를 가지고 있는 2차원 배열)\nl[i][0]=i번쩨 자릿수가 0으로끝나는경우\nl[i][1]=i번째 자릿수가 1로 끝나는 경우\n","title":"백준 2193 이친수","type":"posts"},{"content":"https://www.acmicpc.net/problem/15990\n15990번: 1, 2, 3 더하기 5각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다.www.acmicpc.net\n처음에 보자마자 너무 막막했다.\n살짝 힌트를 얻어서 풀었다.\n이전 dp문제와는 달리, 배열을 [0,0,0,0]이 들어있는 2차원 배열로 만든다\n[[0,0,0,0],[0,0,0,0]\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.]\n여기서 l[i][0]은 i를 만드는 경우의 수(정답),\nl[i][1] 마지막이 1일때 i를 만드는 경우의수\nl[i][2]마지막이 2일때 i를 만드는 경우의수\nl[i][3]마지막이 3일때 i를 만드는 경우의수\n를 뜻한다.\n만일 l[i][1]을 구하고 싶다면, 마지막에 1을 더해야 하므로 l[i-1]에서 1을 더해야 한다.\n하지만 문제의 조건상 1이 연속해서 나올 수 없으므로,\nl[i-1]의 2, 3\n즉 i보다 1 작은 수를 만들 때 끝이 1이 아닌 경우의 수의 합이\nl[i]를 마지막에 1을 더해 만드는 경우의 수이다.\n똑같은 방식으로 l[i][2]=l[i-2] 1,3 l[i][3]=l[i-3] 1,2가 된다.\n이렇게 구한 l[i][1,2,3]의 합을 l[0]에 업데이트시켜주면 된다.\n점화식을 세우면\nl[i][0]= l[i][1]+ l[i][2]+ l[i][3]\nl[i][1]=(l[i-1][2]+l[i-1][3])\nl[i][2]=(l[i-2][1]+l[i-2][3])\nl[i][3]=(l[i-3][1]+l[i-3][2])\n이 된다.\n수가 가면갈수록 기하급수적으로 커지므로\nl[i][1],l[i][2],l[i][3] 을 구할때마다 %\n1,000,000,009를 해주고\n마지막에 l[i][0]을 구할 때 %\n1,000,000,009를 해서 배열에 저장해주면 된다.\nl=[[0,0,0,0]for _ in range(100001)]l[1]=[1,1,0,0]l[2]=[1,0,1,0]l[3]=[3,1,1,1] l[4]=[3,2,0,1]for i in range(4,len(l)): l[i][1]=(l[i-1][2]+l[i-1][3])%1000000009 l[i][2]=(l[i-2][1]+l[i-2][3])%1000000009 l[i][3]=(l[i-3][1]+l[i-3][2])%1000000009 l[i][0]=sum(l[i][1:])%1000000009 T=int(input())for _ in range(T): n=int(input()) print(l[n][0]) 개어려웠다. 이게 왜 실2?\n","date":"20 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/025-%EB%B0%B1%EC%A4%80-15990-123-%EB%8D%94%ED%95%98%EA%B8%B05/","section":"Posts","summary":"https://www.acmicpc.net/problem/15990\n15990번: 1, 2, 3 더하기 5각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다.www.acmicpc.net\n처음에 보자마자 너무 막막했다.\n살짝 힌트를 얻어서 풀었다.\n이전 dp문제와는 달리, 배열을 [0,0,0,0]이 들어있는 2차원 배열로 만든다\n[[0,0,0,0],[0,0,0,0]………….]\n여기서 l[i][0]은 i를 만드는 경우의 수(정답),\nl[i][1] 마지막이 1일때 i를 만드는 경우의수\nl[i][2]마지막이 2일때 i를 만드는 경우의수\nl[i][3]마지막이 3일때 i를 만드는 경우의수\n를 뜻한다.\n만일 l[i][1]을 구하고 싶다면, 마지막에 1을 더해야 하므로 l[i-1]에서 1을 더해야 한다.\n","title":"백준 15990 1,2,3 더하기5","type":"posts"},{"content":"n=int(input())l=[-1]l+=(map(int,input().split()))for i in range(1,len(l)): for j in range(1,i//2+1): l[i]=min(l[i],l[j]+l[i-j])print(l[n]) 카드 구매하기 정확히 반대로하면됨\n","date":"20 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/024-%EB%B0%B1%EC%A4%80-16194-%EC%B9%B4%EB%93%9C-%EA%B5%AC%EB%A7%A4%ED%95%98%EA%B8%B0-2/","section":"Posts","summary":"n=int(input())l=[-1]l+=(map(int,input().split()))for i in range(1,len(l)): for j in range(1,i//2+1): l[i]=min(l[i],l[j]+l[i-j])print(l[n]) 카드 구매하기 정확히 반대로하면됨\n","title":"백준 16194 카드 구매하기 2","type":"posts"},{"content":"https://www.acmicpc.net/problem/11052\n11052번: 카드 구매하기첫째 줄에 민규가 구매하려고 하는 카드의 개수 N이 주어진다. (1 ≤ N ≤ 1,000) 둘째 줄에는 Pi가 P1부터 PN까지 순서대로 주어진다. (1 ≤ Pi ≤ 10,000)www.acmicpc.net\n1.0번째 인덱스를 제외하고 n+1길이의 배열에 각각 p[i]값을 입력한다\nl[4]를 예로 들자면\nl[4] 현재 p[4]이므로\n현재의 l[4]와 (l[3]+l[1]), (l[2]+l[2]), (l[1]+l[3]) 중의 최댓값이 l[4]가 된다.\n이를 점화식으로 쓰면\nl[i]=max( l[i],(l[i-1]+l[i]),(l[i-2]+l[2]),\u0026hellip;..(l[i-n]+l[0]) ) 이다.\n조금더 생각해보면\nl[i-n]+l[n]은 l[n]+l[i]와 같은 결과를 가지므로\nl//2+1 까지만 비교해줘도 답을 알수 있다.\nn=int(input())l=[0]l+=(list(map(int,input().split())))for i in range(2,len(l)): for j in range(1,i//2+1): l[i]=max(l[i],l[i-j]+l[j])print(l[n]) ","date":"19 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/023-%EB%B0%B1%EC%A4%80-11052-%EC%B9%B4%EB%93%9C-%EA%B5%AC%EB%A7%A4%ED%95%98%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/11052\n11052번: 카드 구매하기첫째 줄에 민규가 구매하려고 하는 카드의 개수 N이 주어진다. (1 ≤ N ≤ 1,000) 둘째 줄에는 Pi가 P1부터 PN까지 순서대로 주어진다. (1 ≤ Pi ≤ 10,000)www.acmicpc.net\n1.0번째 인덱스를 제외하고 n+1길이의 배열에 각각 p[i]값을 입력한다\nl[4]를 예로 들자면\nl[4] 현재 p[4]이므로\n현재의 l[4]와 (l[3]+l[1]), (l[2]+l[2]), (l[1]+l[3]) 중의 최댓값이 l[4]가 된다.\n이를 점화식으로 쓰면\nl[i]=max( l[i],(l[i-1]+l[i]),(l[i-2]+l[2]),…..(l[i-n]+l[0]) ) 이다.\n조금더 생각해보면\nl[i-n]+l[n]은 l[n]+l[i]와 같은 결과를 가지므로\nl//2+1 까지만 비교해줘도 답을 알수 있다.\n","title":"백준 11052 카드 구매하기","type":"posts"},{"content":"t=int(input()) for _ in range(t): n=int(input()) l=[-1,1,2,4]+[0 for x in range(n-3)] for i in range(4,len(l)): l[i]=l[i-1]+l[i-2]+l[i-3] print(l[n]) https://www.acmicpc.net/problem/9095\n9095번: 1, 2, 3 더하기각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.www.acmicpc.net\n일단 규칙을찾는다\n(n)=n을 만드는 경우의 수\n(1)= (1)\n(2)=2 (1+1,2)\n(3)=4 (1+1+1,1+2,2+1,3)\n(4)=7 (1+1+1+1,1+1+2,1+2+1,2+2+1,2+2,1+3,3+1)\nn이 4일때\nn(1)에서 3을만들어야되므로 n(3), 4가지\nn(2)에서 2를만들어야되므로 n(2) 2가지\nn(3)에서 1을만들어야되므로 n(1)1가지\n경우의수가 있다.\n그러므로, n[i]=n[i-1]+n[i-2]+n[i-3] n[1]=1, n[2]=2, n[3]=3\n의 점화식이 생긴다.\n이를 배열에 넣고 dp돌리면 된다.\n","date":"19 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/022-%EB%B0%B1%EC%A4%80-9095-123%EB%8D%94%ED%95%98%EA%B8%B0/","section":"Posts","summary":"t=int(input()) for _ in range(t): n=int(input()) l=[-1,1,2,4]+[0 for x in range(n-3)] for i in range(4,len(l)): l[i]=l[i-1]+l[i-2]+l[i-3] print(l[n]) https://www.acmicpc.net/problem/9095\n9095번: 1, 2, 3 더하기각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.www.acmicpc.net\n일단 규칙을찾는다\n(n)=n을 만드는 경우의 수\n(1)= (1)\n(2)=2 (1+1,2)\n(3)=4 (1+1+1,1+2,2+1,3)\n(4)=7 (1+1+1+1,1+1+2,1+2+1,2+2+1,2+2,1+3,3+1)\nn이 4일때\nn(1)에서 3을만들어야되므로 n(3), 4가지\nn(2)에서 2를만들어야되므로 n(2) 2가지\nn(3)에서 1을만들어야되므로 n(1)1가지\n경우의수가 있다.\n그러므로, n[i]=n[i-1]+n[i-2]+n[i-3] n[1]=1, n[2]=2, n[3]=3\n의 점화식이 생긴다.\n이를 배열에 넣고 dp돌리면 된다.\n","title":"백준 9095 1,2,3더하기","type":"posts"},{"content":"https://www.acmicpc.net/problem/11727\n11727번: 2×n 타일링 22×n 직사각형을 1×2, 2×1과 2×2 타일로 채우는 방법의 수를 구하는 프로그램을 작성하시오. 아래 그림은 2×17 직사각형을 채운 한가지 예이다.www.acmicpc.net\n2xn 타일링 1과 비슷한 문제이다.\nn[i]=n[i-2]*2+n[i-1]이라는 점화식을 세울 수 있다.\n왜?\ni번째를 채우기 위해서는\ni-1번째의 경우에서 2*1 타일 1개를 채우는 방법과,\ni-2번째의 경우에서 12 2개로채우는 방법, 22 1개로 채우는 2가지 방법이 있기 때문이다.\ni-1번째의 경우는 그대로, i-2경우에는 각각 2개의 경우이므로 *2를 해주면 된다.\n사실 0개를 채우는 경우의 수는 0이지만, 곱셈연산을 위해 편의상 1로 가정하고 코드를 짰다.\nn=int(input())a,b=1,1cnt=1while cnt\u0026lt;n: a,b=b,a*2+b cnt+=1print(b%10007) ","date":"19 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/021-%EB%B0%B1%EC%A4%80-11727-2xn%ED%83%80%EC%9D%BC%EB%A7%81-2/","section":"Posts","summary":"https://www.acmicpc.net/problem/11727\n11727번: 2×n 타일링 22×n 직사각형을 1×2, 2×1과 2×2 타일로 채우는 방법의 수를 구하는 프로그램을 작성하시오. 아래 그림은 2×17 직사각형을 채운 한가지 예이다.www.acmicpc.net\n2xn 타일링 1과 비슷한 문제이다.\nn[i]=n[i-2]*2+n[i-1]이라는 점화식을 세울 수 있다.\n왜?\ni번째를 채우기 위해서는\ni-1번째의 경우에서 2*1 타일 1개를 채우는 방법과,\ni-2번째의 경우에서 12 2개로채우는 방법, 22 1개로 채우는 2가지 방법이 있기 때문이다.\ni-1번째의 경우는 그대로, i-2경우에는 각각 2개의 경우이므로 *2를 해주면 된다.\n","title":"백준 11727 2xn타일링 2","type":"posts"},{"content":"https://www.acmicpc.net/problem/11726\n11726번: 2×n 타일링2×n 크기의 직사각형을 1×2, 2×1 타일로 채우는 방법의 수를 구하는 프로그램을 작성하시오. 아래 그림은 2×5 크기의 직사각형을 채운 한 가지 방법의 예이다.www.acmicpc.net\n원리만 알면 구현하기 매우 쉽다.\n맨 처음 손으로 써보았다.\n간단하게 n[i]=n[i-1]+n[i-2] 의 점화식 즉 피보나치 수열이 나온다.\n중요한건 왜 그런가이다\n!!!!!!!!!!!!!\nn[4]를 예로 들겠다.\nn[4]를 만드는 방법은\nn[3]의 모든 경우의 수에서 2*1블럭 1개를 가장 오른쪽에 채우는 방법과,\nn[2]의 모든 경우의 수에스 12블럭 2개(22)를 채우는 방법이 있다.\n즉 n[3]과 n[2]의 합이된다.\n구현자체는 매우 쉬운데 이해하기가 어렵다. 아직도 헷갈린다.\n%% 오른쪽에만 채우는게아니라 가운데, 첫번째에 채울 수도 있지 않나 라고 생각했는데\n오른쪽에 채우는게 아니면 이미 그 전의 경우의 수에 들어있게 된다 %%\nn=int(input())a,b=0,1cnt=1while cnt\u0026lt;=n: a,b=b,a+b cnt+=1print(b%10007) ","date":"18 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/020-%EB%B0%B1%EC%A4%80-11726-2xn-%ED%83%80%EC%9D%BC%EB%A7%81/","section":"Posts","summary":"https://www.acmicpc.net/problem/11726\n11726번: 2×n 타일링2×n 크기의 직사각형을 1×2, 2×1 타일로 채우는 방법의 수를 구하는 프로그램을 작성하시오. 아래 그림은 2×5 크기의 직사각형을 채운 한 가지 방법의 예이다.www.acmicpc.net\n원리만 알면 구현하기 매우 쉽다.\n맨 처음 손으로 써보았다.\n간단하게 n[i]=n[i-1]+n[i-2] 의 점화식 즉 피보나치 수열이 나온다.\n중요한건 왜 그런가이다\n!!!!!!!!!!!!!\nn[4]를 예로 들겠다.\nn[4]를 만드는 방법은\nn[3]의 모든 경우의 수에서 2*1블럭 1개를 가장 오른쪽에 채우는 방법과,\n","title":"백준 11726 2xn 타일링","type":"posts"},{"content":"n=int(input())answer=[[0,1,1,1,1,1,1,1,1,1]]for i in range(1,n): l=[] for j in range(10): if j==0: l.append(answer[i-1][j+1]) elif j==9: l.append(answer[i-1][j-1]) else: l.append(answer[i-1][j-1]+answer[i-1][j+1]) answer.append(l) print(sum(answer[-1])%1000000000) https://www.acmicpc.net/problem/10844\n10844번: 쉬운 계단 수첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.www.acmicpc.net\n하나씩 손으로\n써보다보면 규칙이 보인다.\narr[i][j]는 [i]자리의 끝이[j]인 계단수의 개수이다.\n처음엔 0을 제외한 모든수가 계단수이므로\n[0.1.1.1.1.1.1.1.1.1]이다.\n두번째에는[1,1,2,2,2,2,2,2,1]이다.\n이를 반복하면\narr[i][j]=arr[i-1][j-1] + arr[i-1][j+1]이라는 식을 찾을 수 있다.\n파이썬에서 indexerror방지를 위해서 j가 0 또는 9일때만 예외를 설정하여\n0일때는 그 전의 1의값만, 9일때는 그 전의 8의값만 더해주면\n문제를 풀 수 있다.\n","date":"18 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/019-%EB%B0%B1%EC%A4%80-10884-%EC%89%AC%EC%9A%B4-%EA%B3%84%EB%8B%A8%EC%88%98/","section":"Posts","summary":"n=int(input())answer=[[0,1,1,1,1,1,1,1,1,1]]for i in range(1,n): l=[] for j in range(10): if j==0: l.append(answer[i-1][j+1]) elif j==9: l.append(answer[i-1][j-1]) else: l.append(answer[i-1][j-1]+answer[i-1][j+1]) answer.append(l) print(sum(answer[-1])%1000000000) https://www.acmicpc.net/problem/10844\n10844번: 쉬운 계단 수첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.www.acmicpc.net\n하나씩 손으로\n써보다보면 규칙이 보인다.\narr[i][j]는 [i]자리의 끝이[j]인 계단수의 개수이다.\n처음엔 0을 제외한 모든수가 계단수이므로\n[0.1.1.1.1.1.1.1.1.1]이다.\n두번째에는[1,1,2,2,2,2,2,2,1]이다.\n이를 반복하면\narr[i][j]=arr[i-1][j-1] + arr[i-1][j+1]이라는 식을 찾을 수 있다.\n파이썬에서 indexerror방지를 위해서 j가 0 또는 9일때만 예외를 설정하여\n0일때는 그 전의 1의값만, 9일때는 그 전의 8의값만 더해주면\n","title":"백준 10884 쉬운 계단수","type":"posts"},{"content":"https://www.acmicpc.net/problem/1764\n1764번: 듣보잡첫째 줄에 듣도 못한 사람의 수 N, 보도 못한 사람의 수 M이 주어진다. 이어서 둘째 줄부터 N개의 줄에 걸쳐 듣도 못한 사람의 이름과, N+2째 줄부터 보도 못한 사람의 이름이 순서대로 주어진다.www.acmicpc.net\n그냥 리스트에서 하나씩 비교하면 시간때문에 못 풀고,\ndict으로 검색하거나\nset로 바꿔 공집합을 이용하면된다\n풀이1 (dict)\nn,m=map(int,input().split())l=dict()answer=[]for i in range(n): l[input()]=i for _ in range(m): ds=input() if ds in l: answer.append(ds) answer.sort() print(len(answer))for awn in answer: print(awn) 풀이2(set)\nimport sysinput=sys.stdin.readlinen,m=map(int,input().split())a=[]b=[]answer=[] for i in range(n): a.append(input())for _ in range(m): b.append(input()) answer=list(set(a)\u0026amp;set(b))answer.sort() print(len(answer)) for awn in answer: print(awn, end=\u0026#39;\u0026#39;) ","date":"17 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/018-%EB%B0%B1-%EC%A4%80-1764-%EB%93%A3%EB%B3%B4%EC%9E%A1/","section":"Posts","summary":"https://www.acmicpc.net/problem/1764\n1764번: 듣보잡첫째 줄에 듣도 못한 사람의 수 N, 보도 못한 사람의 수 M이 주어진다. 이어서 둘째 줄부터 N개의 줄에 걸쳐 듣도 못한 사람의 이름과, N+2째 줄부터 보도 못한 사람의 이름이 순서대로 주어진다.www.acmicpc.net\n그냥 리스트에서 하나씩 비교하면 시간때문에 못 풀고,\ndict으로 검색하거나\nset로 바꿔 공집합을 이용하면된다\n풀이1 (dict)\nn,m=map(int,input().split())l=dict()answer=[]for i in range(n): l[input()]=i for _ in range(m): ds=input() if ds in l: answer.append(ds) answer.sort() print(len(answer))for awn in answer: print(awn) 풀이2(set)\nimport sysinput=sys.stdin.readlinen,m=map(int,input().split())a=[]b=[]answer=[] for i in range(n): a.append(input())for _ in range(m): b.append(input()) answer=list(set(a)\u0026set(b))answer.sort() print(len(answer)) for awn in answer: print(awn, end='')","title":"백 준 1764 듣보잡","type":"posts"},{"content":"https://www.acmicpc.net/problem/17087\n17087번: 숨바꼭질 6수빈이는 동생 N명과 숨바꼭질을 하고 있다. 수빈이는 현재 점 S에 있고, 동생은 A1, A2, \u0026hellip;, AN에 있다. 수빈이는 걸어서 이동을 할 수 있다. 수빈이의 위치가 X일때 걷는다면 1초 후에 X+D나 X-D로 이www.acmicpc.net\ngcd함수 선언\n동생위치를 동생위치-형위치의 절댓값으로 바꾼다\n동생위치를 2개씩 계속 gcd를 돌린다\ndef gcd(a,b): if a%b==0: return b a,b=b,a%b return gcd(a,b) n,s=map(int,input().split())a=list(map(int,input().split()))answer=a[0]-s for i in range(1,len(a)): a[i]= abs(a[i]-s) answer=gcd(answer,a[i]) print(answer) ","date":"14 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/017-%EB%B0%B1%EC%A4%80-17087-%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%886/","section":"Posts","summary":"https://www.acmicpc.net/problem/17087\n17087번: 숨바꼭질 6수빈이는 동생 N명과 숨바꼭질을 하고 있다. 수빈이는 현재 점 S에 있고, 동생은 A1, A2, …, AN에 있다. 수빈이는 걸어서 이동을 할 수 있다. 수빈이의 위치가 X일때 걷는다면 1초 후에 X+D나 X-D로 이www.acmicpc.net\ngcd함수 선언\n동생위치를 동생위치-형위치의 절댓값으로 바꾼다\n동생위치를 2개씩 계속 gcd를 돌린다\ndef gcd(a,b): if a%b==0: return b a,b=b,a%b return gcd(a,b) n,s=map(int,input().split())a=list(map(int,input().split()))answer=a[0]-s for i in range(1,len(a)): a[i]= abs(a[i]-s) answer=gcd(answer,a[i]) print(answer)","title":"백준 17087 숨바꼭질6","type":"posts"},{"content":"https://www.acmicpc.net/problem/1463\n1463번: 1로 만들기첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.www.acmicpc.net\n처음 구현해본 dp라 처음에는 헤멨는데\n원리만 알고 나니깐 너무 간단하다.\n상향식 접근?\n처음부터 1씩 더해가며 각각의 값을 업데이트하면 된다.\n사실 2,3을 업데이트할 필요도 없음\nx=int(input())cnt_list=[-1]+[0]+[1]+[1]*(x-1) ##첫번째는 안쓸거니깐 x+1개의 리스트 만들기 for i in range(3,len(cnt_list)): ###0,1,2,3을 제외하고 dp cnt=cnt_list[i-1]+1 #1을 빼서 바로 전 수로 만드는 경우 if i%3==0: cnt=min(cnt,cnt_list[i//3]+1) ## 3으로 나누었을때의 경우의 수는, 이를 3으로 나눴을때의 경우의 수 +1 if i%2==0: cnt=min(cnt,cnt_list[i//2]+1) ##위와 동 ##최솟값만 남기고 cnt_list[i]=cnt #최솟값 업데이트print(cnt_list[x]) ","date":"14 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/016-%EB%B0%B1%EC%A4%80-1463-1%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/1463\n1463번: 1로 만들기첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.www.acmicpc.net\n처음 구현해본 dp라 처음에는 헤멨는데\n원리만 알고 나니깐 너무 간단하다.\n상향식 접근?\n처음부터 1씩 더해가며 각각의 값을 업데이트하면 된다.\n사실 2,3을 업데이트할 필요도 없음\nx=int(input())cnt_list=[-1]+[0]+[1]+[1]*(x-1) ##첫번째는 안쓸거니깐 x+1개의 리스트 만들기 for i in range(3,len(cnt_list)): ###0,1,2,3을 제외하고 dp cnt=cnt_list[i-1]+1 #1을 빼서 바로 전 수로 만드는 경우 if i%3==0: cnt=min(cnt,cnt_list[i//3]+1) ## 3으로 나누었을때의 경우의 수는, 이를 3으로 나눴을때의 경우의 수 +1 if i%2==0: cnt=min(cnt,cnt_list[i//2]+1) ##위와 동 ##최솟값만 남기고 cnt_list[i]=cnt #최솟값 업데이트print(cnt_list[x])","title":"백준 1463 1로 만들기","type":"posts"},{"content":"https://www.acmicpc.net/problem/9613\n9613번: GCD 합첫째 줄에 테스트 케이스의 개수 t (1 ≤ t ≤ 100)이 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있다. 각 테스트 케이스는 수의 개수 n (1 \u0026lt; n ≤ 100)가 주어지고, 다음에는 n개의 수가 주어진www.acmicpc.net\n진짜 쉬운 문제인데 각 줄의 첫번째가 n이라는걸 간과해서 시간을 오래 잡아먹었다.\n배열에 2가지를 넣는 경우의 수를 구하고, 이의 최소공배수를 각각 구한다.\nfrom itertools import combinationsdef gcd(a,b): while True: a,b=b,a%b if b==0: return a t=int(input())for _ in range(t): l=list(map(int,input().split())) n=l.pop(0) answer=0 com=combinations(l,2) for i in com: a,b=i answer+=gcd(a,b) print(answer) ","date":"13 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/015-9613.-gcd-%ED%95%A9/","section":"Posts","summary":"https://www.acmicpc.net/problem/9613\n9613번: GCD 합첫째 줄에 테스트 케이스의 개수 t (1 ≤ t ≤ 100)이 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있다. 각 테스트 케이스는 수의 개수 n (1 \u003c n ≤ 100)가 주어지고, 다음에는 n개의 수가 주어진www.acmicpc.net\n진짜 쉬운 문제인데 각 줄의 첫번째가 n이라는걸 간과해서 시간을 오래 잡아먹었다.\n배열에 2가지를 넣는 경우의 수를 구하고, 이의 최소공배수를 각각 구한다.\nfrom itertools import combinationsdef gcd(a,b): while True: a,b=b,a%b if b==0: return a t=int(input())for _ in range(t): l=list(map(int,input().split())) n=l.pop(0) answer=0 com=combinations(l,2) for i in com: a,b=i answer+=gcd(a,b) print(answer)","title":"9613. GCD 합","type":"posts"},{"content":"https://www.acmicpc.net/problem/6588\n6588번: 골드바흐의 추측각 테스트 케이스에 대해서, n = a + b 형태로 출력한다. 이때, a와 b는 홀수 소수이다. 숫자와 연산자는 공백 하나로 구분되어져 있다. 만약, n을 만들 수 있는 방법이 여러 가지라면, b-a가 가장 큰www.acmicpc.net\n간단한문제\n소수를 구한다(체로구현)\nn을 1과 n-1부터 시작해서, 각각1 씩 올려가며확인\n둘다소수이면 종료\nsosu=[False,False]+[True]*int(1000000)for i in range(1,len(sosu)): if sosu[i]: tmp=2 while i*tmp\u0026lt;len(sosu): sosu[i*tmp]=False tmp+=1while True: t=int(input()) if t==0: break a,b=2,t-2 while a\u0026lt;=b: if sosu[a] and sosu[b]: print(f\u0026#39;{t} = {a} + {b}\u0026#39;) break else: a+=1 b-=1 ","date":"12 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/014-%EB%B0%B1%EC%A4%80-6588-%EA%B3%A8%EB%93%9C%EB%B0%94%ED%9D%90%EC%9D%98-%EC%B6%94%EC%B8%A1/","section":"Posts","summary":"https://www.acmicpc.net/problem/6588\n6588번: 골드바흐의 추측각 테스트 케이스에 대해서, n = a + b 형태로 출력한다. 이때, a와 b는 홀수 소수이다. 숫자와 연산자는 공백 하나로 구분되어져 있다. 만약, n을 만들 수 있는 방법이 여러 가지라면, b-a가 가장 큰www.acmicpc.net\n간단한문제\n소수를 구한다(체로구현)\nn을 1과 n-1부터 시작해서, 각각1 씩 올려가며확인\n둘다소수이면 종료\nsosu=[False,False]+[True]*int(1000000)for i in range(1,len(sosu)): if sosu[i]: tmp=2 while i*tmp\u003clen(sosu): sosu[i*tmp]=False tmp+=1while True: t=int(input()) if t==0: break a,b=2,t-2 while a\u003c=b: if sosu[a] and sosu[b]: print(f'{t} = {a} + {b}') break else: a+=1 b-=1","title":"백준 6588 골드바흐의 추측","type":"posts"},{"content":"https://www.acmicpc.net/problem/1918\n1918번: 후위 표기식첫째 줄에 중위 표기식이 주어진다. 단 이 수식의 피연산자는 알파벳 대문자로 이루어지며 수식에서 한 번씩만 등장한다. 그리고 -A+B와 같이 -가 가장 앞에 오거나 AB와 같이 *가 생략되는 등의www.acmicpc.net\n진짜 ㅈ 같은 문제다.\n애초에 후위표기식 개념이 헷갈려서 한참 해멨다.\n%%주의%%\n만일 곱하기나 나누기면, stack의 출구가 곱하기나 나누기가 아닐때까지 pop해주고(answer에 추가)\n부호를 더해준다.\n여기서 한참해멧는데, 예제에는 이런식이 없어서 생각도 못했었다.\n!! 더하기나 빼기는 곱하기나 나누기를 모두 계산한 후에 계산하기 때문에, 스택에 남아있어야 한다.!!!\n이걸간과해도 예제는 맞지만,\nAB-CD/E 등의 식 계산이 틀리계된다.\n정답=\nAB-CD/E 오답=\nABCD-E/\n빨리 골드찍고싶다\na=input()s=[]#스택answer=\u0026#39;\u0026#39; for i in a: if i.isalpha(): #알파벳이면 바로 추가 answer +=i elif i==\u0026#39;(\u0026#39;: #괄호가 열리면 스택에 추가 s.append(i) elif i==\u0026#39;*\u0026#39;or i==\u0026#39;/\u0026#39;: #곱하기나 나누기면 while s and (s[-1] == \u0026#39;*\u0026#39; or s[-1] == \u0026#39;/\u0026#39; ): #+,-는 *,/ 계산 후에 계산함으로 answer+=s.pop() # 곱셈뺄셈 다빼줌 s.append(i) #부호 추가 elif i==\u0026#39;+\u0026#39; or i==\u0026#39;-\u0026#39;: #더하기나 빼기면 while s and s[-1] != \u0026#39;(\u0026#39; : #괄호가아닌이상 answer += s.pop() #정답에 스택pop s.append(i) #부호 스택에 추가 elif i==\u0026#39;)\u0026#39;: #괄호가 닫히면 while s[-1] !=\u0026#39;(\u0026#39; : #괄호가시작됬을때까지 answer +=s.pop() #스택에있는 모든 부호 정답으로 보내기 s.pop() #여는부호는 제거 while s: #마지막으로남아있는 스택에 answer+=s.pop()print(answer) ","date":"12 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/013-%EB%B0%B1%EC%A4%80-1918-%ED%9B%84%EC%9C%84-%ED%91%9C%EA%B8%B0%EC%8B%9D/","section":"Posts","summary":"https://www.acmicpc.net/problem/1918\n1918번: 후위 표기식첫째 줄에 중위 표기식이 주어진다. 단 이 수식의 피연산자는 알파벳 대문자로 이루어지며 수식에서 한 번씩만 등장한다. 그리고 -A+B와 같이 -가 가장 앞에 오거나 AB와 같이 *가 생략되는 등의www.acmicpc.net\n진짜 ㅈ 같은 문제다.\n애초에 후위표기식 개념이 헷갈려서 한참 해멨다.\n%%주의%%\n만일 곱하기나 나누기면, stack의 출구가 곱하기나 나누기가 아닐때까지 pop해주고(answer에 추가)\n부호를 더해준다.\n여기서 한참해멧는데, 예제에는 이런식이 없어서 생각도 못했었다.\n!! 더하기나 빼기는 곱하기나 나누기를 모두 계산한 후에 계산하기 때문에, 스택에 남아있어야 한다.!!!\n","title":"백준 1918 후위 표기식","type":"posts"},{"content":"https://www.acmicpc.net/problem/1935\n1935번: 후위 표기식2첫째 줄에 피연산자의 개수(1 ≤ N ≤ 26) 가 주어진다. 그리고 둘째 줄에는 후위 표기식이 주어진다. (여기서 피연산자는 A~Z의 영대문자이며, A부터 순서대로 N개의 영대문자만이 사용되며, 길이www.acmicpc.net\n진짜 후위표기식이 뭔지만 찾아보고 혼자 푼 문제라 쾌감이 째진다 ㅎㅎ\n단어열이 표현되는 abc_list를 통해 num_list에 접근해서 진짜 값을 알아내는 식으로 숫자를 넣었고,\n그다음부터는 그냥 연산자가 나올때까지 stack에 넣고\n연산자가 나올때마다 stack에서 2개를 pop해주고\n두번째를 a, 첫번째를 b로 정한뒤 연산을 수행하고\n결과를 다시 스택에 넣는다.\n다른사람코드를 보면서 느꼈는데, 각각의 알파벳의 아스키코드를 이용해서 좀더 깔끔하게 정리할 수도 있다.\nord(i)-ord(\u0026lsquo;A\u0026rsquo;)\nimport stringabc_list=list(string.ascii_uppercase)n=int(input())num_list=[] w=input()for _ in range(n): num_list.append(int(input()))pl=[\u0026#39;*\u0026#39;,\u0026#39;+\u0026#39;,\u0026#39;/\u0026#39;,\u0026#39;-\u0026#39;] s=[] #스택for i in w: if i not in pl: s.append(num_list[abc_list.index(i)]) else: b=s.pop() a=s.pop() if i ==\u0026#39;+\u0026#39;: c=a+b elif i==\u0026#39;-\u0026#39;: c=a-b elif i==\u0026#39;*\u0026#39;: c=a*b elif i==\u0026#39;/\u0026#39;: c=a/b s.append(c)print(\u0026#39;{:.2f}\u0026#39;.format(s.pop())) ","date":"11 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/012-%EB%B0%B1%EC%A4%80-1935-%ED%9B%84%EC%9C%84-%ED%91%9C%EA%B8%B0%EC%8B%9D-2/","section":"Posts","summary":"https://www.acmicpc.net/problem/1935\n1935번: 후위 표기식2첫째 줄에 피연산자의 개수(1 ≤ N ≤ 26) 가 주어진다. 그리고 둘째 줄에는 후위 표기식이 주어진다. (여기서 피연산자는 A~Z의 영대문자이며, A부터 순서대로 N개의 영대문자만이 사용되며, 길이www.acmicpc.net\n진짜 후위표기식이 뭔지만 찾아보고 혼자 푼 문제라 쾌감이 째진다 ㅎㅎ\n단어열이 표현되는 abc_list를 통해 num_list에 접근해서 진짜 값을 알아내는 식으로 숫자를 넣었고,\n그다음부터는 그냥 연산자가 나올때까지 stack에 넣고\n연산자가 나올때마다 stack에서 2개를 pop해주고\n두번째를 a, 첫번째를 b로 정한뒤 연산을 수행하고\n결과를 다시 스택에 넣는다.\n","title":"백준 1935 후위 표기식 2","type":"posts"},{"content":"https://www.acmicpc.net/problem/17299\n17298 오큰수와 매우 비슷한 문제다. 오큰수는 그냥 원소의크기만 비교했다면, 오등큰수는 Counter[원소] 의 크기를 비교하면 된다.\n첫풀이\nfrom collections import Countern=int(input())a=list(map(int,input().split())) b=[-1]*n #정답c=Counter(a) #카운터로 빈도수 세기s=list()for i in range(n): while s and c[s[-1][0]]\u0026lt;c[a[i]]: ##만약 스택의 마지막의 빈도수가 새로운 빈도수보다 적을때까지 q,r=s.pop() #싹다처내고 b[r]=a[i] #정답인덱스에 오동큰수 업데이트 s.append([a[i],i]) #원소값과 원소자릿값 묶어서 넣기 print(* b) #정답싹다꺼내기 좀더 깔끔하게 바꾼거\n굳이 원소가 들어가지않고, c[원소] 즉 빈도수가 들어있어도 상관없다.\nfrom collections import Countern=int(input())a=list(map(int,input().split())) b=[-1]*n #정답c=Counter(a) #카운터로 빈도수 세기s=list()for i in range(n): cnt=c[a[i]] #a[i]의 빈도수 while s and s[-1][0]\u0026lt; cnt: ##만약 스택의 마지막의 빈도수가 새로운 빈도수보다 적을때까지 q,r=s.pop() #싹다처내고 b[r]=a[i] #정답인덱스에 오동큰수 업데이트 s.append([cnt,i]) #원소의 빈도수와 원소자릿값 묶어서 넣기 print(* b) #정답싹다꺼내기 ","date":"11 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/011-%EB%B0%B1%EC%A4%80-17299-%EC%98%A4%EB%93%B1%ED%81%B0%EC%88%98-g3/","section":"Posts","summary":"https://www.acmicpc.net/problem/17299\n17298 오큰수와 매우 비슷한 문제다. 오큰수는 그냥 원소의크기만 비교했다면, 오등큰수는 Counter[원소] 의 크기를 비교하면 된다.\n첫풀이\nfrom collections import Countern=int(input())a=list(map(int,input().split())) b=[-1]*n #정답c=Counter(a) #카운터로 빈도수 세기s=list()for i in range(n): while s and c[s[-1][0]]\u003cc[a[i]]: ##만약 스택의 마지막의 빈도수가 새로운 빈도수보다 적을때까지 q,r=s.pop() #싹다처내고 b[r]=a[i] #정답인덱스에 오동큰수 업데이트 s.append([a[i],i]) #원소값과 원소자릿값 묶어서 넣기 print(* b) #정답싹다꺼내기 좀더 깔끔하게 바꾼거\n굳이 원소가 들어가지않고, c[원소] 즉 빈도수가 들어있어도 상관없다.\nfrom collections import Countern=int(input())a=list(map(int,input().split())) b=[-1]*n #정답c=Counter(a) #카운터로 빈도수 세기s=list()for i in range(n): cnt=c[a[i]] #a[i]의 빈도수 while s and s[-1][0]\u003c cnt: ##만약 스택의 마지막의 빈도수가 새로운 빈도수보다 적을때까지 q,r=s.pop() #싹다처내고 b[r]=a[i] #정답인덱스에 오동큰수 업데이트 s.append([cnt,i]) #원소의 빈도수와 원소자릿값 묶어서 넣기 print(* b) #정답싹다꺼내기","title":"백준 17299 오등큰수 G3","type":"posts"},{"content":"https://www.acmicpc.net/problem/17298\n17298번: 오큰수첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에 수열 A의 원소 A1, A2, \u0026hellip;, AN (1 ≤ Ai ≤ 1,000,000)이 주어진다.www.acmicpc.net\n골드치고 쉽다고 생각했는데 역시나였다.\n처음에 값이 중복될수 있다는 생각을 못하고 삽질을 많이 했다.\n# 기본값 -1이 n개 들어있는 정답을 만들어 준다.\nstack에 처음숫자를 넣고 (스택에 추가할때 속도를 위해 원소값과 원소의 자릿값을 같이 묶어 넣는다)\na의를 for i in range(len(a))로 돌리면서, 만일 a[i]가 stack[-1]보다 작다면 스택에 넣고, (스택의 마지막수보다 작다면 스택의 모든 원소보다 작다), 크다면 stack이 비거나 stack[-1]이 a[i]보다 작을때까지\n정답[stack에 들어있는 원소의 자릿값]을 a[i]로 바꿔준다.\n이후 [a[i],i] 즉 원소와 자릿값을 stack에 추가시킨다.\nn=int(input())a=list(map(int,input().split()))b=[-1]*nstack=[] for i in range(len(a)): while stack and stack[-1][0]\u0026lt;a[i]: q,r=stack.pop() #q는 원소, r은 자릿값 b[r]=a[i] stack.append([a[i],i]) #a의 원소와 자릿값print(*b) ","date":"11 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/010-%EB%B0%B1%EC%A4%80-17298-%EC%98%A4%ED%81%B0%EC%88%98/","section":"Posts","summary":"https://www.acmicpc.net/problem/17298\n17298번: 오큰수첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에 수열 A의 원소 A1, A2, …, AN (1 ≤ Ai ≤ 1,000,000)이 주어진다.www.acmicpc.net\n골드치고 쉽다고 생각했는데 역시나였다.\n처음에 값이 중복될수 있다는 생각을 못하고 삽질을 많이 했다.\n# 기본값 -1이 n개 들어있는 정답을 만들어 준다.\nstack에 처음숫자를 넣고 (스택에 추가할때 속도를 위해 원소값과 원소의 자릿값을 같이 묶어 넣는다)\n","title":"백준 17298 오큰수","type":"posts"},{"content":"https://www.acmicpc.net/problem/10799\n10799번: 쇠막대기여러 개의 쇠막대기를 레이저로 절단하려고 한다. 효율적인 작업을 위해서 쇠막대기를 아래에서 위로 겹쳐 놓고, 레이저를 위에서 수직으로 발사하여 쇠막대기들을 자른다. 쇠막대기와 레이저www.acmicpc.net\n문제는 간단한데 문제가 뭔소린지 이해하기가 어려웠다.\na=input()stack=[]answer=0for i in range(len(a)): if a[i]==\u0026#39;(\u0026#39;: stack.append(\u0026#39;(\u0026#39;) else: if a[i-1]==\u0026#39;(\u0026#39;: #레이저일경우 stack.pop() #레이저의시작을 제거하고 answer+=len(stack) #잘린막대기의 갯수를 더한다 else: #막대기일경우 answer+=1 #막대기의 끝을 더하고 stack.pop() #막대기를빼줌print(answer) ","date":"10 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/009-%EB%B0%B1%EC%A4%80-10799-%EC%87%A0%EB%A7%89%EB%8C%80%EA%B8%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/10799\n10799번: 쇠막대기여러 개의 쇠막대기를 레이저로 절단하려고 한다. 효율적인 작업을 위해서 쇠막대기를 아래에서 위로 겹쳐 놓고, 레이저를 위에서 수직으로 발사하여 쇠막대기들을 자른다. 쇠막대기와 레이저www.acmicpc.net\n문제는 간단한데 문제가 뭔소린지 이해하기가 어려웠다.\na=input()stack=[]answer=0for i in range(len(a)): if a[i]=='(': stack.append('(') else: if a[i-1]=='(': #레이저일경우 stack.pop() #레이저의시작을 제거하고 answer+=len(stack) #잘린막대기의 갯수를 더한다 else: #막대기일경우 answer+=1 #막대기의 끝을 더하고 stack.pop() #막대기를빼줌print(answer)","title":"백준 10799 쇠막대기","type":"posts"},{"content":"https://www.acmicpc.net/problem/17413\ns=input()answer=[] ##정답모을거stack1=[] ## 그냥단어모을거stack2=[] ## 태그모을거for i in s: if i==\u0026#39;\u0026lt;\u0026#39;: ##태그가시작되면 while stack1: ##그전에 있던 단어를 거꾸로 정답에 입력 answer.append(stack1.pop()) stack2.append(i) ##태그 입력 elif i==\u0026#39;\u0026gt;\u0026#39;: ##태그가끝나면 stack2.append(i) while stack2: ##태그를 하나씩 정답에 입력 answer.append(stack2.pop(0)) stack2=[] #초기화 elif stack2: #태그가진행중이면 stack2.append(i) #태그에 계속입력 elif i!=\u0026#39; \u0026#39;: #태그가 없고 단어가들어오면 stack1.append(i) else: #만일 단어의 끝이면 while stack1: #단어를 거꾸로입력하고 answer.append(stack1.pop()) answer.append(\u0026#39; \u0026#39;) #공백하나추가while stack1: #남은단어있으면 answer.append(stack1.pop()) #거꾸로해서 추가print(\u0026#39;\u0026#39;.join(answer)) 생각해보니깐 꼭 리스트를 안말들어도\n단어더하기식으로해도됨!\n","date":"10 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/008-%EB%B0%B1%EC%A4%80-17413.-%EB%8B%A8%EC%96%B4%EB%92%A4%EC%A7%91%EA%B8%B02/","section":"Posts","summary":"https://www.acmicpc.net/problem/17413\ns=input()answer=[] ##정답모을거stack1=[] ## 그냥단어모을거stack2=[] ## 태그모을거for i in s: if i=='\u003c': ##태그가시작되면 while stack1: ##그전에 있던 단어를 거꾸로 정답에 입력 answer.append(stack1.pop()) stack2.append(i) ##태그 입력 elif i=='\u003e': ##태그가끝나면 stack2.append(i) while stack2: ##태그를 하나씩 정답에 입력 answer.append(stack2.pop(0)) stack2=[] #초기화 elif stack2: #태그가진행중이면 stack2.append(i) #태그에 계속입력 elif i!=' ': #태그가 없고 단어가들어오면 stack1.append(i) else: #만일 단어의 끝이면 while stack1: #단어를 거꾸로입력하고 answer.append(stack1.pop()) answer.append(' ') #공백하나추가while stack1: #남은단어있으면 answer.append(stack1.pop()) #거꾸로해서 추가print(''.join(answer)) 생각해보니깐 꼭 리스트를 안말들어도\n단어더하기식으로해도됨!\n","title":"백준 17413. 단어뒤집기2","type":"posts"},{"content":"https://www.acmicpc.net/problem/1158\n1158번: 요세푸스 문제첫째 줄에 N과 K가 빈 칸을 사이에 두고 순서대로 주어진다. (1 ≤ K ≤ N ≤ 5,000)www.acmicpc.net\n진짜 쉬운 문제인데 그래도 집중안하면 답이 안나온다.\nn,k=map(int,input().split())num=0 l=[x for x in range(1,n+1)]answer=[] for _ in range(n): num += (k-1) #처음에 리스트가 0부터 시작하니깐 빠지고, 그 다음부턴 리스트가 1개씩 없어지니깐 빠짐 if num\u0026gt;=len(l): num= num%len(l) answer.append(l.pop(num)) answer=list(map(str,answer))print(\u0026#39;\u0026lt;\u0026#39;+\u0026#39;, \u0026#39;.join(answer)+\u0026#39;\u0026gt;\u0026#39;) 파이썬의 deque를 이용한 방법\nfrom collections import dequen,k=map(int,input().split())num=0 l=deque([x for x in range(1,n+1)])answer=[]for _ in range(n): l.rotate(1-k) #음수이므로 앞의값들을 잘라내고 뒤에 붙여넣는다. answer.append(l.popleft()) print(\u0026#39;\u0026lt;\u0026#39;,end=\u0026#39;\u0026#39;)print(*answer, sep=\u0026#39;, \u0026#39;, end=\u0026#39;\u0026#39;)print(\u0026#39;\u0026gt;\u0026#39;) ","date":"8 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/007-%EB%B0%B1%EC%A4%80-1158-%EC%9A%94%EC%84%B8%ED%94%84%EC%8A%A4-%EB%AC%B8%EC%A0%9C/","section":"Posts","summary":"https://www.acmicpc.net/problem/1158\n1158번: 요세푸스 문제첫째 줄에 N과 K가 빈 칸을 사이에 두고 순서대로 주어진다. (1 ≤ K ≤ N ≤ 5,000)www.acmicpc.net\n진짜 쉬운 문제인데 그래도 집중안하면 답이 안나온다.\nn,k=map(int,input().split())num=0 l=[x for x in range(1,n+1)]answer=[] for _ in range(n): num += (k-1) #처음에 리스트가 0부터 시작하니깐 빠지고, 그 다음부턴 리스트가 1개씩 없어지니깐 빠짐 if num\u003e=len(l): num= num%len(l) answer.append(l.pop(num)) answer=list(map(str,answer))print('\u003c'+', '.join(answer)+'\u003e') 파이썬의 deque를 이용한 방법\nfrom collections import dequen,k=map(int,input().split())num=0 l=deque([x for x in range(1,n+1)])answer=[]for _ in range(n): l.rotate(1-k) #음수이므로 앞의값들을 잘라내고 뒤에 붙여넣는다. answer.append(l.popleft()) print('\u003c',end='')print(*answer, sep=', ', end='')print('\u003e')","title":"백준 1158: 요세프스 문제","type":"posts"},{"content":"https://www.acmicpc.net/problem/1406\n처음에는 하나하나 노가다로 풀었는데, 그러면 무조건 시간초과가 나오게 된다.\n해결방법은 스택이 2개있고 그 사이에 커서가 있다고 생각하는 것이다.\n만일 L이라면, 스택 1의 출력을 스택 2에 입력하면 된다 (스택 1이 비었으면 무시)\n만일 D라면, 스택 2의 출력을 스택 1에 입력하면 된다(스택2가 비었으면 무시)\n만일 B라면, 스택 1의 맨 위를 날리면 된다. (역시 비면 무시)\n만일 p라면, 스택 1에 새 문자를 추가하면 된다.\n풀이를 알고 나면 정말 간단한데 풀이가 떠올르지 않는다.\nstack1 =list(input().strip())stack2 =[]M=int(input())for _ in range(M): order=input().split() if order[0]==\u0026#39;L\u0026#39;: if stack1: stack2.append(stack1.pop()) elif order[0]==\u0026#39;D\u0026#39;: if stack2: stack1.append(stack2.pop()) elif order[0]==\u0026#39;B\u0026#39;: if stack1: stack1.pop() elif order[0]==\u0026#39;P\u0026#39;: stack1.append(order[1]) print(\u0026#39;\u0026#39;.join(stack1 + list(reversed(stack2)))) ","date":"7 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/006-%EB%B0%B1%EC%A4%80-1406.-%EC%97%90%EB%94%94%ED%84%B0/","section":"Posts","summary":"https://www.acmicpc.net/problem/1406\n처음에는 하나하나 노가다로 풀었는데, 그러면 무조건 시간초과가 나오게 된다.\n해결방법은 스택이 2개있고 그 사이에 커서가 있다고 생각하는 것이다.\n만일 L이라면, 스택 1의 출력을 스택 2에 입력하면 된다 (스택 1이 비었으면 무시)\n만일 D라면, 스택 2의 출력을 스택 1에 입력하면 된다(스택2가 비었으면 무시)\n만일 B라면, 스택 1의 맨 위를 날리면 된다. (역시 비면 무시)\n만일 p라면, 스택 1에 새 문자를 추가하면 된다.\n풀이를 알고 나면 정말 간단한데 풀이가 떠올르지 않는다.\n","title":"백준 1406. 에디터","type":"posts"},{"content":"스택 (stack)은 기본적인 자료구조 중 하나로, 컴퓨터 프로그램을 작성할 때 자주 이용되는 개념이다. 스택은 자료를 넣는 (push) 입구와 자료를 뽑는 (pop) 입구가 같아 제일 나중에 들어간 자료가 제일 먼저 나오는 (LIFO, Last in First out) 특성을 가지고 있다.\n1부터 n까지의 수를 스택에 넣었다가 뽑아 늘어놓음으로써, 하나의 수열을 만들 수 있다. 이때, 스택에 push하는 순서는 반드시 오름차순을 지키도록 한다고 하자. 임의의 수열이 주어졌을 때 스택을 이용해 그 수열을 만들 수 있는지 없는지, 있다면 어떤 순서로 push와 pop 연산을 수행해야 하는지를 알아낼 수 있다. 이를 계산하는 프로그램을 작성하라.\n입력 # 첫 줄에 n (1 ≤ n ≤ 100,000)이 주어진다. 둘째 줄부터 n개의 줄에는 수열을 이루는 1이상 n이하의 정수가 하나씩 순서대로 주어진다. 물론 같은 정수가 두 번 나오는 일은 없다.\n출력 # 입력된 수열을 만들기 위해 필요한 연산을 한 줄에 한 개씩 출력한다. push연산은 +로, pop 연산은 -로 표현하도록 한다. 불가능한 경우 NO를 출력한다.\n스택의 개념을 활용하는 문제이다.\nanswer=비교할 수열, stack은 스택 구조, start는 push할 순서의 수, cnt는 비교할 수열의 인덱스,answer_list는 기호 저장용이다\n1.먼져 입력으로 수열을 만들어 준 뒤, \b 스택의 마지막 수가 수열의 첫 번째 수가 될때까지 push한다.\nstart+=1 , answer_list.append(\u0026rsquo;+')\n스택의 마지막 수가 수열의[cnt]와 같다면, 스텍의 마지막 수를 pop()한다. 이후 다음 원소 비교를 위하여 cnt를 증가시킨다. cnt+=1, answer_list.append(\u0026rsquo;-')\npop 이후 스택의 마지막 수가 수열의 cnt보다 크다면, 이 수를 없앨 방법이 없으므로 \u0026lsquo;NO\u0026rsquo;를 리턴하고 종료한다. 만일 answer의 끝까지 이가 반복되었다면, 수열만들기에 성공한 거니깐 answer_list를 출력한다.\n개어렵다\nn=int(input())answer=[]stack=[-1]for _ in range(n): answer.append(int(input()))answer_list=[]start=1cnt=0while True: if stack[-1] \u0026lt; answer[cnt]: stack.append(start) answer_list.append(\u0026#39;+\u0026#39;) start+=1 elif stack[-1] == answer[cnt]: stack.pop() answer_list.append(\u0026#39;-\u0026#39;) cnt+=1 else: print(\u0026#39;NO\u0026#39;) break if cnt==len(answer): for an in answer_list: print(an) break ","date":"7 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/005-%EB%B0%B1%EC%A4%80-1874.-%EC%8A%A4%ED%83%9D-%EC%88%98%EC%97%B4/","section":"Posts","summary":"스택 (stack)은 기본적인 자료구조 중 하나로, 컴퓨터 프로그램을 작성할 때 자주 이용되는 개념이다. 스택은 자료를 넣는 (push) 입구와 자료를 뽑는 (pop) 입구가 같아 제일 나중에 들어간 자료가 제일 먼저 나오는 (LIFO, Last in First out) 특성을 가지고 있다.\n1부터 n까지의 수를 스택에 넣었다가 뽑아 늘어놓음으로써, 하나의 수열을 만들 수 있다. 이때, 스택에 push하는 순서는 반드시 오름차순을 지키도록 한다고 하자. 임의의 수열이 주어졌을 때 스택을 이용해 그 수열을 만들 수 있는지 없는지, 있다면 어떤 순서로 push와 pop 연산을 수행해야 하는지를 알아낼 수 있다. 이를 계산하는 프로그램을 작성하라.\n","title":"백준 1874. 스택 수열","type":"posts"},{"content":"스택의 개념을 이용해서 \u0026lsquo;(\u0026lsquo;면 스택에 넣고 \u0026lsquo;)\u0026lsquo;면 스택에서 꺼내며, 꺼낼 \u0026lsquo;(\u0026lsquo;가 없거나 만일 끝났는데 \u0026lsquo;)가 있으면 vps가 아니다\nT=\nint\n(\ninput\n())\nfor\n_\nin\nrange\n(\nT\n):\nl=\n[]\na=\ninput\n()\nanswer=\n\u0026lsquo;YES\u0026rsquo;\nfor\ni\nin\na\n:\nif\ni==\n\u0026lsquo;(\u0026rsquo;\n:\nl.append\n(\ni\n)\nif\ni==\n\u0026lsquo;)\u0026rsquo;\n:\ntry\n:\nl.pop\n()\nexcept\n:\nanswer=\n\u0026lsquo;NO\u0026rsquo;\nbreak\nprint\n(\nanswer\nif\nnot\nl\nelse\n\u0026lsquo;NO\u0026rsquo;\n)\n","date":"6 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/004-%EB%B0%B1%EC%A4%80-9012-%EA%B4%84%ED%98%B8/","section":"Posts","summary":"스택의 개념을 이용해서 ‘(‘면 스택에 넣고 ‘)‘면 스택에서 꺼내며, 꺼낼 ‘(‘가 없거나 만일 끝났는데 ‘)가 있으면 vps가 아니다\nT=\nint\n(\ninput\n())\nfor\n_\nin\nrange\n(\nT\n):\nl=\n[]\na=\ninput\n()\nanswer=\n‘YES’\nfor\ni\nin\na\n:\nif\ni==\n‘(’\n:\nl.append\n(\ni\n)\nif\ni==\n‘)’\n:\ntry\n:\nl.pop\n()\nexcept\n:\nanswer=\n‘NO’\nbreak\nprint\n(\nanswer\nif\nnot\nl\nelse\n‘NO’\n)\n","title":"백준 9012 괄호","type":"posts"},{"content":"n=\nint\n(\ninput\n())\nl=\n[]\nfor\n_\nin\nrange\n(\nn\n):\norder=\ninput\n()\nif\n\u0026lsquo;push\u0026rsquo;\nin\norder\n:\na\n,\nb=order.split\n()\nl.append\n(\nb\n)\nelif\n\u0026lsquo;pop\u0026rsquo;\nin\norder\n:\nif\nl\n:\nprint\n(\nl\n[\n-1\n])\ndel\nl\n[\n-1\n]\nelse\n:\nprint\n(\n-1\n)\nelif\n\u0026lsquo;size\u0026rsquo;\nin\norder\n:\nprint\n(\nlen\n(\nl\n))\nelif\n\u0026rsquo;empty'\nin\norder\n:\nif\nl\n:\nprint\n(\n0\n)\nelse\n:\nprint\n(\n1\n)\nelif\n\u0026rsquo;top'\nin\norder\n:\nif\nl\n:\nprint\n(\nl\n[\n-1\n])\nelse\n:\nprint\n(\n-1\n)\n","date":"6 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/003-%EB%B0%B1%EC%A4%80-10828-%EC%8A%A4%ED%83%9D/","section":"Posts","summary":"n=\nint\n(\ninput\n())\nl=\n[]\nfor\n_\nin\nrange\n(\nn\n):\norder=\ninput\n()\nif\n‘push’\nin\norder\n:\na\n,\nb=order.split\n()\nl.append\n(\nb\n)\nelif\n‘pop’\nin\norder\n:\nif\nl\n:\nprint\n(\nl\n[\n-1\n])\ndel\nl\n[\n-1\n]\nelse\n:\nprint\n(\n-1\n)\nelif\n‘size’\nin\norder\n:\nprint\n(\nlen\n(\nl\n))\nelif\n’empty'\nin\norder\n:\nif\nl\n:\nprint\n(\n0\n)\nelse\n:\nprint\n(\n1\n)\nelif\n’top'\nin\norder\n:\nif\nl\n:\nprint\n(\nl\n[\n-1\n])\nelse\n:\nprint\n(\n-1\n)\n","title":"백준 10828: 스택","type":"posts"},{"content":"풀이 1\nn\n,\nm=\nmap\n(\nint\n,\ninput\n()\n.split\n())\nboard=\nlist\n()\nfor\ni\nin\nrange\n(\nn\n):\nboard.append\n(\ninput\n())\nmin_numbers=\n[]\nfor\ni\nin\nrange\n(\nn\n-7\n):\nfor\nj\nin\nrange\n(\nm\n-7\n):\ncnt1 =\n0\ncnt2 =\n0\nfor\nk\nin\nrange\n(\ni\n,\ni+\n8\n):\nfor\nl\nin\nrange\n(\nj\n,\nj +\n8\n):\n체스판 규칙에 따라서 # (k+ㅣ)%2가 0인 board[k][j]는 모두 a색이어야 하고 # (k+ㅣ)%2가 1인 board[k][j]는 모두 b색이어야 한다. # if\n(\nk + l\n)\n%\n2\n==\n0\n:\nif\nboard\n[\nk\n][\nl\n]\n!=\n\u0026lsquo;W\u0026rsquo;\n:\ncnt1 +=\n1\nelse\n:\ncnt2 +=\n1\nelse\n:\nif\nboard\n[\nk\n][\nl\n]\n!=\n\u0026lsquo;B\u0026rsquo;\n:\ncnt1 +=\n1\nelse\n:\ncnt2 +=\n1\nprint\n(\ncnt1\n,\nend=\n\u0026lsquo;|||\u0026rsquo;\n)\nprint\n(\ncnt2\n)\n좀더 똑똑하게푸는법\nboard에서 자른 8*8크기를 각각\nw로시작하는 체스판으로 채우는수 + B로 시작하는 체스판으로 채우는 수= 항상 64이다\nn\n,\nm=\nmap\n(\nint\n,\ninput\n()\n.split\n())\nboard=list() # for i in range(n): # board.append(input()) # minnum=\n64\nfor\ni\nin\nrange\n(\nn\n-7\n):\nfor\nj\nin\nrange\n(\nm\n-7\n):\ncnt =\n0\nfor\nk\nin\nrange\n(\ni\n,\ni+\n8\n):\nfor\nl\nin\nrange\n(\nj\n,\nj +\n8\n):\nif\n(\nk + l\n)\n%\n2\n==\n0\n:\nif\nboard\n[\nk\n][\nl\n]\n!=\n\u0026lsquo;W\u0026rsquo;\n:\ncnt +=\n1\nelse\n:\nif\nboard\n[\nk\n][\nl\n]\n!=\n\u0026lsquo;B\u0026rsquo;\n:\ncnt +=\n1\nif\ncnt\u0026gt;=\n32\n:\ncnt=\n64\n-cnt\nminnum=\nmin\n(\ncnt\n,\nminnum\n)\nprint\n(\nminnum\n)\n혹시나 입력하기귀찮을까봐\nboard=\n[\n\u0026lsquo;BBBBBBBBWBWBW\u0026rsquo;\n,\n\u0026lsquo;BBBBBBBBBWBWB\u0026rsquo;\n,\n\u0026lsquo;BBBBBBBBWBWBW\u0026rsquo;\n,\n\u0026lsquo;BBBBBBBBBWBWB\u0026rsquo;\n,\n\u0026lsquo;BBBBBBBBWBWBW\u0026rsquo;\n,\n\u0026lsquo;BBBBBBBBBWBWB\u0026rsquo;\n,\n\u0026lsquo;BBBBBBBBWBWBW\u0026rsquo;\n,\n\u0026lsquo;BBBBBBBBBWBWB\u0026rsquo;\n,\n\u0026lsquo;WWWWWWWWWWBWB\u0026rsquo;\n,\n\u0026lsquo;WWWWWWWWWWBWB\u0026rsquo;\n]\n","date":"5 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/002-%EB%B0%B1%EC%A4%80-1018/","section":"Posts","summary":"풀이 1\nn\n,\nm=\nmap\n(\nint\n,\ninput\n()\n.split\n())\nboard=\nlist\n()\nfor\ni\nin\nrange\n(\nn\n):\nboard.append\n(\ninput\n())\nmin_numbers=\n[]\nfor\ni\nin\nrange\n(\nn\n-7\n):\nfor\nj\nin\nrange\n(\nm\n-7\n):\ncnt1 =\n0\ncnt2 =\n0\nfor\nk\nin\nrange\n(\ni\n,\ni+\n8\n):\nfor\nl\nin\nrange\n(\nj\n,\nj +\n8\n):\n체스판 규칙에 따라서 # (k+ㅣ)%2가 0인 board[k][j]는 모두 a색이어야 하고 # (k+ㅣ)%2가 1인 board[k][j]는 모두 b색이어야 한다. # if\n","title":"백준 1018","type":"posts"},{"content":" 블로그 시작 # ","date":"5 March 2022","externalUrl":null,"permalink":"/posts/2022-tistory/001-%ED%99%98%EC%98%81%ED%95%A9%EB%8B%88%EB%8B%A4/","section":"Posts","summary":"블로그 시작 # ","title":"환영합니다!","type":"posts"}]