3월 31, 2024

[유튜브 동영상 편집] 영상편집 프로그램 추천! (다빈치 리졸브 다운로드 방법)

1. 영상편집 프로그램 추천

요즘 유튜브 하시는 분들 많으시죠? 유튜브를 시작할 때 가장 큰 고민이 어떠한 영상 프로그램으로 편집을 할지일텐데요! 요즘에는 여러 영상편집 툴이 많이 개발되어서 전문가가 아니더라도 누구나 쉽게 편집을 할 수 있답니다!

 

오늘 제가 추천할 영상편집 프로그램은 "다빈치 리졸브" 라는 프로그램이에요!

다빈치 리졸브는 무료, 유료 버전이 둘 다 존재하는데요 무료 버전으로도 충분히 대부분의 편집 기능이 지원됩니다. 자막넣기, bgm 넣기, 화면에 그림 넣기, 화면 전환 등 다양한 효과를 쉽게 접근할 수 있어서 쉽게 영상편집을 할 수 있어요! (제 경험을 바탕으로 한 거에요! 처음 유튭 영상편집을 할 때 처음으로 사용한 프로그램이 다빈치 리졸브입니다!)


 

2. 다빈치 리졸브 설치 방법 

그러면 설치를 해봐야 겠죠?

 

우선 아래 링크로 접속해주세요!

 

www.blackmagicdesign.com/kr/products/davinciresolve/


위 링크에 들어가면 다빈치 리졸브 홈페이지로 연결이 될텐데요,

 

위 링크로 들어가셔서 조금 스크롤을 내리다 보면,

 


다운로드 화면

위와 같이 지금 다운로드 라는 버튼이 나온 화면을 볼 수 있을거에요

 

그러면 "지금 다운로드" 버튼을 클릭해줍니다.

 



다운로드 화면

 

여기서 저는 DaVinci Resolve 16.2.8에서 Windows를 눌러서 다운로드를 받아주었어요. studio 버전과 일반 버전의 차이는 studio 버전이 유료버전, 일반버전이 무료버전이라는 거에요!

 

왼쪽 하단의 DaVinci Resolve 16.2.8을 자신의 컴퓨터 os에 맞게 다운받으면 됩니다! 

 

유료버전은 더 지원하는 기능이 많으니 만약 유료버전을 구매하고 싶다면 오른쪽의 DaVinci Resolve Studio 16.2.8로 다운을 받으면 되겠어요!

 

일반 유튜브 용이라면 16 버전도 충분하기 때문에 아마 아래쪽 두개에서 고민을 하면 될 거에요!

 

자신에게 맞는 버전을 설치해주면 다빈치 리졸브 프로그램이 열릴텐데 그 이후의 사용법부터는 이후 포스팅에서 차근차근 다루어보도록 하겠습니다~


3월 31, 2024

[백준] 16968번 차량 번호판1 조합을 활용하여 간단하게 해결해보기

1. 문제

www.acmicpc.net/problem/16968

문제는 위의 링크에 들어가면 확인해볼 수 있다.


2. 풀이

이 문제를 푸는 여러 방법이 있을 수 있는데 오늘 여기서는 재귀로 푸는 법 말고 조합을 활용해서 쉽게 풀 수 있는 법을 소개해보도록 하겠다. 

 

조합을 활용한다는 것은 각 자리의 가능한 경우의 수를 계산해서 곱해준다는 것이다. 

만약 해당 자리의 알파벳이 'c'라면 알파벳이 들어가야 하므로 26가지 경우가 있을 수 있고, 만약 'd'라면 숫자가 들어가야 하므로 10가지 경우가 있을 수 있다. 여기서 추가로 문제에서 동일한 문자나 숫자가 두 번 연속해서 나올 수 없다고 했기 때문에 두 자리가 모두 'c'거나 모두 'd'라면 경우의 수를 한 가지 줄여서 계산해주어야 한다. 

 

이렇게 해서 전체 총 경우에다가 계속 경우의 수를 곱해나가면 쉽게 해결할 수 있다.


3. 코드

전체 코드는 아래와 같다.

import java.util.*;
public class Main {
    public static void main(String args[]) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        int ans = 1;
        for (int i=0; i<s.length(); i++) {
            int count = (s.charAt(i) == 'c' ? 26 : 10);
            if (i > 0 && s.charAt(i) == s.charAt(i-1)) {
                count --; //같은 문자열, 숫자 제외하기 위해서
            }
            ans = ans * count;
        }
        System.out.println(ans);
    }
}

 

알고리즘은 여러가지 방법으로 풀어보는 연습을 해야 실력도 늘고 생각하는 힘도 길러지는 것 같다. 한 가지 방법으로 풀어봤더라도 다양하게 연습해보자.


3월 31, 2024

[백준] 2110번 공유기 설치 문제 이분탐색으로 쉽게 풀어보기

1. 문제

www.acmicpc.net/problem/2110

문제는 위의 링크를 클릭하면 확인할 수 있다.


2. 풀이

이 문제 또한 이분탐색으로 해결할 수 있다. 공유기 사이의 거리를 이분탐색을 통해 구해보면서 c 개를 설치할 수 있는 거리가 어디인지를 찾아보는 것이다.

 

이분탐색의 경우에는 초기의 left 와 right 값을 잘 정하는 것이 중요한데 여기서는 left의 값은 1로 잡고 (가장 최소의 값이 거리 1이기 때문) right의 값은 집의 위치를 정렬한 다음에 가장 끝 집과 첫 집의 거리로 잡으면 된다. (이것이 거리의 가장 최댓값)

 

그런다음에 이분탐색을 진행하고,

 

