미소녀 프사를 달자.

플4..!

gpt를 10번은 혼내서 얻어따.. 진지하게 깃허브 프사까지 바꾸려다가 참음

https://devyeonee911.tistory.com/2

Presigned URL로 S3 프라이빗 버킷 이미지 조회하기 (SDK v2)

프로메사 프로젝트를 진행하던 중, 원래 계획에는 없었던 Presigned URL을 도입하게 되었습니다. 이 글에서는 해당 기술을 왜 선택하게 되었는지 먼저 설명하고 이어서 Presigned URL의 개념을 간단히

devyeonee911.tistory.com


글을 썼다. 나름 잘 쓴 글 같기도.. 근데 시간이 많이 든다 ㅠ

개발하는 것보다 어떻게 글 쓰는 시간이 더 오래 걸린다.... 개발하는 거를 다 쓸 필요는 없지만 그래도 하나씩 얻어가는 거는 꼭 있어서 웬만해서는 기록해두고 싶은데 그 속도를 못 따라간다.

세오스 5주차 과제로 도커 공부 중...그냥 뭔가 데이터 통신 생각남..
데이터 통신을 생각하니 마음이 좋지 않아요

aws 시험 응시료 20만원도 날 너무 힘들게 함... 말이 안 됨

챗gpt 유료버전을 결제했다! 완전 똑똑이야


Listof와 MutableListOf

Kotlin에서 컬렉션 타입(collection type)은 여러 개의 데이터를 표현하는 방법이며 Array, List, Set, Map이 있다. List, Set, Map은 Collection 인터페이스를 구현한 클래스이며, 이를 통틀어 컬렉션 타입 클래스라고 한다. 그중 List순서가 있는 데이터 집합으로 데이터의 중복을 허용한다.

Collection 타입의 클래스는 가변 클래스불변 클래스로 나뉜다. Kotlin에서는 불변 타입인 List, 가변 타입인 MutableList를 제공한다. 불변 타입은 요소 추가, 삭제가 불가능하며, 가변 타입은  .add(), .remove() 등의 메서드를 통해 값을 자유롭게 수정할 수 있다. 

listOf()함수로 List 객체를, mutableListOf()함수를 통해 MutableList를 생성할 수 있다. 또한 mutableListOf() 함수는 타입 추론이 가능하기 때문에 명시적인 타입 선언 없이도 앞서 사용한 타입을 그대로 사용할 수 있다.

fun main() {

    // Immutable list
    // val shoppingList = listOf("Processor", "RAM", "Graphics Card", "SSD")

    // mutable list
    val shoppingList = mutableListOf("Processor", "RAM", "Graphics Card RTX 3060", "SSD")

    // adding items to lists
    shoppingList.add("Cooling System")
    shoppingList.remove("Graphics Card RTX 3060")
    shoppingList.add("Graphics Card RTX 4090")

    println(shoppingList) // [Processor, RAM, SSD, Cooling System, Graphics Card RTX 4090]
}

 

  •  Array와 List의 차이점

Array는 고정 크기의 배열, List는 요소 개수 제한 없는 컬렉션이다.

 

  • mutableListOf()를 val로 선언하는 이유

val은 참조(reference)를 바꿀 수 없다는 뜻으로, 그 안에 있는 내용(data)는 바꿀 수 있다.

val list = mutableListOf("a", "b", "c")
list.add("d")  // ✅ 가능
list.remove("b")  // ✅ 가능
// list = mutableListOf("x", "y")  // ❌ 안 됨! -> 참조 자체를 바꾸려 하니까

val list로 선언한 것은 이 변수가 특정 리스트를 가리키는 참조(주소)를 바꾸지 않겠다는 의미다. 이 리스트의 주소는 고정되지만, 리스트 내부의 요소는 변경할 수 있다. 반면 var로 선언하면, 이 변수는 나중에 다른 리스트를 가리키는 것으로 변경될 수 있다. 즉, 리스트 전체를 새로 덮어쓸 수 있다는 의미다.
따라서 일반적으로는 리스트 안의 요소만 바꾸고 참조는 유지하는 경우가 많기 때문에, 리스트 전체를 다른 것으로 바꾸는 실수를 막기 위해 val 선언을 권장한다.

 

리스트에서의 set 메서드

