728x90

추상화

  • 중요한 것은 남기고, 불필요한 것은 제거하는 것

캡슐화(encapsulation)

  • 관련된 것을 잘 모아서 가지고 있는 것
    → 응집도(Cohesion)가 높다고 표현함

 

* 좋은 객체는 응집도는 높고 결합도(Coupling)는 낮다.

  • 응집도 : 응집도는 모듈의 독립성을 나타내는 개념으로, 모듈 내부 구성요소 간 연관 정도
  • 결합도 : 소프트웨어 구조에서 모듈 간의 관련성을 측정하는 척도

객체의 역할, 책임 그리고 협력

  • 좋은 객체란 역할과 책임에 충실하면서 다른 객체와 잘 협력하여 동작하는 객체
  • 나쁜 객체란 여러가지 역할을 한 가지 객체에 부여하거나, 이름과 맞지 않는 속성과 기능을 가지도록 하거나 제대로 동작하지 않는 객체
    • 다른 객체와도 동작이 매끄럽지 않는 객체

다형성

  • 프로그램 언어의 각 요소들(상수, 변수, 식, 오브젝트, 함수, 메소드 등)이 다양한 자료형(type)에 속하는 것이 허가되는 성질

메소드 오버로딩(Overloading)

  • 메서드의 이름은 같고 매개변수의 갯수나 타입이 다른 함수를 정의하는 것을 의미한다.
  • 리턴값만 다르게 갖는 오버로딩은 작성할 수 없다.

패키지

  • 클래스는 패키지를 이용하여 관련된 클래스를 관리한다.
  • 폴더와 거의 같은 기능을 제공한다고 생각하면 된다.
  • 이름 규칙
    • 보통 도메인 이름을 거꾸로 적은 후에 프로젝트 이름 등을 붙여서 만든다.

패키지 선언 방법

  • package 패키지명 : 주석문이나 빈줄을 제외하고 가장 윗 줄에 선언한다.
  • 사용할 때는 import 패키지명.클래스명;
  • cmd를 이용해서 컴파일시
    • javac -d 경로명 *.java
    • -d 옵션을 사용
  • 실행시
    • java 패키지명.클래스명

상속

  • ‘OO는 OO다. OO는 OO의 종류 중 하나다.’ 라고 표현할 수 있다면 상속관계이다.
  • is-a 관계 혹은 kind of 관계라고 말하기도 한다.
  • 상속 = 일반화 + 확장

상속 선언 방법

[접근제한자] [abstract | final] class 클래스명 extends 부모클래스명 {
	...
}
  • 아무것도 상속받지 않으면 자동으로 java.lang.Object 를 상속받음
  • 최상위 부모 클래스는 아무것도 상속받지 않으므로 결국 모든 클래스는 Object 를 상속받으므로 Object 의 자손이다.

다형성 - 메소드 오버라이딩(Overriding)

  • 상위 클래스의 메서드를 하위 클래스가 재정의 하는 것
  • 메서드의 이름은 물론 파라미터의 갯수나 타입도 동일해야 하며, 주로 상위 클래스의 동작을 상속받은 하위 클래스에서 변경하기 위해 사용
  • 부모 타입으로 자손 타입을 참조할 수 있다.
  • 이때, 메소드가 오버라이딩된 경우 무조건 자식의 메소드가 실행된다.
  • 메소드와 별개로, 필드는 Type을 따라감
// 두 클래스 모두 run() 메소드를 가지고 있을 경우
Car car = new Bus();
car.run();
// Bus의 run 메소드가 실행됨

중요한 필드는 은닉하고, 필드는 메소드를 통해서만 접근해서 사용해야 한다.

 

Object가 오버라이딩하도록 제공하는 메소드

  • toString() / equals() / hashCode()
  • Intellij의 원하는 클래스에서 <마우스 우클릭> 후 생성하면 기본 형식으로 오버라이딩된다.
  • 형식은 원하는대로 바꿔도 된다.
  • Object 메소드 오버라이딩 예시

생성자

  • 인스턴스를 생성할 때 사용한다.
  • 어떤 값을 가진채로 인스턴스가 만들어지게 하고 싶을 때 사용
  • 클래스 작성시 생성자를 따로 만들지 않았다면 자동으로 기본 생성자가 생성됨
    • 기본생성자 : 매개변수를 하나도 받지 않는 생성자

