스프링 AOP(Aspect Oriented Programming)

AOP

AOP는 Asepect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다.

관점 지향은 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고

부가적인 관점을 각각 따로 분리해서 모듈화하겠다는 것이다.

예로들어 핵심적인 관점은 우리가 적용하고자 하는 핵심 비지니스 로직이 된다.

또한 부가적인 관점은 핵심 로직외에 행해지는 데이터베이스 연결, 로깅, 파일 입출력등이 있다.

AOP에서 각 관점을 기준으로 로직을 모듈화한다는 것은 코드를 부분적으로 나누어서 모듈화하겠다는 의미다.

이때, 소스 코드상에서 핵심 비지니스 로직(Core Concerns)외에 계속 반복해서 쓰는 코드들을 발견할 수 있는데 이것을 흩어진 관심사(Crosscutting Concerns)라 부른다.

스크린샷 2020-11-27 오후 12 59 42

위와 같이 흩어진 관심사(Crosscutting Concerns)를 Aspect로 모듈화하고 핵심적인 비지니스 로직(Core concerns)에서 분리하여 재사용하겠다는 것이 AOP이다.

AOP 관련 용어

  • 조인 포인트(JoinPoint) : 클라이언트가 호출하는 모든 비지니스 메소드, 조인포인트 중에서 포인트컷이 되기때문에 포인트 컷의 후보로 생각할 수 있다.
  • 포인트 컷(Pointcut) : 특정 조건에 의해 필터링된 조인 포인트, 수 많은 조인 포인트 중에 특정 메소드에서만 Advice를 수행시키기 위해서 사용된다
  • 어드바이스(Advice) : Crosscutting Concern에 해당하는 공통 기능의 코드
  • 애스펙트(Aspect) : 포인트 컷과 어드바이스의 결합이다. 어떤 포인트 컷 메소드에 대해 어떤 어드바이스 메소드가 실행할 지 결정한다.
  • 위빙(Weaving) : 핵심 비지니스 로직과 Aspect를 연결하여 Advised Object를 만드는 과정
    1. complie time
    2. load time
    3. runtime

AOP 구현 방법

  1. 컴파일

    • A.java —— (AOP) —–> A.class
    • 컴파일을 할 때 중간에 공통 로직을 끼워 넣는다
    • 컴파일하기 전 코드에는 해당 로직이 없지만, 컴파일이 완료되면 해당 로직을 가지고
      있는 상태이다
    • AspectJ를 이용해서 만들어진다
  2. 바이트코드 조작

    • A.java —-> A.class —-> (AOP) —-> 메모리
    • load time에서 ClassLoader가 A.class를 읽어온다
    • 읽어와서 메모리에 올릴 때 공통 로직이 들어가도록 조작한다
    • AspectJ를 이용해서 만들어진다
  3. 프록시 패턴

    • Runtime에서 구현하는 방식
    • 프록시 패턴을 적용하여 실제 객체가 실행되는 것이 아닌 실제 객체를 감싸고 있는 프록시 객체를 Runtime에 생성
    • 프록시 객체는 Aspect가 적용되어있고 Core Concern이 들어있는 실제 객체를 reference하고 있다
    • AOP가 적용된 객체를 요청할 때 실제 객체가 아닌 프록시 객체가 실행된다.
    • AspectJ로 만들어져 있으며 SPRING AOP는 이 방식으로 구현되어있다.

Spring AOP

스프링 AOP 특징

  • 프록시 기반의 AOP 구현체
  • 스프링 빈에만 AOP를 적용할 수 있다
  • 모든 AOP 기능을 제공하는 것이 목적이 아니라, Spring IoC와 연동하여 어플리케이션의 문제에 대한 해결책을 제공하는 것이 목적

프록시 패턴

  • 접근 제어 또는 부가기능 추가

    image

    • 인터페이스에 대해서 핵심 비지니스 로직만으로 구성되어진 실제 객체를 만든다
    • 인터페이스에 대해서 또 하나의 Proxy 구현체를 만든다
    • 이 구현체에는 부가기능이 추가있으며 실제 객체를 참조하여 실제 객체의 비지니스 로직을 실행 시킬 수 있다
    • 클라이언트는 객체를 인터페이스를 통해서 접근하게 되며, 실제객체가 직접 실행되는 것이 아닌 Proxy객체를 통해서 부가기능을 수행하고 간접적으로 실행된다.