만약 가장 인접한 두 공유기 사이의 거리를 통해 주어진 공유기 c개를 설치할 수 있다면 left의 값을 mid+1로 조정, 설치할 수 없다면 right의 값을 mid-1로 조정한다.

 

공유기를 설치할 수 있는지를 판단하는 함수를 하나 만들어주었다.

public static boolean check(int mid){
        int count=1; 
        int first=place[0];
        for(int location: place){
            if (location-first>=mid){
                count++;
                first=location;
            }
        }
        return (count>=k);
    }

여기서 count는 설치할 수 있는 공유기의 개수를 의미하고 기준이 되는 place 의 값을 변경하면서 공유기를 설치할 수 있는지 세어주었다. 만약 count의 값이 문제에서 주어진 c 값보다 크거나 같다면 true를 return 아니면 false를 return 해준다.

 


위의 check 함수를 이용한 이분탐색 부분의 코드는 아래와 같다.

int ans=1;  //거리 최소 1
        int left=1;
        int right=place[n-1]-place[0]; //최대 거리
        while(left<=right){
            int mid=(left+right)/2;
            if (check(mid)){
                ans=Math.max(ans, mid);
                left=mid+1;
            }
            else{
                right=mid-1;
            }
        }

여기서 이분탐색을 사용하기 위해서는 반드시 정렬이 된 상태여야 하기 때문에 문제에서 입력받은 place를 먼저 정렬한 뒤 사용해야 한다.


3. 코드

이를 활용한 전체 코드는 아래와 같다. 

import java.util.*;

public class Main{
    static int k;
    static int []place;
    public static boolean check(int mid){
        int count=1; 
        int first=place[0];
        for(int location: place){
            if (location-first>=mid){
                count++;
                first=location;
            }
        }
        return (count>=k);
    }
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        k=sc.nextInt();
        place=new int[n];
        for(int i=0; i<n; i++){
            place[i]=sc.nextInt();
        }
        Arrays.sort(place);
        int ans=1;  //거리 최소 1
        int left=1;
        int right=place[n-1]-place[0]; //최대 거리
        while(left<=right){
            int mid=(left+right)/2;
            if (check(mid)){
                ans=Math.max(ans, mid);
                left=mid+1;
            }
            else{
                right=mid-1;
            }
        }
        System.out.println(ans);
        
    }
}

 


이분탐색처럼 보이지 않는 문제도 이분탐색을 활용하면 쉽게 해결할 수 있는 경우가 많다. 위의 코드와 비슷한 문제 2805번 나무 자르기 문제도 그 예시중 하나다.


3월 31, 2024

[백준] 1654번 랜선 자르기 문제 이분탐색 활용해서 풀어보기

1. 문제

www.acmicpc.net/problem/1654


문제는 위의 링크에 들어가면 볼 수 있다.


2. 풀이

이 문제는 이분탐색을 활용해서 해결할 수 있는 문제이다. 

사실 모든 랜선의 길이를 하나씩 다 구해보는 방법도 있겠지만 그러면 시간이 오래걸리기 때문에 이분탐색으로 해서 시간을 줄이는 방법을 사용하면 된다.

 

처음에 left의 값은 1로 시작하고 right의 값은 랜선 길이 중에서 최대길이로 시작을 한다. ok라는 함수를 만들어서 랜선의 길이가 문제에서 주어진 개수보다 많거나 같으면 true를 return하고 작으면 false를 return한다.

public static boolean ok(long mid){
        int cnt=0;
        for(int i=0; i<a.length;i++){
            cnt+=(a[i]/mid);
        }
        return (cnt>=k);
    }

 

만약 return 값이 true라면 숫자를 증가시켜도 된다는 뜻이므로 left의 값을 mid+1로 조정하고, false라면 숫자를 낮추어야 한다는 뜻이므로 right를 mid-1로 조정한다. 

 

 

이를 반영한 이분탐색 부분의 코드는 아래와 같다.

 long ans=0;
        long left=1;
        long right=max;
        while(left<=right){
            long mid=(left+right)/2;
            if (ok(mid)){//원하는 개수 이상으로 만들 수 있음
                left=mid+1;
                ans=Math.max(ans, mid);
            }
            else{
                right=mid-1;
            }
        }

이렇게 이분탐색을 진행하면 빠르게 문제를 해결할 수 있다.

 

3. 코드 

전체 코드는 아래와 같다. 

import java.util.*;

public class Main{
    static int k;
    static long a[];
    public static boolean ok(long mid){
        int cnt=0;
        for(int i=0; i<a.length;i++){
            cnt+=(a[i]/mid);
        }
        return (cnt>=k);
    }
        
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
         k=sc.nextInt();
        a=new long[n];
        long max=0; //가장 길이가 긴 랜선 길이 저장
        for(int i=0; i<n; i++){
            a[i]=sc.nextInt();
            max=Math.max(max, a[i]);
        }
        long ans=0;
        long left=1;
        long right=max;
        while(left<=right){
            long mid=(left+right)/2;
            if (ok(mid)){//원하는 개수 이상으로 만들 수 있음
                left=mid+1;
                ans=Math.max(ans, mid);
            }
            else{
                right=mid-1;
            }
        }
        System.out.println(ans);
        
    }
}

3월 31, 2024

[안드로이드] 앱 켜자마자 특정 URL로 이동하기

앱 실행 후 다른 url로 이동하기

앱을 틀자마자 다른 URL로 이동하고 싶을 때 혹은 웹사이트를 만들어서 앱을 통해서도 접근할 수 있게 만들고 싶을때 코드를 단순히 추가하면 쉽게 해결할 수 있다. 

 

나는 코틀린으로 코드를 작성했기 때문에 

MainActivity.kt 파일에서 

 

import android.content.Intent
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val i = Intent(Intent.ACTION_VIEW)
        i.data = Uri.parse("https://programmingstory.com/")
        startActivity(i)
        setContentView(R.layout.activity_main)
    }
}