set() 메서드는 리스트에서 특정 인덱스의 항목을 새로운 값으로 교체사용된다. 개의 매개변수를 받으며, 번째는 바꾸고 싶은 요소의 인덱스, 번째는 새로 넣을 이다.

 

Contains 메서드 - 항목이 리스트에 있는지 확인

contains() 메서드는 리스트 안에 특정 항목이 포함되어 있는지 여부를 확인할 때 사용한다. 찾고 싶은 값을 인자로 전달하면 되고, 결과는 Boolean 타입으로 반환된다.

 

Kotlin에서 리스트 조작 연습

package com.example.kotlinbasics

fun main() {
    val fruitsList = mutableListOf("apple", "peach", "strawberry", "cherry", "banana")
    println(fruitsList)
    fruitsList.add("melon")
    println(fruitsList)
    fruitsList.remove("apple")
    println(fruitsList)
    if (fruitsList.contains("watermelon")) {
        println("Has watermelon")
    }
    else {
        println("No watermelon")
    }
}

 

 

리스트와 함께 사용하는 for루프

  for(item in shoppingList) {
        println(item)
        if (item == "RAM"){
            shoppingList.removeLast()
            break;
        }
    }

    println(shoppingList)

 

for 루프에서 인덱스 가져오기

  • for (i in 1..10) { ... } : 1부터 10까지 1씩 증가
  • for (i in 1 until 10) { ... } : 1부터 9까지 1씩 증가(10 미포함)
  • for (i in 2...10 step 2) { ... } : 2부터 10까지 2씩 증가
  • for (i in 10 downTo 1) { ... } : 10부터 1까지 1씩 감소

 

으l욕 없을 ㄸĦ는 달콤쌉ᄊト름 ㅈΓ몽ㅊr를 ㅁトんıヱ ㅎb을 Łй보ㅈΓ

 

그냥 머리 아픈 거 가타요... 호엥...

2025. 04. 12

LC

어휘

  • sack :  자루
  • photocopier : 복사기
  • fix a meal : 식사 준비를 하다

문장

  • Has anyone fixed the photocopier yet?
    • 누군가 복사기를 고쳤나요?
  • But as you probably noticed, my access card isn’t working. Today’s my first day back after a two-week holiday, and I’m late.
    • 그러나 당신이 아마 알아챘듯이, 제 출입카드가 작동하지 않아요. 오늘은 제가 2주간의 휴가 후 돌아온 첫날인데, 늦었어요.

RC

Although the singer gave a competent performance, audience members were disappointed because they were expecting a superior one.

  • competence : 능숙함
  • competent : (수준이) 괜찮은, 만족할 만한
  • competing : 경쟁하는 
    • compete의 현재분사

 

The auto maker's first compact car quickly sold in the millions upon its release, and this was largely due to its affordability. 

  • autpmaker : 자동차 회사, 자동차 업체
  • compact car : 소형차
  • release : 출시, 발표, 공개
  • largely : 주로, 대체로
  • affordability : (가격이) 적당함, 감당할 수 있는 비용
  • proficiency : 숙달, 능숙

 

Beth Tolley was selected to supervise the Snapfire project, as she understands the details of the undertaking better than most.

  • supervise : 감독하다
  • undertaking : 일, 사업
  • the most : 대다수의 사람

 

  • enclosed : 동봉된
  • invoice : 송장, 청구서
  • shipment : 수송
  • as of today : 오늘 현재로
  • past due : 기일이 지난
  • remit : 송금하다
  • applicable : 해당하는
  • late fee : 연체료

2025.04.13

LC

어휘

  • microphone : 마이크
  • podium : 연단, 지휘대
  • expense  : 비용
  • put together : 만들다, 합치다
  • favor : 부탁, 호의

문장

  • Could I ask a favor of you?
    • 당신에게 한 가지 부탁을 해도 될까요?

 

RC

  • excel : 뛰어나다, 탁월하다
  • commence : 시작하다, 시작되다
  • coincide with : ~와 부합하다, 일치하다
  • vigilant : 바짝 경계하는

 

The company receives correspondence from customers on a regular basis, most of whom request more information about products or inquire about product use.

  • correspondence : 서신, 편지
  • correspond : 일치하다, 부합하다
  • correspondent : 기자, 특파
  • on a regular basis : 정기적으로, 규칙적으로
  • inquire about : ~에 관하여 문의하다, 묻다

 

  • mandate : 권한을 주다
  • allowance 수당
  • legislator  국회의원
  • reading : (의회의 법안 심의) 독회
  • grant : 허용하다, 승인하다
  • privatize : 민영화하다