프록시 패턴을 적용 (Spring AOP를 사용하지 않음)

  • EventService Interface

    1
    2
    3
    4
    5
    6
    7
    public interface EventService {
    void createEvent();

    void publishEvent();

    void deleteEvent();
    }
  • EventService 구현체 (핵심 비지니스 로직 수행)

    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
    @Service
    public class SimpleEventService implements EventService{

    @Override
    public void createEvent() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("Created an event");
    }

    @Override
    public void publishEvent() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("Publish an event");
    }

    public void deleteEvent() {
    System.out.println("Delete an event");
    }
    }
  • EventService의 Proxy 구현체 (부가기능 + 핵심 로직 객체 참조)

    @Primary로 SimpleEventService가 아닌 ProxySimpleEventService이 실행됨

    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
    @Primary
    @Service
    public class ProxySimpleEventService implements EventService{

    @Autowired
    SimpleEventService simpleEventService;

    @Override
    public void createEvent() {
    long begin = System.currentTimeMillis();
    simpleEventService.createEvent();
    System.out.println(System.currentTimeMillis() - begin);
    }

    @Override
    public void publishEvent() {
    long begin = System.currentTimeMillis();
    simpleEventService.publishEvent();
    System.out.println(System.currentTimeMillis() - begin);
    }

    @Override
    public void deleteEvent() {
    simpleEventService.deleteEvent();
    }
    }

문제점

  • 매번 프록시 패턴을 적용해서 프록시 클래스를 작성해야한다
  • 여러 클래스 여러 메소드들에 적용할려면 반복적인 작업이 증가한다
  • 객체들관의 관계도 복잡하다

스프링 AOP 등장

  • 스프링 IoC컨테이너가 제공하는 기반 시설과 Dynamic 프록시를 사용하여 여러 복잡한 문제 해결
  • 동적 프록시 : 동적으로 프록시 객체를 생성하는 방법
    • 자바가 제공하는 방법은 인터페이스 기반 프록시 생성
    • CGlib은 클래스 기반 프록시도 지원
  • 스프링 IoC: 기존 빈을 대체하는 동적 프록시 빈을 만들어 등록해준다
    • 클라이언트 코드에 변경이 없다

스프링 AOP 적용해서 개선

스프링 AOP는 Spring Container에 등록된 Bean들만 적용된다

  • Pointcut을 Annotation 기반으로 작성

    1
    2
    3
    4
    5
    @Documented
    @Retention(RetentionPolicy.CLASS)
    @Target(ElementType.METHOD)
    public @interface PerfLogging {
    }
  • Aspect 작성 (Around 방식을 적용한 Advice 메소드 작성)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Component
    @Aspect
    public class PerfAspect {

    @Around("@annotation(PerfLogging)")
    public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
    long begin = System.currentTimeMillis();
    Object retVal = pjp.proceed();
    System.out.println(System.currentTimeMillis() - begin);
    return retVal;
    }

    @Before("bean(simpleEventService)")
    public void hello(){
    System.out.println("hello");
    }
    }
    • Around방식은 핵심 비지니스로직 실행 권한을 ProceedingJoinPoint로 받아온다 (Spring Container가 받아옴)
    • 받아온 객체를 실행하기 전 후에 작업을 진행한다
    • 받아온 객체가 return값을 가지고 있으면 Advice메소드에서 그 값을 return해 주어야한다
  • AOP를 적용할 Pointcut메소드를 지정한다(메소드 위에 어노테이션 설정)

    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
    @Service
    public class SimpleEventService implements EventService{
    @PerfLogging
    @Override
    public void createEvent() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("Created an event");
    }

    @PerfLogging
    @Override
    public void publishEvent() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("Publish an event");
    }

    @Override
    public void deleteEvent() {
    System.out.println("Delete an event");
    }
    }

Pointcut 정의 방식

  1. execution 설정 방식

    • 사용 예

      1
      @Around("execution(* com.example..*.EventService.*(..))")
    • 적용 규칙

      스크린샷 2020-11-27 오후 2 51 06
  2. bean 설정 방식

    • 사용 예

      적용할 빈 이름을 직접 입력

      1
      @Around("bean(simpleEventService)")
  3. annotation 설정 방식 (위에서 설명)