생성자 오버로딩

  • 생성자는 매개변수의 개수가 다르거나, 타입이 다르다면 여러개를 가질 수 있다.
  • ex)
public class Car {
    String name;
    int speed;
    
		// 기본생성자
    public Car() {
        System.out.println("car() 생성자 호출");
    }
    
    // 매개변수가 있는 생성자 (오버로딩)
    public Car(String name) {
        this.name = name;
    }

    public Car(int speed, String name) {
        this.name = name;
        this.speed = speed;
    }
}

this()

  • this() 생성자
    • 자기 자신의 생성자를 말한다.
    • 생성자 안에서만 사용가능하다.
    • 인스턴스 자기 자신을 참조할 때 사용하는 키워드이다.
    • 생성자 안에서 super() 생성자를 호출하는 코드 다음 or 첫번째 줄에 위치해야 한다.
      •  

super()

  • super() 생성자
    • 부모 생성자를 의미한다.
    • 부모의 생성자를 호출할 때 사용한다.
    • 생성자 안에서만 사용가능하다.
    • 생성자 안에서 첫번째 줄에만 올 수 있다.
    • 사용자가 생성자를 호출하는 코드를 작성하지 않았다면 자동으로 부모의 기본 생성자가 호출된다.
    • 부모 클래스가 기본 생성자를 가지고 있지 않다면, 사용자는 반드시 직접 super() 생성자를 호출하는 코드를 작성
728x90
728x90


뭐지.. 왜 문제 복사가 안되는거지!!!??

 

암튼.. 이제부터 블로그 제대로 작성할려고 노력..!


문제 정리

 

일단 이 문제는 제한사항을 봐야한다. (코딩테스트의 핵심은 제한사항을 잘 봐야한다!)

 

  1. 중복이 없다. 
    - 이말은 lost와 reverse 내의 원소 값이 유일해야한다는 점!
    => lost = [1,1,2,3] reserve = [2,2,3,4] 가 될 수 없다.

  2. 여벌의 체육복이 있는 학생(reserve) 도 도난 당했을 수 있다.
    - lost 값에 reserve 값이 공통적으로 존재할 수 있다는 의미!
    => lost = [1,2,3] reserve = [3,4,5] 처럼..
    그래서 문제를 보면 여벌의 체육복은 1벌 밖에 없으니 3번 학생은 체육복을 빌려줄수가 없다

>> 즉, lost와 reserve에 같은 값이 있다면 그 값은 reserve에서 제외시켜줘야한다.

 

 

step1. reserve의 요소들 중 lost에 동일하게 존재하는 값들은 모두 제거해줘야 한다. 

reserve_set = set(reserve) - set(lost)
lost_set = set(lost) - set(reserve)

//set 차집합 계산법
a = [1,3,5,6]
b = [2,4,6,8]
c = set(a) - set(b) //6이 공통

print(c) 
[1,3,5]

그걸 set 자료형으로 구현했다. set은 집합 자료형이므로객체끼리 집합 연산이 가능하다. 그래서 - 연산자를 사용하여 각 집합별로 차집합을 수행할 수 있다. 

 

step2. 예시를 보자

n lost reserve return
5 [2,4] [3,5] 5

 

 

학생 1 학생 2 학생 3 학생 4 학생 5
  x o x o

 

3, 5 번 학생은 여벌의 체육복을 갖고 있다. 4번 학생은 3번, 5번 학생으로부터 체육복을 받을 수 있다.

 

           - 4번 학생이 3번 학생의 체육복을 받았다고 가정. => 체육 시간 참가 가능

           - 2번 학생은 3번 학생의 체육복을 받을 수 있음에도 3번이 4번한테 줬기에 못 받음. => 체육 시간 참가 불가능

           - 4번 학생은 굳이 3번 학생이 빌려주지 않아도 5번 학생의 체육복을 받을 수 있음.

 

결론 : 체육복은 양 옆 학생에게 빌려줄 수 있으므로 왼쪽 요소부터 탐색 해야 함.

 

step3. 문제 풀이