Amy와 그녀의 커피 함수

커피 없이는 코드 한 줄도 못짜는 Amy(와 본인)

코틀린에서 함수를 선언하려면 fun이라는 키워드를 이용한다. 

  • 함수 선언 형식 : fun 함수명 (매개변수명: 타입) : 반환 타입 {...}
fun makeCoffee(name : String, sugarCount : Int) {
    if (sugarCount == 0) {
        println("Coffee with no sugar for $name")
    }
    else if (sugarCount == 1) {
        println("Coffee with $sugarCount spoon of sugar for $name")
    }
    else {
        println("Coffee with $sugarCount spoons of sugar for $name")
    }
}

 

함수의 매개변수에는 기본값을 선언할 수 있다. 만약 어떤 매개변수에 기본값을 선언했다면 호출할 때 인자를 전달하지 않아도 되며 이때 선언문에 명시한 기본값이 적용된다. 

fun read(
    b: ByteArray,
    off: Int = 0,
    len: Int = b.size,
) { /*...*/ }

 

사용자 입력과 함께 커피는 누구를 위한 것인가

package com.example.kotlinbasics

fun main() {
    println("Who is this coffee for?")
    val name = readln()
    println("How many pieces of sugar do you want?")
    val sugarCount = readln().toInt()

    makeCoffee(name, sugarCount)
}

// Define Function
fun makeCoffee(name : String, sugarCount : Int) {
    if (sugarCount == 0) {
        println("Coffee with no sugar for $name")
    }
    else if (sugarCount == 1) {
        println("Coffee with $sugarCount spoon of sugar for $name")
    }
    else {
        println("Coffee with $sugarCount spoons of sugar for $name")
    }
}

 

반환 유형에 대해 더 알아보기

fun divide(num1 : Int, num2 : Int):Double {
    val result = num1 / num2
    return result
}

해당 함수는 에러가 발생한다. Int와 Int를 나누면 결과가 Int일 것이라고 예상하지만, 반환 타입을 Double로 명시해두었기 때문에 오류가 발생하는 것이다.
이 문제를 해결하려면 num1과 num2 모두를 Double로 변환하거나, 둘 중 하나만 Double로 변환해도 된다.

 

클래스와 객체 생성 그리고 생성

코틀린에서 클래스는 class 키워드로 선언한다. 이때

클래스 이름은 무조건 단수로 짓는다.

 

클래스의 멤버는 생성자, 변수, 함수, 클래스로 구성된다. 이 중 생성자는 constructor 키워드로 선언하는 함수이다. 그리고 클래스 안에 다른 클래스를 선언할 수도 있다. 이때 생성자는 주 생성자보조 생성자로 구분되는데 주 생성자는 클래스 선언부에 constructor 키워드를 사용해 정의하며 반드시 선언해야 하는 것은 아니고 한 클래스에 하나만 가질 수 있다. 만약 어노테이션이나 public, private과 같은 접근 제한자가 없다면, constructor 키워드는 생략할 수 있다.

 

init 키워드로 지정한 영역은 클래스 객체를 생성하는 순간 자동으로 실행된다. 

class User(name: String, count: Int) {
	init {
    	println("I am init...")
    }
}
fun main() {
	val user = User("yeonee", 10)
}

// i am init...

 

생성자의 매개변수는 기본적으로 생성자에서만 사용할 수 있는 지역 변수이다. 즉 init 영역에서만 사용가능하다.

class User(name: String, count: Int) {
	init {
    	println("name : $name, count : $count")	// 성공
    }
    
   fun someFun() {
   	println("name : $name, count : $count")	// 오류
   }
]

 

코틀린은 프로퍼티를 선언하고, 그것을 주 생성자에서 초기화하는 간결한 문법을 제공한다. 주 생성자 안에서 val이나 var 키워드로 선언하면 클래스의 멤버 변수가 된다. 

멤버 변수, 즉 프로퍼티는 실제 객체의 일부라는 뜻이고 매개변수는 그저 객체에 정보를 전달한 것이다. 변수나 클래스의 프로퍼티를 String으로 다루고 싶다면 중괄호를 추가해서 어디서부터 어디까지가 영역인지 구분한다. 또한 프로퍼티에도 기본값을 줄 수 있다.