Advice 실행 시점

  1. Before

    • 핵심 비지니스 로직 실행 전에 실행
    • 실행 예
      1
      2
      3
      4
      5
      6
      7
      @Before("execution(* com.rubypaper.biz..*Impl.*(..))")
      public void printLog(JoinPoint jp) {
      String method = jp.getSignature().getName(); // 클라이언트가 호출한 메소드 이름
      Object[] args = jp.getArgs(); // 클라이언트가 전달한 인자 정보

      System.out.println("<사전 처리> " + method + "비지니스 로직 수행 전 동작" + "() 메소드 ARGS 정보 : " + args[0].toString());
      }
  2. After

    • 핵심 비지니스 로직 실행 후에 실행
    • 실행 예
      1
      2
      3
      4
      5
      6
      7
      @After("execution(* com.rubypaper.biz..*Impl.*(..))")
      public void printLog(JoinPoint jp) {
      String method = jp.getSignature().getName(); // 클라이언트가 호출한 메소드 이름
      Object[] args = jp.getArgs(); // 클라이언트가 전달한 인자 정보

      System.out.println("<사후 처리> " + method + "비지니스 로직 수행 후 동작" + "() 메소드 ARGS 정보 : " + args[0].toString());
      }
  3. After Returning

    • 핵심 비지니스 로직 실행한 후 반환된 객체를 Advice에서 받아와서 실행
    • 실행 예
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @AfterReturning(pointcut = "!void com.rubypaper.biz..*Impl.*(..))", returning = "returnObj")
      public void afterLog(Object returnObj) {
      System.out.println("<사후 처리> 비지니스 로직 리턴 값 : " + returnObj.toString());

      if (returnObj instanceof UserVO) {
      UserVO user = (UserVO) returnObj;
      if(user.getRole().equals("ADMIN")) {
      System.out.println(user.getName() + "님은 관리자 화면으로 바로 이동........");
      }
      }
      }
    • 핵심 비지니스 로직에서 반환된 객체를 returnObj로 받아온다
  4. After Throwing

    • 핵심 비지니스 로직 실행헌 후 예외가 발생하면 예외객체를 받아와서 실행
    • 실행 예
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
       @AfterThrowing(pointcut = "execution(* com.rubypaper.biz..*Impl.*(..))", throwing = "exceptionObj")
      public void exceptionLog(JoinPoint jp, Exception exceptionObj) {
      String method = jp.getSignature().getName(); // 클라이언트가 호출한 메소드 이름

      System.out.println("[ 예외처리 ]"+ method +" 메소드 수행 중 예외 발생");

      if(exceptionObj instanceof IllegalArgumentException){
      System.out.println("0 번 게시 글을 등록할 수 없습니다");
      } else if (exceptionObj instanceof ArithmeticException) {
      System.out.println("0 으로 숫자를 나눌 수 없습니다");
      } else if (exceptionObj instanceof SQLException) {
      System.out.println("SQL 구문에 오류가 있습니다");
      } else {
      System.out.println("문제 발생 !! 시스템을 잠시 종료합니다");
      }
      }
      • 예외객체를 exceptionObj로 받아온다
  5. Around

    • 위에서 설명

Advice에서 Pointcut에 해당되는 메소드에 대한 정보를 얻기 위해 JoinPoint라는 객체를 받아올 수 있다. Around만 예외적으로 ProceedJoinPoint라는 객체를 통해서 받아온다

Comment and share


백준 1759 : 문제링크

  • 문제유형 :

    • 백 트레킹
  • 설명

    • C개의 문자들중에서 L개를 선택하는 모든 조합을 고려한다
    • 모음의 개수와 자음의 개수 조건을 확인한다
  • 풀이

    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 copy

    result = []
    string = []

    def combination(array, length, index):
    if len(string) == length:
    result.append(copy.deepcopy(string))
    return
    for i in range(index, len(array)):
    string.append(array[i])
    combination(array, length, index + 1)
    string.pop()

    vowels = ('a', 'e', 'i', 'o', 'u')
    l, c = map(int, input().split(' '))

    array = input().split(' ')
    array.sort()

    combination(array, l, 0)

    for password in result:
    count = 0:
    for i in password:
    if i in vowels:
    count += 1

    if count >= 1 and count <= l - 2:
    print(''.join(password))

Comment and share


백준 1987 : 문제링크

  • 문제유형 :

    • 백 트레킹
  • 설명

    • 말을 상하좌우 네 가지 방향으로 이동시킨다
    • 지금까지 지나온 모든 칸에 적힌 알파벳과 다른 알파벳이 적인 칸으로 이동
    • 백 트레킹을 이용하여 다른 알파벳이 적힌 칸으로 이동하도록 한다
  • 풀이

    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
    dx = [-1, 1, 0, 0]
    dy = [0, 0, 1, -1]

    def bfs(x, y):
    global result
    q = set()
    q.add((x, y, array[x][y]))

    while q:
    x, y, step = q.pop()
    result = max(result, len(step))

    for i in range(4):
    nx = x + dx[i]
    ny = y + dy[i]

    if (0 <= nx and nx < r and 0 <= ny and ny < c
    and array[nx][ny] not in step)
    q.add(nx, ny, step + array[nx][ny])

    r, c = map(int, input().split())
    array = []
    for _ in range(r):
    array.append(input())

    result = 0
    bfs(0, 0)
    print(result)