위와 같이 코드를 작성해주면 된다.

 

그러면 네이버 앱을 틀었을때 바로 내가 원하는 URL로 이동할 수 있다.


3월 25, 2024

[백준] 1517번 버블 소트 문제 Merge Sort로 풀어보기 (버블 소트로는 풀 수 없는 이유?)

1. 문제

www.acmicpc.net/problem/1517

문제는 위의 링크에 들어가면 확인할 수 있다.


2. 풀이

이 문제는 버블 소트를 할 때 수를 바꾸는 과정이 몇 번 있는지를 묻는 문제이다. 하지만 이것을 실제 Bubble Sort대로 풀면 시간초과가 나게 될 것이다. 왜냐하면 버블소트는 시간복잡도 O(N^2) 이기 때문에 문제에서 주어진 조건을 초과하게 된다. 

 

따라서 이 문제는 시간복잡도 O(NlogN)인 merge sort를 활용하여서 풀 수 있다. 

 

버블 소트는 index i, j에 대해서 i<j인데 a[i] > a[j] 일때 두 수를 바꾸어주는 알고리즘을 의미한다. 

그래서 이를 merge sort라고 생각하면, 두 그룹을 합쳐줄 때 버블 소트에서 두 수를 바꾸어주는 count를 세어 줄 수 있다.

 

다시 말해서,


위와 같은 두 그룹을 merge sort를 통해서 합쳐준다고 생각하면 첫 그룹의 7과 두번째 그룹의 1을 비교해서 1이 먼저 앞으로 들어가게 된다. 

Bubble Sort라고 생각해보면 7, 9, 1, 3이 다 합쳐져있었을 것이고 1을 기준으로 7과 1이 한번 교환되었을 것이고, 9과 1이 한번 더 교환되었을 것이다. 그러면 1 기준으로 두 번의 숫자 교환이 이루어졌던 것이다. 

우리는 merge sort를 통해서 구현하고 있었으니 이를 merge sort로 생각해보면 두 번째 그룹인 1이 가장 처음에 들어갈 때 앞에 남아있는 그룹의 원소 개수만큼 숫자의 교환이 이루어지는 것이다. 여기서는 1이 가장 먼저 정렬될 때 첫 번째 그룹에 7,9 이렇게 두 개의 숫자가 남아있으니 답에는 2가 더해지게 되는 것이다. 마찬가지로 3의 경우에도 3이 정렬될 때 첫 번째 그룹에 두 개의 숫자가 남아있으니 답에는 추가로 2만큼 더해주어야 한다.

 


3. 코드

이를 코드로 구현한 것은 아래와 같다.

 

import java.util.*;
import java.io.*;

public class Main{
    static int a[];
    public static long go(int start, int end){
        if (start==end){
            return 0;
        }
        int mid=(start+end)/2;
        long ans=go(start, mid)+go(mid+1, end);
        int [] tmp=new int[end-start+1];
        { //두 그룹을 합칠 때 merge
            int i=start;
            int j=mid+1;
            int k=0;
            while(i<=mid || j<=end){
                if (i<=mid && (j>end||a[i]<=a[j])){
                    tmp[k++]=a[i++];
                }else{
                    ans+=(long)(mid-i+1);  //두번째 그룹의 숫자가 들어갈 때 
                    tmp[k++]=a[j++];
                }
            }
            
        }
        for (int i=start; i<=end; i++) {
            a[i] = tmp[i-start];
        }
        return ans;
    }
    public static void main(String[] args) throws IOException{
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        int n=Integer.valueOf(br.readLine());
        a=new int[n];
        String s[]=br.readLine().split(" ");
        for(int i=0; i<n; i++){
            a[i]=Integer.valueOf(s[i]);
        }
        System.out.println(go(0, n-1));
    }
}

merge sort의 구현을 그대로 따라주었고 다른 점은 두 번째 그룹의 숫자를 tmp라는 배열에 담을 때 첫 번째 그룹의 원소개수를 정답에 추가해주어야 한다는 점이다. 


3월 25, 2024

[백준] 2263번 트리의 순회 문제 풀어보기

1. 문제

www.acmicpc.net/problem/2263

문제는 위의 링크에 들어가면 볼 수 있다.


2. 풀이

이 문제는 간단히 말해서 인오더와 포스트오더가 주어졌을때 프리오더로 출력할 수 있냐는 문제이다. 

우선 트리의 순회는

https://www.programmingstory.com/2024/02/blog-post_11.html

위의 포스팅에서 다루었기 때문에 트리에 대해 처음 접하는 사람들은 위의 포스팅을 먼저 읽고 오자.


우선 인오더의 경우에는 왼쪽 자식 노드를 방문한 뒤, 루트 노드를 방문하고, 오른쪽 자식 노드를 방문하는 순서이고,

포스트오더의 경우에는 왼쪽 자식노드와 오른쪽 자식 노드를 모두 방문한 뒤 마지막에 루트 노드를 방문하는 경우를 뜻한다.

 

따라서 우리가 확실하게 알 수 있는 것은 포스트오더의 마지막에는 항상 루트 노드가 나온다는 것이다. 

그런 다음에 인오더의 배열에서 루트 노드를 찾고, 루트 노드를 기준으로 왼쪽 자식 노드로 또 다시 재귀로 함수를 호출하고, 오른쪽 노드들로 다시 재귀함수를 호출하면 된다. 그러면서 프리오더를 구현해야 하기 때문에 루트 노드는 먼저 출력해주고, 다음에 왼쪽 자식 노드들 호출, 오른쪽 자식 노드들을 호출해주면 되는 것이다.

 


3. 코드