package com.example.kotlinbasics

class Book (val title : String = "Unknown",
            val author : String = "Anonymous", 
            val yearPublished : Int = 2025) {
}
package com.example.kotlinbasics

fun main() {
    val myBook = Book()
    println("title : ${myBook.title}, " +
            "author : ${myBook.author}, " +
            "yearPublished : ${myBook.yearPublished}")
}

 

데이터 클래스와 커피

코틀린의 데이터 클래스는 주로 데이터를 저장하는 데 사용된다. class대신에 data class 키워드를 사용하여 선언하다.data class를 사용하면 toString(), equals(), hashCode() 같은 유용한 함수들이 자동으로 만들어진다. 데이터 클래스에서는 별도의 함수 정의가 불가능하다.

 

데이터 클래스의 주요 특징은 다음과 같다.

  • Immutability : 불변성
  • Standard Methods : 표준 메서드
  • Destructuring Declarations : 구조 분해 선언

우선, 데이터 클래스는 주로 val을 사용해 불변 속성을 권장하므로, 단순하고 변경되지 않는 데이터를 표현하기에 적합하다. 또한, toString(), equals(), hashCode() 같은 기본 메서드들을 코틀린이 자동으로 생성해준다. 마지막으로, 구조 분해 선언을 지원해 객체의 속성들을 개별 변수로 간편하게 꺼내 쓸 수 있다.

package com.example.kotlinbasics


data class CoffeeDetails(
    val sugarCount: Int,
    val name: String,
    val size: String,
    val creamAmount: Int)

fun main() {
    val coffeeForYeonee = CoffeeDetails(0, "yeonee", "L", 0)
    makeCoffee(coffeeForYeonee)
}

// Define Function
fun makeCoffee(coffeeDetails: CoffeeDetails) {
    if (coffeeDetails.sugarCount == 0) {
        println("Coffee with no sugar for ${coffeeDetails.name}")
    }
    else if (coffeeDetails.sugarCount == 1) {
        println("Coffee with ${coffeeDetails.sugarCount} " +
                "spoon of sugar for ${coffeeDetails.name}")
    }
    else {
        println("Coffee with ${coffeeDetails.sugarCount} spoons of sugar for ${coffeeDetails.name}")
    }
}

 

요약

https://tutorials.eu/basic-kotlin-syntax-functions-objects-and-classes-in-kotlin-day-3-android-14-masterclass/

 

풀이

ETT로도 풀린다는데 모르겠어서 HLD로 풀었습니다. 일단 이 문제는 영어라서 해석부터 해보자면...

  • 정점 1번은 Bitburg 마을입니다.
  • 1번부터 N번까지의 정점 N개는 N-1개의 시골 도로로 연결되어 있습니다.
  • 이 시골도로를 고속도로로 하나씩 바꿉니다.
  • "A a b" 형식의 입력은 a번 정점과 b번 정점을 잇는 시골 도로를 고속도로로 바꿉니다.
  • "W a" 형식의 입력은 Bitburg 마을, 즉 1번 정점부터 a번 정점까지 가는 경로에 존재하는 시골 도로의 개수를 출력합니다.

마찬가지로 간선에 값이 있는 경우이기 때문에 부모와 자식 노드 중 자식 노드에 정보를 저장했습니다. 그러면 포인트 업데이트가 되고, 시골도로를 1, 고속도로를 0으로 두어 구간합 세그먼트 트리를 구성했습니다. 

 

