BOJ(백준) 1952번 달팽이2



문제 : M줄 N칸의 Matrix에 달팽이가 (0,0)부터 돌았을 때 몇 번 방향을 꺾는지 확인하는 문제다.


매우 쉬운 문제지만, 나의 코드의 1/3밖에 안되지만 속도도 더 빠른 BOJ id tncks0121님의 코드를 소개하고자 글을 쓴다.


 먼저 나의 코드는 아래와 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fun main(args: Array<String>) {
    val (r,c)= readLine()!!.split(' ').map { it.toInt() }
    val map=Array(r){IntArray(c)}
    val dx= arrayOf(0,1,0,-1)
    val dy= arrayOf(1,0,-1,0)
    var res=0
    var count=1

    fun go(x:Int,y:Int,dir:Int){
        map[x][y]=count++
        val bx=x+dx[dir]
        val by=y+dy[dir]
        if(bx in 0 until r && by in 0 until c && map[bx][by]==0){
            go(bx, by, dir)
        }
        else{
            res++
            if(map[x+dx[(dir+1)%4]][y+dy[(dir+1)%4]]==0)
               go(x+dx[(dir+1)%4],y+dy[(dir+1)%4],(dir+1)%4)
        }
    }
    map[0][0]=1
    go(0,0,0)
    println(res-1)
}
cs


 나의 코드 개념은 처음(0,0)부터 돌면서 matrix에 다 숫자를 써준다는 것이다. 

tncks님의 코드를 보고 감탄을 할 정도로 내 코드는 역겨웠다....



 다음은 tncks0121님의 코드이다.


1
2
3
4
5
6
7
8
9
10
11
12
fun main(args: Array<String>) {
  fun go(n: Int, m: Int): Int = when {
    n == 1 -> 0
    m == 1 -> 1
    n == 2 -> 2
    m == 2 -> 3
    else -> 4 + go(n - 2, m - 2
  }
  val (M, N) = readLine()!!.split(" ").map{ it.toInt() }
  println(go(M, N))
}
cs


  go 함수를 파헤쳐보자...

 go함수 내부의 when절 내부에 else문을 보면 4 + go(n-2, m-2) 로 되어있는데

이것은 한바퀴 돌았다는 것을 의미한다.  4 = 방향을 4번 바꿨다 = 한바퀴 돌았다. 

그리고 n과 m을 2씩 줄여서 재귀함수를 돌린다. 왜냐하면 한바퀴 돌면  Matrix의 행과 열의 양끝을 돈 것이므로...


이제 else위의 경우를 하나하나씩 살펴보자.

 n == 1 -> 0   : 이것은 row(행)가 1개밖에 없는 것으로  col(열)이 몇개가 되든 우측 방향으로 가기만 하면 끝난다는 생각이므로 , 0을 return하게 하였다.


 m == 1 -> 1    : 이것은 col이 1개밖에 없는 것으로 1을 return한다. 이유는 재귀를 돌고나면 우측방향으로 돌고있다고 생각하기 때문에(처음에 시작이 오른쪽 방향임), 그런데 현재 col이 1이므로 아래 방향으로 가야한다 = 한번 방향 전환을 해야함. 따라서 1을 return 한다.


 n == 2 -> 2   : 이 것은 row가 2인 상황이다. 위에 내용을 이해 했다면 왜 2를 return 하는지 알 수 있다.

한번 더 설명하자면, row가 2인 경우 col의 크기에 상관없이 방향 전환을  ( 우측 상단 끝, 우측 하단 끝) 이렇게 2번 하면 된다. 따라서 2를 return 한다.


 m == 2 -> 3 : 이 설명은 생략한다.



 

 결론 : 내 코드는 Matrix의 모든 Point를 다 돌아보도록 짰는데, 이 코드는 재귀를 몇번 돌지도 않는다.....



 

 일단 내가 판단하기에 잘 짰다고 생각해서 코드를 보고 퍼왔다. tncks님이 이 글을 보신다면 댓글좀요.

코틀린 좀 가르쳐줘요.

 Spring 기본 개념을 잘 적어놓은 사이트를 공유하고자 한다.

https://12bme.tistory.com/157


 다음 글은 내가 Spring을 사용한 예제를 자세히 파헤쳐 볼 예정이다.

그리고 Rest의 개념 글도 써야겠다.


'Spring Framework' 카테고리의 다른 글

싱글톤 패턴과 Spring의 싱글톤  (1) 2019.01.05

 요즘 서버를 구현하는데 있어서 기본지식의 부족함을 느꼈다. 개념 하나를 몰라서 찾아보면 또 모르는 개념이 나오고 이게 반복되다보니 무지함을 많이 느껴서 
앞으로 모르는 것이 나올때마다 하나씩 포스팅하려고한다.

 일단 클라이언트와 서버 개념부터 잡고 간다. 많이 들어봤고, 대충 알지만 정확하게 익히는게 좋을 것 같아서다.
이 글은 http://cupjoo.tistory.com/   이 블로그를 참조했다. 

  1. 웹 클라이언트와 서버.

     1) 클라이언트와 서버

  

  기본적으로 클라이언트와 서버는 웹 브라우저에서 특정 페이지를 요청하면 웹 서버는 해당 페이지를 반환해주는 역할을 한다. 초창기 웹이 출현했을 때는
 논문 열람 사이트와 같은 정적인 웹 페이지들을 하이퍼링크로 연결하는 것이 전부였기에 위의 구조가 유행했었다.

  하지만 점차 사용자가 늘어나자, DB를 통해 여러 데이터를 입력하고 조회할 수 있는 기능과 동적 페이지에 대한 필요성이 대두되었고, 새로운 서버 모델이 필요하게 되었다.

       
    2) 정적 페이지와 동적 페이지

 그렇다면 정적 페이지와 동적 페이지는 도대체 차이가 뭘까? 두 페이지의 차이는 페이지를 요청한 사용자의 정보와 현재 시점에 따라 내용이 변한다는 것이다. 우선 정적 페이지에 대해 알아보자.