전체 코드는 아래와 같다.

import java.util.*;

public class Main{
    static int inorder[];
    static int postorder[];
    static int location[];
    static void solve(int in_s, int in_e, int post_s, int post_e ){
        if (in_s>in_e||post_s>post_e) return;
        int root=postorder[post_e];
        System.out.print(root+" ");
        int index=location[root]; //프리오더의 index
        int left_count=index-in_s; 
        solve(in_s, index-1, post_s, post_s+left_count-1);
        solve(index+1, in_e, post_s+left_count, post_e-1);
    }
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        inorder=new int[n];
        postorder=new int[n];
        for(int i=0; i<n; i++){
            inorder[i]=sc.nextInt();
        }
        for(int i=0; i<n; i++){
            postorder[i]=sc.nextInt();
        }
        location=new int[100001];
        for(int i=0; i<n; i++){
            location[inorder[i]]=i; //숫자를 적으면 바로 index가 나오게
        }
        solve(0, n-1, 0, n-1);
    }
}

 

여기서 location이라는 배열을 구한 것은 매번 루트 노드가 몇 번째 index에 위치해있는지를 찾지 않기 위함이다. 그러면 시간복잡도가 매우 높게 나오기 때문에 애초에 배열을 사용해서 해당 숫자를 배열의 index로 주면 바로 몇 번째 index에 루트 노드가 위치해있는지를 판단할 수 있도록 코드를 구현했다.

 

solve 함수에서 in_s 는 인오더의 시작 index, in_e는 인오더의 끝 index, post_s는 포스트오더의 시작 index, post_e는 포스트오더의 끝 index를 뜻한다. 이후에 루트 노드를 찾아주고 인오더 배열 기준으로 왼쪽 오른쪽을 재귀적으로 호출해주면 프리오더를 구현할 수 있다. 


3월 25, 2024

[백준] 11728번 배열 합치기 Merge Sort 풀이법

1. 문제

www.acmicpc.net/problem/11728

문제는 위의 링크에 들어가면 확인할 수 있다.


2. merge sort

merge sort 중에서 합치는 부분을 코드로 구현해야 하는 문제이다.

Merge Sort에 관한 내용은 이전 포스팅에서 정말 자세하게 다루었다.

https://www.programmingstory.com/2024/02/merge-sort.html

위의 포스팅에서 합치는 코드 또한 다루었기 때문에 한번 읽어보면 이 문제를 푸는데에도 큰 도움이 될 것이다.


3. 풀이

다른 점은 위 포스팅에서는 start, end 라고 해서 두 그룹을 합쳐서 처음과 끝 index를 따로 표시해주었는데 이번에는 두 배열을 서로 다른 배열로 입력받아놓았다는 점이다. 

그래도 알고리즘 자체는 완전히 같다.

 

합치는 코드 부분만 우선 살펴보겠다.

 //합치는 코드
        int i=0; //첫번째 그룹 index
        int j=0; //두번째 그룹 index
        int k=0; //현재 저장하는 c index
        while(i<n && j<m){
            if (a[i]<=b[j]){
                c[k++]=a[i++];
            }
            else {
                c[k++]=b[j++];
            }
        }
        while(i<n){
            c[k++]=a[i++];
        }
        while(j<m){
            c[k++]=b[j++];
        }

 

여기서 c라는 배열에다가 정렬된 것을 저장해놓을 것이고, 두 그룹의 index별로 대소관계를 비교해 먼저 c 배열에 들어와야 할 요소를 판단한다. 

마지막에 while문은 만약 하나의 그룹의 원소들은 모두 다 c 배열에 저장이 되었는데 다른 그룹의 원소들이 남아있을 경우를 대비하여 c 배열에 순서대로 저장하는 코드를 추가한 것이다.


4. 전체 코드

입력받고 출력하는 것까지 전체 코드는 아래와 같다.

import java.util.*;
import java.io.*;

public class Main{
    public static void main(String[] args) throws IOException{
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        String[] line = br.readLine().split(" ");
        int n = Integer.valueOf(line[0]);
        int m = Integer.valueOf(line[1]);
        int[] a = new int[n];
        line = br.readLine().split(" ");
        for (int i=0; i<n; i++) {
            a[i] = Integer.valueOf(line[i]);
        }
        int[] b = new int[m];
        line = br.readLine().split(" ");
        for (int i=0; i<m; i++) {
            b[i] = Integer.valueOf(line[i]);
        }
        int[] c = new int[n+m]; //저장할 공간
        
        //합치는 코드
        int i=0; //첫번째 그룹 index
        int j=0; //두번째 그룹 index
        int k=0; //현재 저장하는 c index
        while(i<n && j<m){
            if (a[i]<=b[j]){
                c[k++]=a[i++];
            }
            else {
                c[k++]=b[j++];
            }
        }
        while(i<n){
            c[k++]=a[i++];
        }
        while(j<m){
            c[k++]=b[j++];
        }
        StringBuilder sb = new StringBuilder();
        for (int in=0; in<n+m; in++) {
            sb.append(c[in] + " ");
        }
        System.out.println(sb);
    }
}

 

입력이 많기 때문에 Scanner 를 사용하는 대신 BufferedReader를 사용했고, 출력또한 매번 System.out.print()를 사용하는 것이 아닌, StringBuilder를 사용해서 한꺼번에 출력하여 시간을 줄였다. 


3월 25, 2024

오버로딩과 오버라이딩 차이 (overloading vs. overriding)

1. 오버로딩 (overloading)과 오버라이딩 (overriding)의 차이

Java에서 흔히 혼동되는 개념으로 오버로딩과 오버라이딩이 있다. 

이 두 개념은 매우 간단하지만 기술면접에도 자주 등장하는 주제이기 때문에 정확히 알아두면 좋다. 