코드

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int n; cin >> n;
	vector<vector<int>> g(n + 1);
	for (int i = 1; i < n; i++) {
		int a, b; cin >> a >> b;
		g[a].push_back(b);
		g[b].push_back(a);
	}

	vector<int>dep(n + 1, 0);
	vector<int>par(n + 1, -1);
	vector<int>sz(n + 1, 1);
	auto dfs1 = [&](const auto& self, int cur)-> void {
		for (int& nxt : g[cur]){
			auto it = find(g[nxt].begin(), g[nxt].end(), cur);
			g[nxt].erase(it);
			dep[nxt] = dep[cur] + 1;
			par[nxt] = cur;
			self(self, nxt);
			sz[cur] += sz[nxt];
			if (sz[g[cur][0]] < sz[nxt]) swap(g[cur][0], nxt);
		}
	};
	dfs1(dfs1, 1);

	vector<int>in(n + 1, 0);
	vector<int>top(n + 1, 0);
	int cnt = 0;
	auto dfs2 = [&](const auto& self, int cur) -> void {
		in[cur] = ++cnt;
		for (int nxt : g[cur]){
			if (nxt == g[cur][0]) top[nxt] = top[cur];
			else top[nxt] = nxt;
			self(self, nxt);
		}
	};
	top[1] = 1;
	dfs2(dfs2, 1);

	int t_sz = 1;
	while(t_sz < n) t_sz *= 2;
	vector<int> tree(t_sz * 2, 0);
	
	auto update = [&](int i) {	// i is children
		i = i - 1 + t_sz;
		if (tree[i] == 0) tree[i] = 1;
		else tree[i] = 0;

		while(i > 1) {
			i /= 2;
			tree[i] = tree[i * 2] + tree[i * 2 + 1];
		}
	};

	auto query = [&](int l, int r) {
		l = l - 1 + t_sz;
		r = r - 1 + t_sz;
		int res = 0;
		while(l <= r) {
			if (l % 2 == 1) res += tree[l], l++;
			if (r % 2 == 0) res += tree[r], r--;

			l /= 2, r /= 2;
		}
		return res;
	};

	// init
	for (int i = 2; i <= n;i++) update(i);

	auto edge_update = [&](int a, int b) {
		// b is children
		if (par[a] == b) swap(a, b);
		update(in[b]);
	};

	auto path_query = [&](int a, int b) {
		int res = 0;
		while(top[a] != top[b]) {
			if (dep[top[a]] < dep[top[b]]) swap(a, b);
			res += query(in[top[a]], in[a]);
			a = par[top[a]];
		}
		if (dep[a] > dep[b]) swap(a, b);
		res += query(in[a] + 1, in[b]);

		return res;
	};

	int m; cin >> m;
	for (int i = 1; i < m + n; i++) {
		string str; cin >> str;
		int a;	cin >> a;
		if (str == "A") {
			int b; cin >> b;
			edge_update(a, b);
		}
		else {
			cout << path_query(1, a) << '\n';
		}
	}
}

플4까지 단 90..!

문제 풀 때마다 기여 중인데 은근 레이팅이 잘 오릅니다. 2-3씩 오르는 

급성장!

2일차! 부족한 개념은 Do it! 책을 활용했습니당. 코틀린 공식 문서가 굉장히 잘 되어있네요... 그치만 영어는 시러

몇 가지 부분은 건너뛰었습니당


Int 데이터 유형

Type Size (bits) Min value Max value
Byte 8 -128 127
Short 16 -32768 32767
Int 32 -2,147,483,648 (-231) 2,147,483,647 (231 - 1)
Long 64 -9,223,372,036,854,775,808 (-263) 9,223,372,036,854,775,807 (263 - 1)

 

Kotlin에서는 변수를 선언하는 두 가지 방법으로 val과 var이 있다.

  • val : value의 줄임말로, 불변하는 값. 초깃값을 할당하면 재할당 할 수 없다.
  • var : variable의 줄임말로 변하는 값. 초깃값을 할당하고 나서도 재할당 할 수 있다.

변수명 뒤에는 콜론(:)을 추가해 타입을 명시할 수 있으며, 타입을 명시하지 않아도 자동으로 Int로 저장된다. 이를 타입 추론이라고 한다.  만약 Int 범위가 넘어간다면 자동으로 Long으로 저장된다.

 

큰 숫자를 사용할 예정으로 Long을 쓰고 싶다면 L을 표기한다. 만약 Byte 또는 Short를 사용하고 싶다면 콜론 기호(:)를 통해 명시적으로 선언한다. 

val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1

 

Hello World 실행

온라인에서 코틀린을 실행시킬 수 있는 사이트

https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMi4xLjIwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiLyoqXG4gKiBZb3UgY2FuIGVkaXQsIHJ1biwgYW5kIHNoYXJlIHRoaXMgY29kZS5cbiAqIHBsYXkua290bGlubGFuZy5vcmdcbiAqL1xuZnVuIG1haW4oKSB7XG4gICAgcHJpbnRsbihcIkhlbGxvLCB3b3JsZCEhIVwiKVxufSJ9

 