정적 페이지는 서버에 미리 저장되어 있으며, 주로 HTML, CSS, Javascript, 이미지만으로 이루어져 있다. 사용자가 페이지를 요구하면 서버는 해당 정적 페이지를 반환해주는 역할만 한다.

 정적 페이지로 웹 페이지를 제작했다고 했을 때, 해당 페이지는 누가, 언제 열람하더라도 같은 내용이 유지된 상태로 사용자에게 보여진다. 물론 Javascript를 통해 글자의 색 파란색으로 바꾼다던가, Building의 정보를 411로 바꾸는 등 사이트를 동적으로 제어하더라도, 이는 사전에 정의된 동작으로 인해 결국 모든 사용자에게 같은 정보를 제공하므로 정적 페이지다.

그에 반면 동적 페이지는 사용자가 페이지를 요청할 시, DB로부터 얻은 해당 사용자의 정보나 현재 시점 정보 등을 가공하여 새롭게 생성한 페이지를 말한다. 페이스북 사이트를 예로 보겠다.




  로그인 하지 않은 경우, 위와 같은 화면이 나온다.



 로그인을 하게되면 사용자마다 모두 다른 게시물이 나오는 것을 알 수 있다.




  1.  CGI
         
   위에서 설명한 동적 페이지를 사용자에게 제공하기 위해 웹 서버 내에 프로그래밍 기능이 들어가는 방식을 CGI라고 한다.

 하지만 CGI 방식의 경우 웹 서버에서 각각의 클라이언트 요청에 대해 독립적인 별도의 프로세스를 생성해 요청을 처리하므로, 시스템에 부하가 커져 문제가 발생하게 된다.



  1. 웹 어플리케이션 서버
           
 1) 개념

CGI 방식의 문제점을 해결하기 위해 나타난 것이 웹 어플리케이션 서버이다. 웹 어플리케이션 서버는 웹 서버와 DB 서버 사이에서 미들웨어로서, DB 연동 및 동적 페이지를 생성하기 위한 처리를 한다.




요즘은 웹 어플리케이션 서버에서도 웹 서버의 기능을 제공하는 경우가 많다. 그렇다면 이제는 웹 서버가 굳이 필요할까? 라는 의문이 들 수도 있는데, 실제로 웹 서버의 경우 단순 이미지나 html파일 등의 리소스로 이루어진 정적 페이지를 보다 빠르고 안정적으로 사용자에게 제공할 수 있다.


그 외에도, 캐시 기능, 프록시 기능 등을 비롯해 접속 가능한 클라이언트 수 제한 및 처리 프로세스 관리, 요청 및 응답 로그 기록, 안정성 확보를 위한 인증 제어 및 암호화 처리 등의 기능을 제공하기에 지금도 많은 사이트들이 두 서버를 같이 사용한다.

           
 2) 실제 서버 구조

현재 Node.js와 더불어 가장 핫한 웹 프레임워크 중 하나인 Django의 실제 서버 구조는 다음과 같이 구성되어 있다.