각각의 차이점과 개념을 정확히 알아두자.


1) overloading : 오버로딩

메서드의 매개변수의 유형과 개수를 다르게 하면서 같은 이름의 메서드를 가지는 것

 

overloading의 예시를 들어보면 아래와 같다.

void greetings(String s){
System.out.println(s);
}

void greetings(String person, String s){
System.out.println("Say" + s+ " to "+person); //person에게 s라고 인사해라
}

위와 같이 두 메소드 모두 인사를 하는 메소드임에도 불구하고 상황에 따라서 매개변수가 다르게 들어가는 경우가 있다. 

 

물론 이러한 경우에 method의 이름을 다르게 정의한다면 해결되는 일이지만, 코드상으로 같은 기능을 가지는데 이름을 다르게 정의하는 것은 좋은 코딩이 아니다.

 

이런 것을 방지하기 위해 Java에서 제공하는 기능이 overloading 인 것이다. C++의 경우 연산자 overloading이라는 기능 또한 제공하지만 Java에서는 연산자 overloading은 제공하지 않는다라는 것을 추가적으로 알아두면 좋을 것 같다.


2) overriding: 오버라이딩

상위 클래스가 가지고 있는 메서드를 하위 클래스에서 재정의해서 사용하는 것을 overriding이라고 한다. 상속을 하다 보면 하위 클래스에서 똑같은 메서드를 구현하지만 변형이 필요할 때가 있다. 이 또한 예시를 들어 설명해보겠다.

 

만약 Human 이라는 class가 상위클래스이고, 이를 상속받은 Student라는 class가 있다고 해보자. 그럴 경우, 두 class 모두 who 라는 메서드를 가질 수 있고 이 메서드는 해당 객체가 어떤 사람인지를 출력해주는 것이라고 해보자. 그러면 모든 사람은 이름을 가지기 때문에 아래와 같이 작성할 수 있다. 

 

class Human{ //부모클래스
    public String name;

    public void who(){
        System.out.println("이 사람의 이름은 "+name+"입니다.");
    }
    
}

반면 Student라는 class는 여기에 더해, 이 학생이 다니고 있는 학교 이름까지 담고 있을 수 있고, who 라는 메서드에서 이러한 정보도 같이 출력하고 싶을 수 있다. 

class Student extends Human{ //자식 클래스
	String school; 
    public void who(){
        System.out.println("이 사람의 이름은 "+name+"이고 학교는 "+school+"학교에 다닙니다");
    }
    
}

위에서 알 수 있듯이 Student class의 who 메서드는 상위클래스에서 적용된 who 메서드에 school 정보까지 출력하고 있다는 것을 알 수 있다. 

 

즉 위와 같이 같은 이름의 메서드이지만 상위클래스의 메서드를 하위클래스에서 다시 덮어쓰는 방식을 우리는 overriding이라고 한다. 

 


overloading과 overriding은 Java에서 다형성을 구현하는 데 핵심이 되는 기능이다. 

이러한 기능을 잘 사용하면 Java의 OOP적인 특성을 더 강화할 수 있고 코드를 더 compact하게 작성할 수 있다는 장점이 있으니 최대한 활용해보길 권장한다. 


3월 25, 2024

cmd/linux에서 tar 로 압축하는 법 코드

1. tar 파일 압축하는 방법

cmd 상에서 여러 파일을 하나의 tar 파일로 압축하는 코드에 대해 알아보겠다.


2. cmd/linux 명령어 

window 기준으로,

cmd에서 cd 명령어 뒤에 파일이 있는 곳으로 이동해준다.

 

예를 들어 a.txt 와 b.txt 를 압축하려고 하는데 이 두개의 text file이 C:\text에 들어있다면,

cd C:\text

이런식으로 이동해주는 것이다. 

 

그런 다음에

tar -cvf "압축 된 이후에 tar 파일 명" "압축파일명 1" "압축파일명 2" "압축파일명 3"

 

이런식으로 압축을 해주면 된다. 압축파일할 것이 더 있다면 계속 이어서 적어주면 된다. " "는 생략하고 적어준다. 즉 위와 같이 a.txt와 b.txt 파일을 textfiles.tar에 압축하고 싶다면,

tar -cvf textfiles a.txt b.txt

이렇게 적어주면 되는 것이다. 

 

이렇게 하고 본다면 원하는 폴더에 tar 파일이 생성된 것을 볼 수 있다.


3월 24, 2024

파이썬으로 스누피 캐릭터 그리기 투토리얼

1. Python turtle library를 사용하여 스누피 그리기

파이썬 turtle library를 사용하면 다양한 기능을 사용하여 원하는 것을 그릴 수 있다.

이전에도 python turtle library를 사용하여 어피치, 라이언, 보노보노 등 다양한 캐릭터를 그리는 방법을 영상으로 제작한 바 있는데, 오늘은 스누피 캐릭터 그리는 방법에 대한 투토리얼을 제작했다.

 

1) 링크

 https://youtu.be/svN8Q-Acnpo


해당 방법은 위의 동영상을 참고해주면 된다. 

 

스누피는 이전 캐릭터와 달리 굴곡을 구현하는 것이 어렵기 때문에 조금 더 섬세한 조정이 필요하다.


2) python으로 현재 좌표값 찾기

https://www.programmingstory.com/2024/02/python-turtle.html

가장 유용하게 사용했던 문법은 현재 좌표를 구할 때 사용하던 xcor, ycor 문법이었으며, 자세한 내용은 이전 글을 참고해주면 된다. 


3월 24, 2024

[백준] 10816번 숫자카드 2문제 풀어보기 (이분탐색 활용)

1. 문제

www.acmicpc.net/problem/10816