부호 없는 정수

Type Size (bits) Min value Max value
UByte 8 0 255
UShort 16 0 65,535
UInt 32 0 4,294,967,295 (232 - 1)
ULong 64 0 18,446,744,073,709,551,615 (264 - 1)

 

-128 ~ 127 대신 0~255를 사용한다. 1이 아닌 0부터 시작하고 양수 방향으로만 범위가 커진다.  

 

대입하는 값 끝에 u또는 U를 붙여줘야 한다. 대문자, 소문자는 관계없다. 

val b: UByte = 1u  // UByte, expected type provided
val s: UShort = 1u // UShort, expected type provided
val l: ULong = 1u  // ULong, expected type provided

val a1 = 42u // UInt: no expected type provided, constant fits in UInt
val a2 = 0xFFFF_FFFF_FFFFu // ULong: no expected type provided, constant doesn't fit in UInt
val a = 1UL // ULong, even though no expected type provided and the constant fits into UInt

정수의 전체 bit범위를 양수로 사용하고 싶을 때 부호없는 정수를 사용한다.

 

Booleans true, false, 부정

오직 두가지 값 true 또는 false를 가진다. Boolean?은 true, false, null 세 가지 값 중 하나를 가질 수 있다.

또한 세 개의 내장된 연산자를 가진다. 

  • ||
  • &&
  • !

 

Char, 유니코드 및 백슬래시 이스케이프 문자