-웹 서버 Nginx는 정적 데이터 반환과 함께 리버스 프록시, 캐시 기능 등을 제공하며, 동적 데이터 처리 시에는 웹 어플리케이션에 브라우저 요청을 전달한다.
-웹 어플리케이션 서버에서 실행중인 웹 어플리케이션 Django는 MariaDB와 연동하여 데이터를 처리한 뒤, 템플릿을 렌더링해 생성한 HTML 파일을 브라우저에 반환한다.
-WSGI 미들웨어의 한 종류인 Gunicorn은 Nginx와 Django가 통신하기 위한 인터페이스 역할을 한다.


'데이터통신 & 네트워크' 카테고리의 다른 글

이더넷과 토큰링  (0) 2019.01.07
데이터통신 시스템&데이터 흐름 방향  (0) 2018.09.28

백준 9576번 : 책 나눠주기



 설명: 이 문제를 처음에는 이분매칭으로 풀려고 하다가, 이분매칭보다는 그냥 Greedy하게 풀면 풀리겠다고 생각해서

      그리디로 해결하였다.


    List에 사람 각각의 (a,b)를 Pair로 저장해준다. 그리고 Sorting한다.

Sorting의 기준은 b의 오른차순으로 정렬했으며, b가 똑같은 경우 a가 작은 것이 앞에 오도록 정렬하였다.


(만약 input이 

1                이라면 소팅 된 후의 값은   

5 5 (n,m)           

2 4                            1 2

3 4                           1 3

3 3                           3 3

1 2                           2 4

1 3                           3 4

)

 정렬 후 List의 처음부터 한명씩 확인한다.

그리고 그 사람의 a와 b사이를 확인하면서 가져갈 수 있는 책이 존재하면 가져가도록 하고

그 index의 visit을 true로 바꿔준다.


 핵심은 정렬의 기준을 잡고 정렬 후 이중for문을 돌면서 확인해서 책을 가져가는 것이라고 생각한다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.io.BufferedReader
import java.io.InputStreamReader

fun main(args: Array<String>) {
    val br = BufferedReader(InputStreamReader(System.`in`))
    data class Pair(val s: Int, val e: Int) : Comparable<Pair> {
        override fun compareTo(other: Pair): Int {
            if (this.e != other.e) return this.e - other.e
            return this.s - other.s
        }
    }
    repeat(br.readLine()!!.toInt()) {
        val (n, m) = br.readLine()!!.split(" ").map { it.toInt() }
        val list = Array(m) {
            val (s, e) = br.readLine()!!.split(" ").map { it.toInt() - 1 }
            Pair(s, e)
        }
        list.sort()
        
        val visit = BooleanArray(n)
        var res = 0
        for (i in 0 until m) {
            for (j in list[i].s..list[i].e) {
                if (!visit[j]) {
                    visit[j] = true
                    res++
                    break
                }
            }
        }
        println(res)
    }
}
cs









DP문제.


점화식은    dp[i][j] => i~j 까지의 최소 비용

dp[i][j]= min(dp[i][j], dp[i][k] + dp[k+1][j])    => 합친 값의 최소값을 선택. k는 i와 j의 사이값들 모두 확인해야한다.

dp[i][j]+=sum[j]-sum[j-1]   (i부터 j까지의 합 추가로 더해줘야함)


O(n^3)


문제 바로가기 : boj.kr/11066


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.io.BufferedReader
import java.io.InputStreamReader
import kotlin.math.min

fun main(args: Array<String>) {
    val br= BufferedReader(InputStreamReader(System.`in`))
    
repeat(br.readLine()!!.toInt()){
        val n=br.readLine()!!.toInt()
        val arr=br.readLine()!!.split(" ").map { it.toInt() }.toIntArray()
        val dp=Array(n){IntArray(n)}
        val sum=IntArray(n)
        sum[0]=arr[0]
        for(i in 1 until n)  sum[i]=sum[i-1]+arr[i]
        
        for(j in 1 until n){
            for(i in j-1 downTo 0){
                dp[i][j]=987654321
                for(k in i until j){
                    dp[i][j]= min(dp[i][j],dp[i][k]+dp[k+1][j])
                }
                if(i!=0)    dp[i][j]+=sum[j]-sum[i-1]
                else    dp[i][j]+=sum[j]
            }
        }
        println(dp[0][n-1])
    }
}
cs


'BOJ(백준)' 카테고리의 다른 글