문제는 위의 백준 링크에 들어가면 확인할 수 있다.


2. 풀이

이 문자는 이분탐색을 이용하여서 쉽게 해결할 수 있다. 먼저 해당 숫자가 몇번 나오는지를 count해야 하기 때문에 두가지 함수를 만들어줄 것이다.

  1. lower_bound: 내가 찾는 숫자와 같은 수의 값을 가지고 있는 인덱스 중 가장 작은 인덱스를 return. 만약 없다면 -1 return
  2. upper_bound: 내가 찾는 숫자와 같은 수의 값을 가지고 있는 인덱스 중 가장 큰 인덱스를 return. 없다면 -1을 return

즉 정렬을 해준뒤에 lower_bound와 upper_bound의 값을 얻으면 해당 숫자가 몇개가 있는지 쉽게 구할 수 있다. 예를 들어, lower_bound의 return 값이 3, upper_bound의 return 값이 5라면, 3,4,5번 index에 해당 숫자가 있는 것이기 때문에 총 (5-3+1)개가 있다고 할 수 있다. 

 

즉 우리는 (upper_bound의 return 값 - lower_bound의 return 값 +1) 만큼 해당 숫자가 존재한다고 할 수 있다. 


여기서 upper_bound와 lower_bound 함수는 이분탐색을 사용하여 쉽게 구현할 수 있다. 

 

먼저 lower_bound부터 살펴보겠다.

static int lower_bound(int num){ //같은 수 중에서 가장 작은 인덱스
        int n=a.length;
        int left=0;
        int right=n-1;
        int ans=-1; //못찾았을경우에는 -1
        while(left<=right){
            int mid=(left+right)/2;
            if (a[mid]==num){
                ans=mid;
                right=mid-1;
            }
            else if (a[mid]>num){
                right=mid-1;
            }
            else{
                left=mid+1;
            }
        }
        return ans;
    }

원하는 숫자를 찾은 뒤에는 right을 mid-1의 값으로 바꾸어주어 while문을 끝나게 만들어주어야 한다.


upper_bound 함수도 코드 한 줄만 수정하여 쉽게 구현할 수 있다. 

static int upper_bound(int num){ //같은 수 중에서 가장 큰 인덱스 
        int n=a.length;
        int left=0;
        int right=n-1;
        int ans=-1; //못찾았을경우에는 -1
        while(left<=right){
            int mid=(left+right)/2;
            if (a[mid]==num){
                ans=mid;
                left=mid+1;
            }
            else if (a[mid]>num){
                right=mid-1;
            }
            else{
                left=mid+1;
            }
        }
        return ans;
    }

upper_bound는 해당 숫자를 찾은 뒤에도 해당 숫자를 값으로 가지고 있는 최대 index를 찾아야 하므로 left의 값을 mid+1으로 수정시켜 계속 while문을 돌도록 해야 한다.


3. 코드

이것을 코드로 구현하면 아래와 같다.

import java.util.*;
import java.io.*;

public class Main{
    static int a[];
    static int lower_bound(int num){ //같은 수 중에서 가장 작은 인덱스
        int n=a.length;
        int left=0;
        int right=n-1;
        int ans=-1; //못찾았을경우에는 -1
        while(left<=right){
            int mid=(left+right)/2;
            if (a[mid]==num){
                ans=mid;
                right=mid-1;
            }
            else if (a[mid]>num){
                right=mid-1;
            }
            else{
                left=mid+1;
            }
        }
        return ans;
    }
    static int upper_bound(int num){ //같은 수 중에서 가장 큰 인덱스 
        int n=a.length;
        int left=0;
        int right=n-1;
        int ans=-1; //못찾았을경우에는 -1
        while(left<=right){
            int mid=(left+right)/2;
            if (a[mid]==num){
                ans=mid;
                left=mid+1;
            }
            else if (a[mid]>num){
                right=mid-1;
            }
            else{
                left=mid+1;
            }
        }
        return ans;
    }
    public static void main(String[] args) throws IOException{
         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.valueOf(br.readLine());
        String[] line = br.readLine().split(" ");
         a = new int[n];
        for (int i=0; i<n; i++) {
            a[i] = Integer.valueOf(line[i]);
        }
        Arrays.sort(a);
        int m = Integer.valueOf(br.readLine());
        String[] s = br.readLine().split(" ");
        StringBuilder ans = new StringBuilder();
        for(int i=0; i<m; i++){
            int num=Integer.valueOf(s[i]);
            int low=lower_bound(num);
            int high=upper_bound(num);
            if (low==-1){
                //없다는 뜻이므로
                ans.append("0 ");
            }
            else{
                ans.append((high-low+1)+" ");
            }
        }
        System.out.println(ans);
    }
}

3월 24, 2024

[백준] 1744번 수 묶기 문제 (어떻게 묶으면 최대가 될까?)

1. 문제

www.acmicpc.net/problem/1744

문제는 위의 링크에 들어가면 확인할 수 있다.


2. 풀이

이 문제는 두 수를 잘 묶으면 곱하기가 된다는 문제를 바탕으로 어떻게 두 수를 곱할 때 최대가 될지 생각해보아야 하는 문제이다. 

  • 양수: 양수끼리는 서로 큰 숫자끼리 곱하면 최대가 된다. 1이랑 곱하는 것은 그대로 유지됨으로 1과 곱하기는 지양하는 것이 최대를 만들 수 있는 길이다. 
  • 음수: 음수는 작은 수끼리 곱하면 오히려 최대가 된다. 따라서 양수는 내림차순 정렬하고, 음수는 오름차순 정렬한 뒤에 둘 씩 곱해보는 것이 필요하다는 것을 알 수 있다. 
  • 만약 음수의 개수가 홀수이고, 0이 1개 이상 있다면 남는 음수 하나는 0과 곱해서 0을 만드는 것이 최대를 만들 수 있는 방법이다. 
  • 1은 양수지만 곱하기보다는 그대로 더하는 것이 최대를 만드는 방법이다. 1*1은 1이지만 1+1은 2이기 때문이다. 더하기를 하는 것이 나은 수이다. 