하나의 문자만 저장 가능하며 작은 따옴표(')로 감싸서 표현한다. 

 

유니코드를 표현하려면, \u를 붙이고 뒤에 유니코드 숫자만 붙인다.

println('\uFF00')

 

문자열

일련의 문자, 즉 단어나 문단들 같은 긴 문자 같은 것들을 저장할 수 있다. 큰따옴표(")로 감싸서 표현한다. 문자열(String)의 요소들은 문자(Char)들이며, s[i]와 같은 인덱싱 연산을 통해 접근할 수 있다. 또한, for (c in str) 같은 for문을 사용해 이 문자들을 순회(iterate)할 수 있다.

한 번 문자열을 초기화하면, 그 값을 바꾸거나 새 값을 할당할 수 없다. 문자열을 변형하는 모든 연산은 새로운 String 객체로 결과를 반환하며, 원래 문자열은 그대로 유지된다.

val str = "abcd"

// Creates and prints a new String object
println(str.uppercase())
// ABCD

// The original string remains the same
println(str) 
// abcd

 

ReadIn 및 toInt를 사용하여 문자열을 정수 변수로 변환

readln() 함수를 사용하여 표준 입력으로부터 데이터를 읽는다. 이때 전체 줄을 문자열로 읽는다. 만약 다른 자료형으로 변환하고 싶으면 .toInt(), .toLong(), .toDouble(), .toFloat(), 또는 .toBoolean()와 같은 함수를 이용한다. 즉 .toInt()를 사용해서 Integer을 만들 수 있다. 이는 String을 파싱하여 Integer 숫자로 결과를 반환한다.

val age = readln().toInt()

 

.toInt(), .toDouble() 같은 형 변환 함수는, 입력된 문자열이 해당 자료형에 맞는 올바른 값일 거라고 가정하고 동작한다. 따라서서 "hello"처럼 숫자가 아닌 문자열을 변환하려고 하면 에러(예외)가 발생한다.

 

Else if 및 in 키워드

if (age in 18..39){...}

if (age >= 18 && age < 40){...}

 

in은 범위 지정 연산자로, 두 조건식은 같은 의미이다.

 

가위 바위 보

컴퓨터의 선택 받기

package com.example.rockpaperscissors

fun main(){
var computerChoice = ""
var playerChoice = ""
println("Rock, Paper or Scissors? Enter your choice!")
playerChoice = readln()

val randomNumber = (1..3).random()
if (randomNumber == 1) computerChoice = "Rock"
else if (randomNumber == 2) computerChoice = "Paper"
else computerChoice = "Scissors"

println(computerChoice)
}

1부터 3까지의 숫자 중 .random() 함수를 이용하여 숫자 하나를 무작위로 선정하여 변수 randomNumber에 저장한다. 

 

승자 찾기

when문을 이용한다. when 키워드 다음의 소괄호()에 넣은 데이터가 조건이 되고 이 값에 따라 -> 오른쪽에 있는 구문을 실행한다. 또는 데이터를 명시하지 않고 조건만 명시할 수도 있다. 표현식으로도 사용할 수 있는데, 즉 when 문의 실행 결과를 반환할 수 있다. when문을 표현식으로 사용할 때는 else문을 생략할 수 없다. 

val winner = when {
playerChoice == computerChoice -> "Tie"
playerChoice == "Rock" && computerChoice == "Scissors" -> "Player"
playerChoice == "Paper" && computerChoice == "Rock" -> "Player"
playerChoice == "Scissors" && computerChoice == "Paper" -> "Player"
else -> "Computer"
}

승자 표시

if (winner == "Tie") {
println("It's a tie")
}
else {
println(winner + " won!")
}
if (winner == "Tie") {
println("It's a tie")
}
else {
println("$winner won!")
}

String 타입의 데이터에 변숫값이나 어떤 연산식의 결괏값을 포함해야 할 때는 $ 기호를 이용한다. 이를 문자열 템플릿이라고 한다. 

 

카운터와 함께 While 루프

fun main() {
var count = 0
while (count < 3) {
println("Count is $count")
count++
}
}

사용자 입력과 함께 While 루프

fun main() {
var userInput = readln()
while (userInput == "1") {
println("While loop executed")
userInput = readln()
}
println("Loop is done!")
}

퀴즈7: 코딩 연습 - 가위바위보 게임에서 플레이어 입력 유효성 검사

package com.example.rockpaperscissors

fun main(){
var computerChoice = ""
var playerChoice = ""
println("Rock, Paper or Scissors? Enter your choice!")
playerChoice = readln().lowercase()

while(playerChoice != "rock" && playerChoice != "paper" && playerChoice != "scissors"){
println("Rock, Paper or Scissors? Enter your choice!")
playerChoice = readln().lowercase()
}
val randomNumber = (1..3).random()

when (randomNumber) {
1 -> {
computerChoice = "Rock"
}
2 -> {
computerChoice = "Paper"
}
3 -> {
computerChoice = "Scissors"
}
}

println(computerChoice)
computerChoice = computerChoice.lowercase()

val winner = when {
playerChoice == computerChoice -> "Tie"
playerChoice == "rock" && computerChoice == "scissors" -> "Player"
playerChoice == "paper" && computerChoice == "rock" -> "Player"
playerChoice == "scissors" && computerChoice == "paper" -> "Player"
else -> "Computer"
}

if (winner == "Tie") {
println("It's a tie")
}
else {
println("$winner won!")
}
}
  • 플레이어가 가위, 바위, 보만 입력하도록 강제
  • 대소문자 구분을 없앰 -> rock, Rock, ROCK, rOCk 모두 바위로 처리
println(computerChoice) // Rock

computerChoice.lowercase()
println(computerChoice) // Rock

computerChoice = computerChoice.lowercase()
println(computerChoice) // rock

 

요약

https://tutorials.eu/exploring-basic-kotlin-syntax-and-structure-day-2-android-14-masterclass/

 

Exploring Basic Kotlin Syntax - Day 2 Android 14 Masterclass

Welcome to Day 2 of our Android 14 Masterclass, where we dive into the Basic Kotlin Syntax and Structure. Embark on this coding journey!

tutorials.eu

 

 

풀이

  • P u v: u번 농장과 v번 농장 사이의 경로에 존재하는 모든 도로에 나무를 하나씩 심는다.
  • Q u v: u번 농장과 v번 농장 사이의 도로에 존재하는 나무의 개수를 출력한다.

update와 query 모두 경로이며 특히 updaterange update이다. 따라서 lazy가 사용되어야함을 알 수 있습니다.

 

처음에는 hld에 어떻게 lazy 세그트리를 적용하지? 고민이 되어서 lazy가 아니라 그냥 세그먼트 트리로도 풀리나?라고 생각을 했었습니다. 그러나 아무리 봐도 이건 range update로 lazy를 쓰는 게 확실해 보이는데.......

 

결론은, u에서 v로 가는 path별로 lazy propagation을 이용해 업데이트합니다. 그리고 query 출력에서도 각 path별로 값을 구해서 전부 합한 값을 출력합니다. 

그리고 도로, 즉 간선에 나무를 심습니다. 이때 부모와 자식 중 자식 노드에 값을 저장합니다. 이 아이디어는 트리와 쿼리 1 (https://www.acmicpc.net/problem/13510)에서 얻었습니다. 이처럼 자식 노드에 값을 저장하기 때문에 u와 v의 top이 같아졌을 때, update 및 query를 호출할 경우 첫번째 인자로 in[u] + 1, 1을 더해줘야합니다. 

 

코드

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int n, m; cin >> n >> m;
	vector<vector<int>> g(n + 1);
	for (int i = 1; i < n;i++) {
		int u, v;	cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}

	// path decomposition
	vector<int> dep(n + 1, 0);
	vector<int> par(n + 1, -1);
	vector<int> sz(n + 1, 1);
	auto dfs1 = [&](const auto& self, int cur) -> void {
		for (auto& nxt : g[cur]) {
			auto it = find(g[nxt].begin(), g[nxt].end(), cur);
			g[nxt].erase(it);
			dep[nxt] = dep[cur] + 1;
			par[nxt] = cur;
			self(self, nxt);
			sz[nxt] += sz[cur];
			if (sz[g[cur][0]] < sz[nxt]) swap(g[cur][0], nxt);
		}
	};
	dfs1(dfs1, 1);

	vector<int> in(n + 1, 0);
	vector<int> top(n + 1, 0);
	int cnt = 0;
	auto dfs2 = [&](const auto& self, int cur)-> void {
		in[cur] = ++cnt;
		for (int nxt : g[cur]) {
			if (nxt == g[cur][0]) top[nxt] = top[cur];
			else top[nxt] = nxt;
			self(self, nxt);
		}
	};
	top[1] = 1;
	dfs2(dfs2, 1);

	int t_sz = 1;
	while(t_sz < n) t_sz *= 2;
	vector<int> tree(t_sz * 2, 0);
	vector<int> lazy(t_sz * 2, 0);

	auto push = [&](int node, int l, int r) -> void {
		if (lazy[node] != 0) {
			tree[node] += lazy[node] * (r - l + 1);
			if (l != r) {
				lazy[node * 2] += lazy[node];
				lazy[node * 2 + 1] += lazy[node];
			}
			lazy[node] = 0;
		}
	}; 

	auto update = [&](int l, int r) {
		auto rec = [&](const auto& self, int node, int node_l, int node_r) -> void {
			push(node, node_l, node_r);
			if (r < node_l || node_r < l) return;
			if (l <= node_l && node_r <= r) {
				lazy[node] = 1;
				push(node, node_l, node_r);
				return;
			}
			int mid = (node_l + node_r) / 2;
			self(self, node * 2, node_l, mid);
			self(self, node * 2 + 1, mid + 1, node_r);
			tree[node] = tree[node * 2] + tree[node * 2 + 1];
		};
		rec(rec, 1, 1, t_sz);
	};

	auto query = [&](int l, int r) -> long long {
		auto rec = [&](const auto& self, int node, int node_l, int node_r) -> long long {
			push(node, node_l, node_r);
			if (r < node_l || node_r < l) return 0;
			if (l <= node_l && node_r <= r) return tree[node];
			int mid = (node_l + node_r) / 2;
			return self(self, node * 2, node_l, mid) + self(self, node * 2 + 1, mid + 1, node_r);
		};
		return rec(rec, 1, 1, t_sz);
	};

	auto path_update = [&](int u, int v) {
		while(top[u] != top[v]) {
			if (dep[top[u]] < dep[top[v]]) swap(u, v);
			update(in[top[u]], in[u]);
			u = par[top[u]];
		}
		if (dep[u] > dep[v]) swap(u, v);
		update(in[u] + 1, in[v]);
	};

	auto path_query = [&](int u, int v) -> long long {
		int res = 0;
		while(top[u] != top[v]){
			if (dep[top[u]] < dep[top[v]]) swap(u, v);
			res += query(in[top[u]], in[u]);
			u = par[top[u]];
		}
		if (dep[u] > dep[v]) swap(u, v);
		res += query(in[u] + 1, in[v]);
		return res;
	};

	for (int i = 0;i < m;i++) {
		string q; cin >> q;
		int u, v; cin >> u >> v;
		if (q == "P") path_update(u, v); 
		else cout << path_query(u, v) << '\n';
	}
}

+ Recent posts