def solution(n, lost, reserve):
    reserve_set = set(reserve) - set(lost)
    lost_set = set(lost) - set(reserve)
    
    for student in reserve_set:
        if student - 1 in lost_set:
            lost_set.remove(student - 1)
        elif student + 1 in lost_set:
            lost_set.remove(student + 1)
            
    answer = n - len(lost_set)
    return answer

reserve의 student -1 요소는 reserve 학생의 왼쪽 학생부터 빌려주고

만약에 없다면 오른쪽 학생을 주는 식으로 작성하면 끝

728x90
728x90

3월 25일 월요일 학습 내용


객체지향

  • 객체들의 모임으로 파악하고자 하는 것
  • 각각의 객체는 메시지를 주고받고, 데이터를 처리할 수 있음
  • 객체지향 관점에서의 서점
    • 책을 관리하는 것은? <책장>
    • 손님을 관리하는 것은? <방명록>
    • 돈을 관리하는 것은? <금고>


객체 지향 프로그래밍

  • 객체 지향 프로그래밍은 컴퓨터 프로그래밍의 패러다임 중 하나이다.  
  • “객체”들의 모임으로 파악하고자 하는 것
    → 각각의 객체는 메시지를 주고받고, 데이터를 처리할 수 있다.
  • 클래스 class
  • 오브젝트 object
  • 인스턴스 instance
  • 참조형 변수 reference variable
  • 핵심 : 메시징 => 객체가 어떻게 해야 하는가가 아니라 무엇을 해야하는가를 설명한다는 것 (메소드)


final 키워드

  • 변수(variable), 메서드(method), 또는 클래스(class)에 사용 가능
  • 변수에 final을 붙이면 이 변수는 수정할 수 없다는 의미
  • 메서드에 final을 붙이면 override를 제한
  • final 키워드를 클래스에 붙이면 상속 불가능 클래스


접근제한자

  • public, protected, package, private가 있다.
    • public : 외부 클래스가 자유롭게 사용할 수 있도록 한다.
    • protected : 같은 패키지 또는 자식 클래스에서 사용할 수 있도록 한다.
    • default**(**package) : 같은 패키지에서 사용할 수 있도록 한다.
    • private : 외부 클래스에서 사용할 수 없도록 한다.접근제한자

  • package안의 하위 package가 있더라고 다른 package로 보기 때문에 사용하고자하는 package를 import 해줘야한다.

 

<예시 코드>

  • com.example.util 패키지 안에 있는 Calculator 클래스
package com.example.util;

public class Calculator {
    public int plus(int a, int b){
        return a+b;
    }
    public int minus(int a, int b){
        return a-b;
    }
}
  • com.example.main 패키지 안에 있는 CalculatorTest 클래스
package com.example.main;

// util 패키지에 있는 클래스를 사용하기 위해 import
import com.example.util.Calculator; 

public class CalculatorTest {
    public static void main(String[] args) {
        Calculator cal = new Calculator();
        System.out.println(cal.plus(1,5));
        System.out.println(cal.minus(5,2));
    }
}

클래스

    • 자바에서 클래스는 설계도라고 본다.
    • 클래스는 필드(속성, Field)와 메소드(행위, 기능, Method)를 가진다.
    • 클래스에는 객체를 생성하기 위한 필드와 메소드가 정의되어 있어야한다.
    • 클래스로부터 객체를 만드는 과정을 인스턴스화라고 한다.
    • 하나의 클래스로부터 여러 객체를 만들 수 있음
    • 클래스로부터 만들어진 객체를 해당 클래스의 인스턴스라고 함
    • main 메소드가 없는 클래스는 실행되지 않음

 

필드(field)

  • 클래스가 가지는 속성
  • 다른 언어의 경우 멤버변수라고 하는 경우도 있다.
  • static 키워드가 함께 사용되는 필드를 클래스 필드, 함께 사용되지 않는 필드를 인스턴스 필드라고 한다.

필드 선언 방법

[접근제한자] [static] [final] 타입 필드명 [=초기값];