이것을 바탕으로 문제에서 양수, 음수를 입력받아서 서로 다른 ArrayList에 구분해서 넣어준다. 1의 경우에는 양수지만 우리가 곱하지 않을 것이기 때문에 plus ArrayList에 넣어주지는 않는다.

 


3. 코드

이것을 코드로 구현한 것은 아래와 같다.

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        ArrayList<Integer> plus=new ArrayList<>();
        ArrayList<Integer> minus=new ArrayList<>();
        int zerocount=0;
        int onecount=0;
        for(int i=0; i<n; i++){
            int x=sc.nextInt();
            if (x==1) onecount++; //1인 것은 안묶어야 최대
            else if (x>0) plus.add(x);
            else if (x<0) minus.add(x);
            else{
                zerocount++;
            }
        }
        Collections.sort(plus);  
        Collections.reverse(plus); //양수는 내림차순으로 정렬
        Collections.sort(minus); //음수는 오름차순으로 해야 곱했을 때 최대
        
        if (plus.size()%2==1){
            //양수가 홀수이면 하나는 1을 넣어준다
            plus.add(1);
        }
        if (minus.size()%2==1){
            if (zerocount>0){
                minus.add(0);
            }else{
                minus.add(1);
            }
        }
        int ans=onecount;
        for (int i=0; i<plus.size(); i+=2) {
            ans += plus.get(i) * plus.get(i+1);
        }
        for (int i=0; i<minus.size(); i+=2) {
            ans += minus.get(i) * minus.get(i+1);
        }
        System.out.println(ans);
    }
}

zerocount은 0의 개수, onecount는 1의 개수를 의미한다. 어짜피 둘 씩 곱해줄 것이기 때문에 양수의 개수가 홀수이면 그냥 1을 넣어주었다. 어짜피 1과 곱한 값은 하나를 더한 것과 동일하기 때문에 문제의 정답에 영향을 미치지 않기 때문이다. 나중에 둘씩 곱해주기 위해서 양수, 음수의 개수를 모두 짝수개로 맞추어주려고 한 것이다. 음수의 경우에는 개수가 홀수일 경우 그리고 0이 한개 이상 있을 경우 0과 곱해주면 되고, 아닌 경우 그냥 그 수 자체를 더해주면 되니 1을 추가해준 것이다. 


3월 24, 2024

[백준] 2109번 순회강연 문제 풀이

1. 문제

www.acmicpc.net/problem/2109

문제는 위의 백준 링크에 들어가보면 볼 수 있다.


이 문제는 전 포스팅에서 다루었던, 

https://www.programmingstory.com/2024/03/1202-priorityqueue.html

보석도둑 문제와 굉장히 유사한 문제라고 할 수 있다. 유사한 풀이로 이 문제도 풀어볼 것이니 위의 포스팅이 큰 도움이 될 것이다.


2. 풀이

이 문제는 예를 들어서 4일 차에는 강연을 4일내, 5일내, 6일내...등등으로 부탁한 강의들만 할 수 있기 때문에 강의를 day를 기준으로 내림차순으로 정렬하는 것이 필요하다. 

그래서, Lecture class를 하나 생성해주고,

class Lecture implements Comparable<Lecture>{
    int price, day;
    Lecture(int price, int day){
        this.price=price;
        this.day=day;
    }
    public int compareTo(Lecture a){
        if (a.day!=this.day){
            return a.day-this.day;
        }
        else{
            return a.price-this.price;
        }
    }
}

코드를 위와 같이 적어주었다. day를 기준으로 내림차순으로 정렬할 수 있도록 compareTo method를 구현해주었다.


이렇게 class를 만들어 준 뒤에는 PriorityQueue를 사용해서 문제를 해결할 수 있다. 위에서도 언급했지만 n일차에는 강연 요청이 n일차 이상으로 부탁한 강연들만 할 수 있다. 따라서 가능한 조건 내에서 price를 PriorityQueue에 넣어준다. 가장 비싼 강연을 하는 것이 좋기 때문에 내림차순으로 정렬될 수 있도록 -1을 곱해서 queue에 넣어주는 것이 핵심이다. 

 

또한 문제에서 Lecture를 day 기준으로 정렬했기 때문에 0번째 index에 있는 강연이 현재 상태로는 가장 널널한 강의일 것이다. 따라서 그 때의 day부터 시작하여 day가 1이 될때까지 하나씩 감소시켜가면서 문제를 해결해주면 된다.

 


3. 코드

이를 코드로 구현한 것은 아래와 같다.

import java.util.*;
class Lecture implements Comparable<Lecture>{
    int price, day;
    Lecture(int price, int day){
        this.price=price;
        this.day=day;
    }
    public int compareTo(Lecture a){
        if (a.day!=this.day){
            return a.day-this.day;
        }
        else{
            return a.price-this.price;
        }
    }
}
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        Lecture a[]=new Lecture[n];
       
        for(int i=0; i<n; i++){
            a[i]=new Lecture(sc.nextInt(), sc.nextInt());
        }
        PriorityQueue<Integer> q = new PriorityQueue<>();
        Arrays.sort(a);
        int maxday=0; //아무 강의요청도 없을 수 있기 때문에
        if(n>0){
            maxday=a[0].day;
        }
        int cur=0;
        int ans=0;
        for(int i=maxday; i>=1; i--){
            while(cur<n && a[cur].day==i){
                q.offer(a[cur].price*-1);
                cur++;
            }
            if (!q.isEmpty()){
                ans+=q.poll()*-1;
            }
        }
       System.out.println(ans);
        
    }
}