[BOJ] 1952번 달팽이2  (0) 2018.12.31
[BOJ] 백준 9576번 책 나눠주기  (0) 2018.11.22
[BOJ] 백준 5397번 키로거 by Kotlin  (0) 2018.11.20
[BOJ] 백준 8983번 사냥꾼 by Kotlin  (0) 2018.11.20
[BOJ] 2170번 선 긋기 by Kotlin  (0) 2018.11.13

결과를 StringBuilder로 안하고 +로 하여 시간초과가 계속 나서 고생했던 문제.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.*

fun main(args: Array<String>) {
    val br = BufferedReader(InputStreamReader(System.`in`))
    repeat(br.readLine()!!.toInt()) {
        val str = br.readLine()!!
        val res = LinkedList<Char>()
        var now = 0
        for (i in 0 until str.length) {
            when (str[i]) {
                '<' -> if (now != 0) now--
                '>' -> if (now != res.size) now++
                '-' -> if (now != 0 && !res.isEmpty()) res.removeAt(--now)
                else -> res.add(now++, str[i])
            }
        }
        val ans = StringBuilder()
        for (ch in res) {
            ans.append(ch)
        }
        println(ans)
    }
}
cs




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.io.BufferedReader
import java.io.InputStreamReader


fun main(args: Array<String>) {
    val br=BufferedReader(InputStreamReader(System.`in`))
    val (m,n,l)=br.readLine()!!.split(" ").map { it.toInt() }
    val saro=br.readLine()!!.split(" ").map { it.toInt() }.sorted()
    val animal=Array(n){ val (x,y)=br.readLine()!!.split(" ").map { it.toInt() }
        Pair(x,y) }
    var res=0

    for(i in 0 until n){
        val (x,y)=animal[i]
        if(y>l) continue    // y값이 l보다 커지면 사대의 x좌표에 상관없이 도달 할 수 없음.
        var left=0
        var right=m-1
        while(left<=right){     // 동물의 x좌표를 가지고 사대를 이진탐색
            val mid=(left+right)/2
            if(saro[mid] in x-(l-y)..x+(l-y)){  // 동물의 x좌표에서 양옆으로 (l-y)만큼 떨어져있으면
//사냥가능하다
                res++
                break
            }
            if(saro[mid]<x-(y-l))   left=mid+1
            else    right=mid-1
        }
    }
    println(res)
}

cs


  1. Annotation 이란?
        - 본질적인 목적은 소스 코드에 메타데이터를 표현하는 것이다. 
            (메타데이터 : 데이터에 대한 데이터, 즉 어떤 목적을 가지고 만들어진 데이터)
        - 리플렉션(reflection)을 이용하면 어노테이션 지정만으로도 원하는 클래스를 주입하는 것 등이 가능하다.
 (리플렉션이란 객체를 통해 클래스의 정보를 분석해 내는 프로그램 기법을 말한다.)
        - 비지니스 로직에는 영향을 주지 않지만, 해당 타겟의 연결 방법이나 소스코드의 구조를 변경 할 수 있다.
        - Annotation의 종류에는 빌트인 어노테이션, 메타 어노테이션, 커스텀 어노테이션이 있다.

       



 2. 빌트인(Built-in) 어노테이션
         
    - 자바 SDK에서 지원하는 어노테이션으로 @Override, @Deprecated, @SuppressWarnings 등이 있다.

  • @Override : 어노테이션은 현재 메소드가 수퍼클래스의 메소드를 오버라이드한 메소드임을 명시한다.
                  만약 수퍼클래스 또는 구현해야할 인터페이스에 해당 메소드가 없으면 컴파일러가 에러를 발생시킨다.

  • @Deprecated : 해당 메소드가 더 이상 사용되지 않음을 표시한다. 
                      만약 사용할 경우 컴파일 경고를 발생시킨다.

  • @SuppressWarnings : 선언한 곳의 컴파일 경고를 무시하도록 한다.
                          Object형을 Element로 하는 컬렉션을 사용하면 컴파일러 경고가 발생하는데, 이 어노테이션을
                        사용하여 프로그래머의 의도적인 Object형 사용임을 알려 경고를 제거할 수 있다.

  • @SafeVarargs : 제너릭 같은 가변인자의 매개변수를 사용할 때의 경고를 무시한다.
  • @FunctionalInterface : 함수형 인터페이스를 지정하는 어노테이션.
                             만약 메소드가 존재하지 않거나, 1개 이상의 메소드가 존재할 경우 컴파일 오류를 발생시킨다.





      

 3. 메타(Meta) 어노테이션

  • @Retention : 자바 컴파일러가 어노테이션을 다루는 방법과 관련이 있다. 
                                        소스파일, 클래스파일, 런타임 중 어느 시점까지 어노테이션을 보유하고 있을 것인지를 결정한다.

  • @Target : 어노테이션이 적용할 위치를 선택한다.  @Target (ElementType.~)
    • ElementType.PACKAGE : 패키지 선언시
    • ElementType.TYPE : 타입 선언 시
    • ElementType.ANNOTATION_TYPE : 어노테이션 타입 선언시
    • ElementType.CONSTRUCTOR : 생성자 선언시
    • ElementType.FIELD : 멤버변수 선언시
    • ElementType.LOCAL_VARIABLE : 지역 변수 선언시
    • ElementType.METHOD : 메소드 선언시
    • ElementType.PARAMETER : 파라미터 선언시
    • ElementType.TYPE_PARAMETER : 파라미터 타입 선언시
    • ElementType.TYPE_USE : 타입선언시
  • @Documented : 해당 어노테이션을 Javadoc에 포함시킨다.
  • @Inherited : 어노테이션의 상속을 가능하게 한다.
  • @Repeatable : 연속적으로 어노테이션을 선언할 수 있게 해준다.


  (커스텀 어노테이션은 다음 글 : )