Comment and share


백준 9663 : 문제링크

  • 문제유형 :

    • 백 트레킹
  • 설명

    • DFS를 이용하여 백트래킹 알고리즘을 구현한다
    • 각 행을 차례대로 확인하면서, 각 열에 퀸을 놓는 경우를 고려한다
    • 이 때 위쪽 행을 모두 확인하며, 현재 위치에 놓을 수 있는지 확인한다
  • 풀이

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    def check(x):
    for i in range(x):
    if row[x] == row[i]:
    return False
    if abs(row[x] - row[i]) == x - i:
    return False
    return True

    def dfs(x):
    global result
    if x == n:
    result += 1
    else:
    for i in range(n):
    row[x] = i
    if check(x):
    dfs(x+1)

    n = int(input())
    row = [0] * n
    result = 0
    dfs(0)
    print(result)

Comment and share


백준 1781 : 문제링크

  • 문제유형 :

    • 탐욕 알고리즘
  • 설명

    • 정렬 및 우선순위 큐를 이용하여 O(NlogN)의 시간에 해결할 수 있다
    • 데드라인 기준으로 오름차순 정렬을 수행한다
    • 각 문제의 컵라면 수를 우선순위 큐에 넣으면서, 데드라이을 초과할 결우 최소원소를 제거한다
  • 풀이

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import heapq

    n = int(input())
    array = []
    q = []

    for i in range(n):
    a, b = map(int, input().split(' '))
    array.append((a, b))
    array.sort()

    for i in array:
    a = i[0]
    heapq.heappush(q, i[1])
    if a < len(q):
    heapq.heappop(q)

Comment and share


백준 1461 : 문제링크

  • 문제유형 :

    • 탐욕 알고리즘
  • 설명

    • 일직선상에 책들을 순서대로 위치시킨다
    • 0보다 큰 책들과 0보다 작은 책들을 나누어서 처리한다
    • 음수와 양수 좌표를 위해 두 개의 우선순위 큐를 이용한다
    • 마지막 책을 놓을 때는 다시 0으로 돌아올 필요가 없기 때문에, 가장 먼 책을 마지막으로 놓는다
  • 풀이

    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 heapq

    n, m = map(int, input().split(' '))
    array = list(map(int, input().split(' ')))
    positive = []
    negative = []

    largest = max(max(array), - min(array))

    for i in array:
    if i > 0:
    heapq.heappush(positive, -i)
    else:
    heapq.heappush(negative, i)

    result = 0

    while positive:
    result += heapq.heappop(positive)
    for _ in range(m - 1):
    if positive:
    heapq.heappop(positive)

    while negative:
    result += heapq.heappop(negative)
    for _ in range(m - 1):
    if negative:
    heapq.heappop(negative)

    print(-result * 2 - largest)

Comment and share


백준 1092 : 문제링크

  • 문제유형 :

    • 탐욕 알고리즘
  • 설명

    • 정렬만 수행하면 되므로 시간복잡도는 O(NlogN)
    • 각 센서를 오름차순으로 정렬한다
    • 각 센서 사이의 거리를 계산한다
    • 가장 거리가 먼 순서대로 k - 1개의 연결 고리르 제거한다
  • 풀이

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import sys

    n = int(input())
    k = int(input())

    if k >= n:
    print(0)
    sys.exit()

    array = list(map(int, input().split(' ')))
    array.sort()

    distance = []
    for i in range(1, n):
    distances.append(array[i] - array[i - 1])
    distance.sort(reverse=True)

    for i in range(k - 1):
    distances[i] = 0
    print(sum(distances))

Comment and share