String name;
String address = "경기도 수원시";
public int age = 50;
protected boolean flag;​

 

  • 대괄호 안에 있는 내용은 생략가능하다는 뜻
  • 필드의 첫번째 글자는 소문자로 시작하는 것이 관례이며, 자바의 경우 흔히 카멜 케이스로 작성
  • 타입(type)은 기본형(boolean, byte, …)과 참조타입(class, 인터페이스, 배열) 등 사용가능
  • 초기값이 없는 경우
    • 참조형일 경우 null / boolean형일 경우 false / 나머지 기본형은 모두 0 으로 초기화된다.

 

클래스 선언

클래스 선언 방법

public class 클래스명 {

}
  • Dice 클래스와 클래스의 필드 및 메소드 선언
package com.example.util;

public class Dice {

    private int face;
    private int eye;

    public void roll(){
        eye = (int)(Math.random()*face) + 1;
    }
    public int getEye(){
        return eye;
    }
    public void setFace(int face) {
        this.face = face;
    }
}

 

객체 선언

객체 선언 방법

   클래스명 객체명 = new 클래스명();
// 참조타입 참조변수 new연산자 생성자
  • 메모리
    • new 연산자를 사용할 때마다 메모리에 인스턴스가 생성.
    • 인스턴스는 더 이상 참조되는 것이 없을 때, 보통 메모리 부족시 가비지 컬렉션(Garbage Collection)에 저장
    • static한 필드는 클래스가 로딩될 때 딱 한번 메모리에 올라가고 초기화
    • 인스턴스 메소드(static이 안붙은 메소드)는 인스턴스를 생성하고나서 레퍼런스 변 수를 이용해 사용
    • 클래스 메소드는 클래스명.메소드명() 으로 사용가능
    • 메소드 안에 선언된 변수들은 메소드가 실행될 때 메모리에 생성되었다가, 메소드가 종료될 때 사라짐
  • DiceTest 클래스에서 Dice 클래스 객체 생성
package com.example.main;

import com.example.util.Dice;

public class DiceTest {
    public static void main(String[] args) {
        Dice dice = new Dice();
        dice.setFace(6);
        dice.roll();
        int eye = dice.getEye();
        System.out.println(eye);
    }
}

 

메소드 선언

  • 객체지향의 핵심은 “메시징”이다.
  • 자율적인 객체는 스스로 정한 원칙에 따라 판단하고 스스로의 의지를 기반으로 행동하는 객체이다.
  • 객체가 어떤 행동을 하는 이유는 다른 객체로부터 요청을 수신했기 때문이다.

메소드 선언 방법

[접근제한자][static]리턴type 메소드이름([매개변수,...]) {
실행문..
}
    • 메소드 이름은 소문자로 시작하는 것이 관례이다.
    • 클래스의 메소드를 호출하려면
      • 클래스에 대한 인스턴스를 생성하거나
      • 래퍼런스 변수를 이용하여 메시지를 전송한다. (메소드 호출)
      • static 메소드는 인스턴스를 생성하지 않아도 호출할 수 있다.
  • 전달인자 : 메소드를 호출할 때 전달하는 실제 값
  • 매개변수 : 메소드 정의 부분에 나열되어있는 변수
728x90
728x90

완전탐색(Brute Force)이란?

- 무식하게 모든 경우를 탐색해보는 알고리즘이다.

- 정확성은100% 보장되나 속도가 최악이라는 단점이 있다.

- 문제에서 주어진 데이터가 맹루 적을때만 사용 가능하다.

- 완전 탐색은 딱히 알고리즘이라고 할 게 없지만, 완전탐색을 하기 위한 기법들은 여러 개가 있다.


1. 단순 브루트포스

- for 문과 if문 등으로 모든 케이스를 조사해보는 방법이다.

- 코테에 단독적으로 나오지 않는다. pass!!


2. 순열

- 완전 탐색의 대표적인 유형이다.

- 서로 다른 n개 중 r개를 중복없이 선택하여 나열하는 경우의 수이다.

- 순서가 상관이 있고 [1,2,3] 과 [3,2,1]은 다른 것이다.


 

순열