백준 2170번 선 긋기


문제 바로가기 : boj.kr/2170



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.BufferedReader
import java.io.InputStreamReader

fun main(args: Array<String>) {
    val br=BufferedReader(InputStreamReader(System.`in`))
    val n=br.readLine()!!.toInt()
    data class Pair(val start:Int,val end:Int):Comparable<Pair>{
        override fun compareTo(other: Pair): Int {
            return this.start.compareTo(other.start)
        }
    }
    val line=Array(n){ val (start,fin)=br.readLine()!!.split(" ").map { it.toInt() }
        Pair(start,fin)
    }
    line.sort()
    var now=line[0].start
    var res=0
    for(i in 0 until n){
        if(line[i].end>now){
            res += if(now>=line[i].start) {
                Math.abs(line[i].end - now)
            } else{
                Math.abs(line[i].end-line[i].start)
            }
            now = line[i].end
        }
    }
    println(res)
}
cs


백준 1017번 소수 쌍


문제 설명


 각 수에 대해 다른 수와 합이 소수이면 선택 가능하도록 간선을 만들어준다.

그리고 첫번째 수에 가능한 수를 하나씩 고정시켜놓고, 나머지를 이분매칭 시켜서 모두 매칭이 되면 res에 추가.

 

 1) 두 수의 합이 최대 2000까지이므로 에라토스테네스의 체로 2000까지 Boolean Array로 감별

 

 2) 하나의 숫자에 대해 다른 숫자와의 합이 소수이면 간선 추가.


 3) adj[0][0] , adj[0][1] ... adj[0][adj.size-1] 에 대해

 각각 B[adj[0][i]]=0 (즉, 첫번째 수와 i번째 수를 매칭시킴)

 그리고 나머지에 대해 이분매칭 



문제 바로가기 : boj.kr/1017


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.*

fun main(args: Array<String>) {
    val br= BufferedReader(InputStreamReader(System.`in`))
    val n=br.readLine()!!.toInt()
    val arr=br.readLine()!!.split(" ").map { it.toInt() }
    val chkPrimeNum=BooleanArray(2001){true}
    val adj=Array(n){ LinkedList<Int>() }
    val visit=BooleanArray(n)
    val B=IntArray(n){-1}

    chkPrimeNum[1]=false
    for(i in 2..2000){ // 에라토스테네스의 체로 2000까지
        var j=2
        if(chkPrimeNum[i]) {
            while (i * j <= 2000) {
                chkPrimeNum[i*j]=false
                j++
            }
        }
    }

    for(i in 0 until n){ //간선 연결
        for(j in 0 until n){
            if(i==j)    continue
            if(chkPrimeNum[arr[i]+arr[j]]){
                adj[i].add(j)
            }
        }
    }

    fun go(here:Int):Boolean{
        if(visit[here]) return false
        visit[here]=true
        for(there in adj[here]){
            if(B[there]==-1||go(B[there])){
                B[there]=here
                return true
            }
        }
        return false
    }

    val res=LinkedList<Int>()
    for(i in 0 until adj[0].size){
        B.fill(-1)
        B[adj[0][i]]=0
        var count=1
        for(j in 1 until n){
            visit.fill(false)
            visit[0]=true
            if(go(j))   count++
            else break
        }
        if(count==n) res.add(arr[adj[0][i]])
    }
    if(res.isEmpty())   println(-1)
    else{
        res.sort()
        println(res.joinToString(" "))
    }
}

cs









+ Recent posts