백준 1092 : 문제링크

  • 문제유형 :

    • 탐욕 알고리즘
  • 설명

    • 크레인과 박스를 무게 기준으로 내림차순 정렬한다
    • 무거운 박스부터 크레인이 옮기도록한다
    • 크레인의 수 N, 박스의 수 M일때 시간복잡도는 O(NM)이다
  • 풀이

    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
    import sys

    n = int(input())
    cranes = list(map(int, input().split()))

    m = int(input())
    boxes = list(map(int, input().split()))

    if max(cranes) < max(boxes):
    print(-1)
    sys.exit()

    positions = [0] * n
    checked = [False] * m

    cranes.sort(reverse=True)
    boxes.sort(reverse=True)

    result = 0
    count = 0

    while True:
    if count == len(boxes):
    break
    for i in range(n):
    while position[i] < len(boxes):
    if not checked[position[i]] and cranes[i] >= boxes[position[i]]:
    checked[position[i]] = True
    position[i] += 1
    count += 1
    break
    position[i] += 1
    result += 1

    print(result)

Comment and share


백준 5719 : 문제링크

  • 문제유형 :

    • 그래프
    • 크루스칼 알고리즘
  • 설명

    • 정점의 개수 N이 최대 1000이므로, 가능한 통로의 개수는 약 N^2이다
    • 크루스칼 알고리즘은 간선의 개수가 E일 때 O(ElogE)로 동작하므로 해결할 수 있다 (정렬 알고리즘 시간복잡도)
  • 풀이

    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
    61
    62
    import math
    import sys

    input = sys.stdin.readline

    def get_distance(p1, p2):
    a = p1[0] - p2[0]
    b = p1[1] - p2[1]
    return math.sqrt((a * a) + (b * b))

    def get_parent(parent, n):
    if parent[n] == n:
    return n
    return get_parent(parent, parent[n])

    def union_parent(parent, a, b):
    a = get_parent(parent, a)
    b = get_parent(parent, b)
    if a < b:
    parent[b] = a
    else:
    parent[a] = b

    def find_parent(parent, a, b):
    a = get_parent(parent, a)
    b = get_parent(parent, b)
    if a == b:
    return True
    else:
    return False

    edges = []
    parent = {}
    locations = []
    n, m = map(int, input().split())

    for _ in range(n):
    x, y = map(int, input().split())
    locations.append((x, y))

    length = len(locations)

    for i in range(length - 1)
    for j in range(i + 1, length):
    edges.append((i + 1, j + 1), get_distance(location[i], location[j]))

    for i in range(1, n + 1):
    parent[i] = i

    for i in range(m):
    a, b = map(int, input().split())
    union_parent(parent, a, b)

    edges.sort(key=lambda data: data[2])

    result = 0
    for a, b, cost in edges:
    if not find_parent(parent, a, b):
    union_parent(parent, a, b)
    result += cost

    print("%0.2f" %result)

Comment and share


백준 5719 : 문제링크

  • 문제유형 :

    • 그래프
    • 다익스트라 최단 경로 알고리즘
  • 설명

    • 다익스트라 최단 경로에 포함되는 모든 간선을 추적한다
    • 최단 경로에 포함되는 모든 간선을 추적할 때 DFS를 사용하여 추적하여 제외시킨다
    • 제외된 간선 외에서 다익스트라 최단 경로 알고리즘으로 차선 최단 경로를 찾는다
  • 풀이

    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
    from collections import deque
    import heapq
    import sys

    input = sys.stdin.readline

    def dijkstra():
    heap_data = []
    heapq.heappush(heap_data, (0, start))
    distance[start] = 0
    while heap_data:
    dist, now = heapq.heappop(heap_data)
    if distance[now] < dist:
    continue
    for i in adj[now]:
    cost = dist + i[1]
    if distance[i[0]] > cost and not dropped[now][i[0]]:
    distance[i[0]] = cost
    heapq.heappush(heap_data, (cost, i[0]))

    def bfs():
    q = deque()
    q.append(end)
    while q:
    now = q.popleft()
    if now == start:
    continue
    for prev, cost in reverse_adj[now]:
    if distance[now] == distance[prev] + cost
    dropped[prev][now] = True
    q.append(prev)

    while True:
    n, m = map(int, input().split())
    if n == 0:
    break
    start, end = map(int, input().split())
    adj = [[] for _ in range(n + 1)]
    reverse_adj = [[] for _ in range(n + 1)]
    for _ in range(m):
    x, y, cost = map(int, input().split())
    adj[x].append((y, cost))
    reverse_adj[y].append((x, cost))
    dropped = [[False] * (n + 1) for _ in range(n + 1)]
    distance = [1e9] * (n + 1)
    dijkstra()
    bfs()
    distance = [1e9] * (n + 1)
    dijkstra()
    if distance[end] != 1e9:
    print(distance[end])
    else:
    print(-1)

Comment and share

Yechan Kim

author.bio


author.job