from itertools import permutations
list(permutations([1,2,3], 2)) # permutations(값, 몇개로 묶을지)
[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

 

조합 

- 순서 상관 없다. [1,2] 나 [2,1] 은 같다고 봄.

from itertools import combinations
list(combinations([1,2,3], 2))
[(1, 2), (1, 3), (2, 3)]

 

중복순열

from itertools import combinations_with_replacement
list(combinations_with_replacement([1,2,3], 2))
[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]

3. 재귀

- 자기 자신을 호출한다는 의미이다.

- 자신이 수행할 작업을 유사한 형태의 여러 조각으로 쪼갠 뒤 그 중 한 조각을 수행하고, 나머지는 자기 자신을 호출해 실행한다. 

- 반복문을 줄일 수 있지만 스택 오버플로우가 잘 발생한다는 점에서 주의해야 한다.

- 한번 함수의 상태를 저장하는 파라미터와 재귀 탈출 조건이 반드시 필요하고, return 문을 명확하게정의해야 한다.

 

ex. 간단한 재귀함수 예시(팩토리얼)

def recursive_factorial(n):
    if n <= 1:
        return 1
    return n * recursive_factorial(n-1)
    
print(recursive_factorial(5))
120

** 재귀함수는 주로 BFS/DFS에서 사용된다.


4. 비트마스크

- 2진수를 사용하여 연산하는 방식이다.

- 각 원소가 포함 or 불포함으로 구성되는 경우에 사용된다.

 

ex. [1,2,3,4] 의 부분집합을 만들때 비트마스크를 사용하면

[1, 2, 3, 4]
 1  1  1  1
 1  1  1  0
 1  1  0  1
 1  1  0  0
 ...

- 리스트의 길이만큼 비트 리스트를 만든 후에 원소가 포함되면 1, 아니면 0으로 구분해서 간단하게 표기할 수 있다.

- 비트 연산은 (&,|,^,~,<<,>>)이 가능하다.


5. BFS/DFS

- 이건 나중에 따로 정리


6. 백그래킹

- 해를 찾는 도중에 막히면 더 이상 깊이 들어가지 않고 이전 단계로 되돌아가서 해를 찾아나가는 기법이다.

- 불필요한 부분을 쳐내고(가지치기) 최대한 올바른 방향으로 나아가려는 방식이다.

- 백트래킹을 사용하는 대표적인 알고리즘이 DFS인데, DFS는 재귀를 통해 가능한 경로 중 유망한 경로만 찾아서 탐색을 수행한 뒤 다시 돌아와 그 중 가장 최적의 경로를 반환하는 것이다. 


* 완전탐색 시간복잡도

비트마스크 > DFS/BFS > Brute-Force > 재귀함수 > 순열 > 백트래킹

728x90

'Python' 카테고리의 다른 글

[Python] enumerate함수  (0) 2024.08.08
[Python] collections - Counter  (0) 2024.07.06
Python - Set()  (0) 2023.03.04
[Python] DFS & BFS  (0) 2023.02.18
[Python] 자료구조 : 큐(Queue) 개념 및 사용  (0) 2023.02.18
728x90

문제 설명

명함 지갑을 만드는 회사에서 지갑의 크기를 정하려고 합니다. 다양한 모양과 크기의 명함들을 모두 수납할 수 있으면서, 작아서 들고 다니기 편한 지갑을 만들어야 합니다. 이러한 요건을 만족하는 지갑을 만들기 위해 디자인팀은 모든 명함의 가로 길이와 세로 길이를 조사했습니다.

아래 표는 4가지 명함의 가로 길이와 세로 길이를 나타냅니다.

명함 번호가로 길이세로 길이
1 60 50
2 30 70
3 60 30
4 80 40

가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다. 이때의 지갑 크기는 4000(=80 x 50)입니다.

모든 명함의 가로 길이와 세로 길이를 나타내는 2차원 배열 sizes가 매개변수로 주어집니다. 모든 명함을 수납할 수 있는 가장 작은 지갑을 만들 때, 지갑의 크기를 return 하도록 solution 함수를 완성해주세요.


제한사항
  • sizes의 길이는 1 이상 10,000 이하입니다.
    • sizes의 원소는 [w, h] 형식입니다.
    • w는 명함의 가로 길이를 나타냅니다.
    • h는 명함의 세로 길이를 나타냅니다.
    • w와 h는 1 이상 1,000 이하인 자연수입니다.

입출력 예sizesresult
[[60, 50], [30, 70], [60, 30], [80, 40]] 4000
[[10, 7], [12, 3], [8, 15], [14, 7], [5, 15]] 120
[[14, 4], [19, 6], [6, 16], [18, 7], [7, 11]] 133

입출력 예 설명

입출력 예 #1
문제 예시와 같습니다.

입출력 예 #2
명함들을 적절히 회전시켜 겹쳤을 때, 3번째 명함(가로: 8, 세로: 15)이 다른 모든 명함보다 크기가 큽니다. 따라서 지갑의 크기는 3번째 명함의 크기와 같으며, 120(=8 x 15)을 return 합니다.

입출력 예 #3
명함들을 적절히 회전시켜 겹쳤을 때, 모든 명함을 포함하는 가장 작은 지갑의 크기는 133(=19 x 7)입니다.

 


풀이

def solution(sizes):
    w = [] # 가로 값을 담을 빈 리스트 생성
    h = [] # 세로 값
    for i in range(len(sizes)): # sizes는 2차원 리스트
        if sizes[i][0] >= sizes[i][1]: # 조건 : 가로>=세로
            w.append(sizes[i][0]) 
            h.append(sizes[i][1]) 
        else: # 명함번호2 해당
            h.append(sizes[i][0]) 
            w.append(sizes[i][1]) 
    return max(w) * max(h)

위와 같이 풀었는데 아래와 같이 푸는 방법도 있더라..

def solution(sizes):
    w = []
    h = []
    for i in range(len(sizes)):
    	sizes[i].sort()
        w = max(w, sizes[i][0])
	h = max(h, sizes[i][1])

    return w * h

각 w, h를 비교해서 둘 중 큰 값을 한 리스트에 넣고 나머지를 리스트로 만든다. 두 개의 리스트 중 가장 큰 값을 뽑아서 곱하면 된다.

  1. w, h 리스트를 만든다.
  2. for문을 돌면서 가로와 세로 2개의 모서리 w, h 중 큰 값은 가로 w리스트에, 작은 값은 세로 h리스트에 담는다.
  3. 가로/세로 중 가장 큰 값으로 만든 사각형의 넓이인 두 개의 리스트에서 가장 큰 값이 곱한 값이 답이다.
  4. w, h중 큰 값을 모아서 그 중 큰 값과, w, h중 작은 값을 모아서 그 중 큰 값을 곱하면 간단하게 끝난다.
728x90
728x90

1. 문제

2. 풀이과정

해당 문제는 단어가 1자리부터 최대 5자리까지만 존재하므로 모든 경우를 구한 뒤에 찾는 것으로 해결하였다.

모든 경우를 찾을 때는 중복순열을 활용하여 찾아주었다.

 

  1. 중복순열을 구해주는 product 함수를 사용하기 위해 product 모듈을 불러온다. from itertools import product
  2. 중복순열을 저장해 줄 리스트를 생성한다. result = list()
  3. 최소 1자리 단어부터 최대 5자리 단어까지 존재하므로 각 자리 개수만큼 반복하며 for i in range(1, 6)
  4. 모음 리스트에서 중복을 허용하여 해당 자리 단어를 리스트로 생성한다. li = list(product(["A", "E", "I", "O", "U"], repeat=i))
  5. 중복순열을 구한 뒤 해당 순열을 하나씩 불러오며 for j in li
  6. 각 순열을 하나의 단어로 만들어 결과에 한 단어로 저장한다. result.append(''.join(k for k in j))
  7. 전부 구한 중복순열의 결과를 사전순으로 정렬한다. result.sort()
  8. 정렬한 중복순열 리스트에서 해당 단어의 인덱스를 찾아 1을 더해준 값이 정답이다. 인덱스 값으로 찾았기 때문에 번호를 붙일 때는 1을 더해줘야 한다. answer = result.index(word) + 1
 

3. 소스코드

from itertools import product

def solution(word):
    answer = 0
    
    result = list()
    for i in range(1, 6):
        li = list(product(["A", "E", "I", "O", "U"], repeat=i))
        for j in li:
            result.append(''.join(k for k in j))
        
    result.sort()
    answer = result.index(word) + 1

    return answer
728x90
728x90

문제

우리는 사람의 덩치를 키와 몸무게, 이 두 개의 값으로 표현하여 그 등수를 매겨보려고 한다. 어떤 사람의 몸무게가 x kg이고 키가 y cm라면 이 사람의 덩치는 (x, y)로 표시된다. 두 사람 A 와 B의 덩치가 각각 (x, y), (p, q)라고 할 때 x > p 그리고 y > q 이라면 우리는 A의 덩치가 B의 덩치보다 "더 크다"고 말한다. 예를 들어 어떤 A, B 두 사람의 덩치가 각각 (56, 177), (45, 165) 라고 한다면 A의 덩치가 B보다 큰 셈이 된다. 그런데 서로 다른 덩치끼리 크기를 정할 수 없는 경우도 있다. 예를 들어 두 사람 C와 D의 덩치가 각각 (45, 181), (55, 173)이라면 몸무게는 D가 C보다 더 무겁고, 키는 C가 더 크므로, "덩치"로만 볼 때 C와 D는 누구도 상대방보다 더 크다고 말할 수 없다.

N명의 집단에서 각 사람의 덩치 등수는 자신보다 더 "큰 덩치"의 사람의 수로 정해진다. 만일 자신보다 더 큰 덩치의 사람이 k명이라면 그 사람의 덩치 등수는 k+1이 된다. 이렇게 등수를 결정하면 같은 덩치 등수를 가진 사람은 여러 명도 가능하다. 아래는 5명으로 이루어진 집단에서 각 사람의 덩치와 그 등수가 표시된 표이다.

이름(몸무게, 키)덩치 등수
A (55, 185) 2
B (58, 183) 2
C (88, 186) 1
D (60, 175) 2
E (46, 155) 5

위 표에서 C보다 더 큰 덩치의 사람이 없으므로 C는 1등이 된다. 그리고 A, B, D 각각의 덩치보다 큰 사람은 C뿐이므로 이들은 모두 2등이 된다. 그리고 E보다 큰 덩치는 A, B, C, D 이렇게 4명이므로 E의 덩치는 5등이 된다. 위 경우에 3등과 4등은 존재하지 않는다. 여러분은 학생 N명의 몸무게와 키가 담긴 입력을 읽어서 각 사람의 덩치 등수를 계산하여 출력해야 한다.

입력

첫 줄에는 전체 사람의 수 N이 주어진다. 그리고 이어지는 N개의 줄에는 각 사람의 몸무게와 키를 나타내는 양의 정수 x와 y가 하나의 공백을 두고 각각 나타난다.

출력

여러분은 입력에 나열된 사람의 덩치 등수를 구해서 그 순서대로 첫 줄에 출력해야 한다. 단, 각 덩치 등수는 공백문자로 분리되어야 한다.

제한

  • 2 ≤ N ≤ 50
  • 10 ≤ x, y ≤ 200

예제 입력 1 복사

5
55 185
58 183
88 186
60 175
46 155

예제 출력 1 복사

2 2 1 2 5

풀이

 

N = int(input()) # 전체 사람 수
people = [] # 사람 정보를 받을 빈 list 생성

for _ in range(N): 
    x, y = map(int, input().split()) # 키와 몸무게 입력
    people.append((x,y)) # 그 값을 people 리스트에 추가

for i in people:
    rank = 1 # 초기값은 전부 1
    for j in people:
        if i[0] < j[0] and i[1] < j[1]: # 키와 몸무게 둘 다 커야한다는 and 조건 
            rank += 1 # 위 조건 만족 시 count + 1
    print(rank, end = " ")

문제 속에 답이 다 나와있는 문제였다.

일단, 자신보다 몸무게와 키 모두 큰 사람의 수 + 1이 자신의 덩치 등수가 되는 것을 알 수 있다.

 

=> ( 몸무게가 x kg이고 키가 y cm라면 이 사람의 덩치는 (x, y)로 표시된다. 두 사람 A 와 B의 덩치가 각각 (x, y), (p, q)라고 할 때 x > p 그리고 y > q 이라면 우리는 A의 덩치가 B의 덩치보다 "더 크다"고 말한다.

 

따라서 입력받은 몸무계와 키를 각각의 사람마다 묶어서 people 리스트에 저장해준 후,

자신보다 몸무게 그리고 키가 모두 큰 사람의 수를 세는 것으로 쉽게 해결할 수 있다. 

728x90
728x90

문제

셀프 넘버는 1949년 인도 수학자 D.R. Kaprekar가 이름 붙였다. 양의 정수 n에 대해서 d(n)을 n과 n의 각 자리수를 더하는 함수라고 정의하자. 예를 들어, d(75) = 75+7+5 = 87이다.

양의 정수 n이 주어졌을 때, 이 수를 시작해서 n, d(n), d(d(n)), d(d(d(n))), ...과 같은 무한 수열을 만들 수 있다. 

예를 들어, 33으로 시작한다면 다음 수는 33 + 3 + 3 = 39이고, 그 다음 수는 39 + 3 + 9 = 51, 다음 수는 51 + 5 + 1 = 57이다. 이런식으로 다음과 같은 수열을 만들 수 있다.

33, 39, 51, 57, 69, 84, 96, 111, 114, 120, 123, 129, 141, ...

n을 d(n)의 생성자라고 한다. 위의 수열에서 33은 39의 생성자이고, 39는 51의 생성자, 51은 57의 생성자이다. 생성자가 한 개보다 많은 경우도 있다. 예를 들어, 101은 생성자가 2개(91과 100) 있다. 

생성자가 없는 숫자를 셀프 넘버라고 한다. 100보다 작은 셀프 넘버는 총 13개가 있다. 1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, 97

10000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 출력하는 프로그램을 작성하시오.

입력

입력은 없다.

출력

10,000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 증가하는 순서로 출력한다.

예제 입력 1 복사

예제 출력 1 복사

1
3
5
7
9
20
31
42
53
64
 |
 |       <-- a lot more numbers
 |
9903
9914
9925
9927
9938
9949
9960
9971
9982
9993

 

 


흠... 일단 문제가 너무 이해가 안갔다... 그래서 끄적끄적 코드를 쳐보니 

natural = set(range(1, 10001)) 
generated = set() 

for n in range(1, 10001):
    for j in str(n):
        n += int(j) 
        
    generated.add(n) 
self_num = sorted(natural - generated) 
for i in self_num:
    print(i)

 

1. 자연수 1~ 10000을 natural set에 넣어준다. 이 친구는 생성자가 없는 숫자를 골라내기 위한 용도로 쓰인다.

2. generated set는 d(n)들을 넣어줄 set이다. 빈방 상태로 시작한다.

3. 첫 번째 for문을 돌린다. 1부터 10000까지 돌리는 이유는, 1부터 10000 사이에서 나올 수 있는 생성자를 빠짐없이 찾기 위해서다. 문제에 있는 것처럼 d(d(d(... n)))수열식으로 꼬리 물면서 찾으면, 시작점에 따라 수열이 달라지는 셀프 넘버의 특성상 코드가 복잡해진다.

 

어.. 그래... 1부터 10000까지 계산한다고 하자.... generated set에 들어가는 숫자가 중복될 수 있으면,,, 101 같은 경우는 생성자가 2갠데 어떡해? -> 이는 중복을 허용하지 않는 set으로 해결

 

4.  그럼 이제 두 번째 for문을 보자. n를 str(n)으로 바꿨다!! -> indexing 해서 자릿수 씹고 뜯고 맛보려고...!

그리고 indexing 되어 한 글자씩 뽑힌 j를 int(j)로 형 변환해준다. -> 사칙연산하려고..!... n += int(j) 하려고...!

이렇게 각 자릿수를 원래 자기 수 n에 더해주면서 d(n)이 생성!

5. 이 d(n)은 set에 인자를 넣어주는 add()를 사용해서 generated에 집어넣어 준다.

6. 그럼 이제 1부터 10000까지의 수가 들어있는 natural에서 생성자가 있는 d(n)들을 모조리 빼준다.

출력할 때 순서대로 나와야 하니까 sorted로 정렬도 해준다.

그럼.. 생성자 없는 self_num set이 완성....

for문으로 self_num 하나씩.. 하나씩 출력하면 끝

 

728x90

+ Recent posts