3월 24, 2024

[백준] 1202번 보석 도둑 문제에 필요한 자료구조는? (PriorityQueue사용)

1. 문제

1) 링크

www.acmicpc.net/problem/1202

문제는 위의 링크를 클릭하면 확인할 수 있다.


2. 풀이

이 문제를 풀기 위해서 우리는 class를 하나 만들어 주는 것이 필요하다. 왜냐하면 보석과 가방을 묶어서 하나의 객체로 만들 것이고, 필요한 인스턴스는 무게, 가격, 그리고 보석인지 가방인지를 알려주는 정수 이렇게 세개이기 때문이다. 가방의 경우 가격은 주어져 있지 않기 때문에 0이라고 가정하고, 보석일 경우 0, 가방일 경우 1로 instance를 만들어 객체를 만들어주기로 했다.

다시 말해서,

class Jewelry implements Comparable<Jewelry>{
        int m,v,t;
        Jewelry(int m, int v, int t){
            this.m=m; //무게
            this.v=v; //가격
            this.t=t;//t가 0이면 보석, 1이면 가방
        }
        public int compareTo(Jewelry a){
            if (this.m!=a.m){
                return this.m-a.m;
            }
            else{
                return this.t-a.t; //가방이 나중에 오도록
            }
        }
       
    }

위와 같이 class를 하나 만들어준다. 그런 다음에 우리가 해당 객체를 정렬할 때 필요한 compareTo 메서드를 구현해줄 필요가 있다. 우선 무게 순으로 정렬을 할 것이기 때문에 m을 기준으로 오름차순이 될 수 있도록 정렬을 해주고, 만약 무게가 같을 경우에 보석이 다 온 뒤에 가방이 올수 있도록 t를 기준으로 정렬해주었다. t는 가방일 경우에 1이고, 보석일 경우에 0으로 해놓았기 때문에 this.t-a.t를 하게 되면 같은 무게일 경우 보석 뒤에 가방이 오게 된다.


그렇다면 정렬을 한 뒤의 모습은 어떠할지 생각해보자.

 

ex) 보석: (무게, 가격) 순으로 (3, 100) , (1, 50), (2, 99) 가 있었다고 가정하고,

    가방: (무게) 2, 4 인 무게를 담을 수 있는 가방 두개가 있다고 가정하면,

 

정렬 이후에는 

(1, 50), (2, 99), 무게 2인 가방, (3, 100), 무게 4인 가방

이렇게 정렬이 된다는 것을 알 수 있다. 

 

즉, 무게 2인 가방에 담을 수 있는 것은 정렬된 배열 기준으로 무게 2인 가방 앞에 오는 보석들이고, 무게 4인 가방에 담을 수 있는 보석들도 마찬가지로, 해당 가방 앞에 정렬된 보석들이라는 것을 알 수 있다.

그 중에서 우리는 최대 가격의 보석을 담을 수록 좋기 때문에 가장 가격이 높은 보석을 담아야 한다.


3. 자료구조 - PriorityQueue

그러면 이것을 구현할 수 있는 유용한 자료구조로 무엇을 사용하면 좋을까?

 

PriorityQueue를 사용해서 구현을 해보았다. PriorityQueue를 사용한 이유는 담는 순서대로 담기는 일반 Queue와는 달리, 순서대로 정렬이 될 수 있기 때문이다. 

 

그러면 우리는 가방이 나오기 전까지 보석의 가격을 PriorityQueue에다가 담아주면 된다. 하지만 가장 높은 가격의 가방이 가장 처음에 오도록 담고 싶기 때문에, 가방의 무게에 -1을 곱한 값을 PriorityQueue에 담아주어야 한다. 

만약 -1을 곱하지 않는다면 숫자는 오름차순 정렬이 기본이기 때문에 가장 처음의 숫자는 가장 낮은 가격의 가방이 오게 된다. 우리가 원하지 않았던 결과이기 때문에 음수를 곱해준 것이다.

 

이렇게 한 뒤에, 만약 가방이 나온다면, 우리는 PriorityQueue에 있는 보석 한개를 꺼내주게 되면 그것이 가방에 담을 수 있는 가장 비싼 보석이 되는 것이다. (정렬을 이미 해 놓았기 때문에)


4. 코드

이 모든 것을 구현한 코드는 아래와 같다. 

import java.util.*;
 class Jewelry implements Comparable<Jewelry>{
        int m,v,t;//t가 0이면 보석, 1이면 가방
        Jewelry(int m, int v, int t){
            this.m=m;
            this.v=v;
            this.t=t;
        }
        public int compareTo(Jewelry a){
            if (this.m!=a.m){
                return this.m-a.m;
            }
            else{
                return this.t-a.t; //가방이 나중에 오도록
            }
        }
       
    }
public class Main{
   
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int k=sc.nextInt();
        Jewelry [] jew=new Jewelry[n+k];
        for(int i=0; i<n; i++){
            int m=sc.nextInt();
            int v=sc.nextInt();
            jew[i]=new Jewelry(m,v,0);
        }
        for(int i=0; i<k; i++){
            int m=sc.nextInt();
            jew[n+i]=new Jewelry(m, 0, 1);
        }
        Arrays.sort(jew);
        PriorityQueue<Integer>q=new PriorityQueue<>();
        long ans=0;
        for(Jewelry j: jew){
            if (j.t==0){
                //보석일 경우
                q.offer(-j.v);  
            }
            else{
                if (!q.isEmpty()){
                    ans+=(long)q.poll()*-1;
                }
            }
        }
        System.out.println(ans);
    }
}