IT이론 2018. 3. 9. 18:12


재귀란?



static void dfsForRecursive(int v,String str) {

if(count==6) {

sb.append(str+"\n");

}else {

for(int i=v+1;i<K;i++) {

++count;

dfsForRecursive(i, str+iArr[i]+" ");

}

}

--count;

}


재귀란 위의 코드를 보듯이 자신의 메소드 안에서 자기자신을 호출하는 것을 말한다. 여기서 중요한 개념은 자신의 메소드에서 자기자신을 호출하게 되면 메모리 스택영역에 자기자신을 호출한 메소드가 복사되어서 들어가는 것이다.(맨 처음의 원본이 그대로 들어가는 것이 아니다.) 그 말은 즉, 자기자신을 호출할때 바뀌게 되는 매개변수의 값은 다시 같은 변수의 이름으로 들어가더라도 원본의 매개변수의 값에 영향을 주지 않는다.(메소드의 매개변수는 전역변수가 아니라 지역변수이기 때문이다.) 즉, 스택에 그대로 복사를 하게 되는 것이므로 원본에 영향을 미치지 않는 것이다. 본인도 이 개념이 확실히 잡히지 않아서 재귀, 백트랙킹 문제를 풀때 쉽지 않았기에 이 개념을 머리 속에 잘 담아두면 재귀호출을 이용하는 문제의 로직을 짤때 훨씬더 수월 할 것이다.

posted by 여성게
:

재귀를 이용한 하노이탑(백준1914번문제)




재귀를 이용한 하노이탑 문제풀이




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
package recursiveandbacktracking;
 
import java.math.BigInteger;
import java.util.Scanner;
 
public class Baekjun1914hanoiRecursive {
    static void hanoi(int N,char from,char aux,char to) {
        if(N==1) {
            System.out.println(from+" "+to);
        }else {
            hanoi(N-1,from,to,aux);
            System.out.println(from+" "+to);
            hanoi(N-1,aux,from,to);
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner sc=new Scanner(System.in);
        int N=sc.nextInt();
        BigInteger result=new BigInteger("1");
        if(N==1System.out.println(1);
        else {
            for(int i=0;i<N;i++) {
                result=result.multiply(new BigInteger("2"));
            }
            result=result.subtract(new BigInteger("1"));
            System.out.println(result);
        }
        if(N<=20) {
            hanoi(N,'1','2','3');
        }
    }
 
}
 
cs


여기서 문제는 N값이 20 이하 일때만 원반을 옮기는 과정을 출력하지만 20보다 클 경우에는 원반의 옮긴 수만 출력하면 된다. 여기서 옮긴 횟수를 함수 안에서 재귀호출 할때마다 카운트를 1씩 증가시키는 것으로 코드를 짜게되면 아마 시간 초과가 발생할 것이다.  왜냐면 조건에서의 가장 큰수 100을 입력하면 1267650600228229401496703205375라는 원반을 옮긴 횟수가 나오는데 보통 1억번에 1초정도 걸리는데 이것은 어마어마한 시간이 걸릴 것이므로 횟수는 카운트를 1씩 증가시키는 것이 아니라 점화식을 이용하여 횟수를 구한다.





즉, 원반을 옮기는 횟수는 2^N-1번이다.  N값이 100이라 가정했을 때는, 이 값은 long 데이터 타입도 수용하지 못할 값이므로 자바의 BigInteger 클래스를 이용해 횟수를 저장한다. 내부적으로 String을 가지므로 무한히 큰 값도 수용이 가능하다.


원반을 옮기는 과정의 함수를 설명하자면 예를들어 3개의 원반을 1번탑에서 3번탑으로 옮긴다면 hanoi(3,'1','2','3') 함수를 호출하게 된다. 그러면 함수 내부에서 재귀호출을 통해서 문제를 쪼개는 과정에 들어간다. 3개의 원반을 1번탑에서 3번탑으로 옮기는 것이라면 우선 더 작은 문제로 1번,2번 원반을 2번탑으로 옮긴후에 그다음 3번원반을 3번탑에 옮기고 그리고 1번,2번의 원반을 다시 2번탑에서 3번탑으로 옮기는 것 이렇게 문제를 작은 단위로 쪼개는 것이다. 이것이 총 3번의 과정으로 1번 n-1개의 원반을 보조탑(2번탑으로 옮기는과정)으로 옮기는 과정은 hanoi(2,from,to,aux) 함수에서 해결하고 그다음 3번원반을 1번탑에서 3번탑으로 옮기는 과정은 system.out.println(from+" "+to); 라는 출력문으로 그리고 마지막으로 보조탑에 있던 1번,2번 원반을 3번탑으로 옮기는 과정은 hanoi(2,aux,from,to) 함수에서 해결하게 된다. 그리고 각 함수 내에서 문제를 더 작은 단위로 쪼개고 쪼갠다. 예를 들어 1,2번 원반을 보조탑으로 옮기는 hanoi(2,from,to,aux) 함수에서 다시 문제를 1번원반을 보조탑인 3번탑으로 옮기는 과정(hanoi(1,1,2,3)과 2번원반을 2번탑으로 옮기는 과정(system.out.println(from+" "+to) 그리고 마지막으로 보조탑에 있던 1번원반을 목적탑인 2번탑으로 옮기는과정(hanoi(1,3,1,2))으로 마무리 되는 것이다. 

즉,

 hanoi(N-1,from,to,aux);

System.out.println(from+" "+to);

hanoi(N-1,aux,from,to);

이 과정이 n의 수가 1이 될때까지 계속 각 n값에 대하여 반복하게 되는 것이다.

posted by 여성게
:

재귀함수-백준1074번 Z문제






풀이-재귀호출을 이용한 알고리즘풀이



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
package recursiveandbacktracking;
 
import java.util.Arrays;
import java.util.Scanner;
 
public class Baekjun1074ZproblemRecursive {
    static long count;
    public static long zRecursive(long powNum,int r,int c) {
        if(powNum==2) { //2x2일 경우
            //1사분면일경우
            if(r%powNum==&& c%powNum==0) {
                return count;
            }
            //2사분면일경우 1사분면칸수를 더해준다
            else if(r%powNum==&& c%powNum==1) {
                return count+1;
            }
            //3사분면일경우 1,2사분면칸수를 더해준다
            else if(r%powNum==&& c%powNum==0) {
                return count+2;
            }
            //4사분면일경우 1,2,3사분면칸수를 더해준다.
            else {
                return count+3;
            }
        }else { //2x2가 아닐 경우 
            //1사분면일경우
            if((r%powNum<(powNum/2)) && (c%powNum<(powNum/2))) {
                return zRecursive(powNum/2, r, c);
            }
            //2사분면일경우 1사분면의 모든 칸수를 더해주며 재귀호출
            else if(r%powNum<(powNum/2&& (c%powNum>(powNum/2-1))) {
                count+=((powNum/2)*(powNum/2))*1;
                return zRecursive(powNum/2, r, c);
            }
            //3사분면일경우 1,2사분면의 모든 칸수를 더해주며 재귀호출
            else if((r%powNum>(powNum/2-1)) && (c%powNum<(powNum/2))) {
                count+=((powNum/2)*(powNum/2))*2;
                return zRecursive(powNum/2, r, c);
            }
            //4사분면일경우 1,2,3사분면의 모든 칸수를 더해주며 재귀호출
            else {
                count+=((powNum/2)*(powNum/2))*3;
                return zRecursive(powNum/2, r, c);
            }
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner sc=new Scanner(System.in);
        
        int N=sc.nextInt();
        int r=sc.nextInt();
        int c=sc.nextInt();
        long powNum=(long) Math.pow(2,N);
        System.out.println(zRecursive(powNum, r, c));
    }
}
 
cs

이 문제를 보면 딱 떠오르는 것이있다. Z방향으로 배열을 순회하는 것이다. 그 말은 즉슨 배열을 1사분면->2사분면->3사분면->4사분면 순서로 방문을 하는 것이다. 여기서 이용되는 재귀호출의 기능은 예를들어 8x8의 배열이라면 재귀호출을 통해 4x4배열로 줄여주는 역할을 한다.(이말을 재귀호출을 통해서 큰 문제를 작은 문제로 쪼개나가는 과정이라고 생각하면 된다.) 1/4씩 배열의 크기를 줄여가며 방문한 횟수를 카운트해주는 것이다. 예를 들어 8x8의 배열의 2사분면이라면? 8x8배열에서 이미 1사분면을 지나왔으므로 (64/4)*(해당분면-1=1)의 배열만큼 방문해서 2사분면을 온것이므로 방문횟수에 (64/4)*1만큼의 수를 증가시켜주고 8x8의 2사분면인 4x4배열로 재귀를 통해 문제의 크기를 줄여준다. 그 다음의 위치가 4x4의 배열에서 몇 사분면이냐라는 것을 다시 따져서 위처럼 횟수를 증가시켜준다. 그리고 배열을 줄여가다 마지막으로 2x2의 배열이 되었을때 1사분면일때는 횟수를 0, 2사분면은 횟수를 1, .....4사분면은 횟수를 3을 증가시켜주면된다.

posted by 여성게
:
IT이론 2018. 3. 8. 15:30

서비스 추상화란?


서비스 추상화란 Spring framework는 물론 객체지향 프로그래밍에서 아주 중요한 개념이다. 간단히 이야기하면 개발환경, 혹은 어떠한 비즈니스 로직을 위한 로우레벨의 기술에 구애 받지 않게 하기위해서, 그리고 책임을 분리 시키기 위한 추상화 개념이다. 예를 들어서 PlatformTransactionManager 같은 경우가 서비스 추상화의 대표적인 예이다. 트랜잭션을 관리한다는 것은 크게 보면 디비의 트랜잭션을 관리한다는 말이다. 그렇다면? 과연 디비라는 것은 종류가 하나인 것인가? 아니다. JDBC,하이버네이트 등등 아주 많은 디비의 종류가 있다. 그렇다면 각자의 디비의 트랜잭션을 관리하기 위해 각각다른 트랜잭션 코드가 필요하다면? 만약 디비가 바뀌게 된다면 그에 따라 트랜잭션 관리 코드 또한 바뀌어야 할것이다. 여기서 중요한 개념이 서비스 추상화 개념이다. 트랜잭션을 관리하기 위한 최상위 인터페이스인 PlatformTransactionManager를 선언하고 각 디비에 대한 transactionManager 클래스를 DI해주면? 트랜잭션 관련 코드는 통일되는 동시에 디비가 바뀐다면 DI설정 XML파일만 교체해주면 된다. 디비가 바뀌게 되더라도 트랜잭션 관련코드의 변경은 하나도 없는것이다. 이것이 서비스 추상화의 장점인 것이다.




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
public class UserDAOTest {
    @Autowired
    UserService userService;
    @Autowired
    PlatformTransactionManager transactionManager;
    
    //트랜잭션 경계설정 코드
    @Test(expected=DataAccessException.class)
    public void insertUserTest() {
        //트랜잭션의 시작을 알린다.(트랜잭션을 얻어오는 시점이 시작)
        TransactionStatus status=this.transactionManager.getTransaction(new DefaultTransactionDefinition());
        UserDTO user1=new UserDTO();
        UserDTO user2=new UserDTO();
        UserDTO user3=new UserDTO();
        try {
            user1.setId("test@test.com");
            user1.setPw("1111");
            user1.setNickName("tester");
            user2.setId("test1@test.com");
            user2.setPw("1111");
            user2.setNickName("tester1");
            user3.setId("test@test.com");
            user3.setPw("1111");
            user3.setNickName("tester2");
        
            userService.insertUser(user1);
            userService.insertUser(user2);
            userService.insertUser(user3);
            this.transactionManager.commit(status);
        }catch (RuntimeException e) {
            // TODO: handle exception
            this.transactionManager.rollback(status);
            throw e;
        }
        //UserDTO user4=userService.getUser(user1);
        //assertThat(user2.getId(),is(user1.getId()));
        //userService.deleteUser(user);
    }
cs

1
2
3
4
<!-- Transaction Manager -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
cs

위의 코드를 예로보면 디비가 바뀌게 된다면? 단순히 밑의 XML 설정에서 각 디비에 대한 트랜잭션매니저클래스로 설정만 바꿔주면 별도의 코드변경없이 트랜잭션 기능을 유지해 나갈수 있다.




posted by 여성게
:
Web/Spring 2018. 3. 7. 16:28

Spring Transaction(트랜잭션) 범위 설정하기



1.예외 상황(트랜잭션 범위설정이전)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void insertUserTest() {
        
        UserDTO user1=new UserDTO();
        UserDTO user2=new UserDTO();
        UserDTO user3=new UserDTO();
        
        user1.setId("test@test.com");
        user1.setPw("1111");
        user1.setNickName("tester");
        user2.setId("test1@test.com");
        user2.setPw("1111");
        user2.setNickName("tester1");
        user3.setId("test@test.com");
        user3.setPw("1111");
        user3.setNickName("tester2");
        
        userService.insertUser(user1);
        userService.insertUser(user2);
        userService.insertUser(user3);
        
    }
cs

예를 들어 이런 코드가 있다고 가정해보자. 여기서 보면 user3이라는 객체의 인스턴스변수를 설정해줄때, user3.setId() 메소드 부분에 user1과 같은 id가 들어가서 duplicationkey라는 예외가 발생하여 user3은 DB에 정상적으로 데이터가 삽입되지 않을 것이다. 여기서 transaction manager를 사용한다고 가정했을 때,



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <!-- Transaction Manager -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- txManager Advice -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>    
    </tx:advice>
    <!-- txManagerAdvice aop -->
    <aop:config>
        <aop:pointcut expression="execution(* com.web.nuri..*(..))" id="txPointCut"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
cs

위에 보이는 대로 transaction manager를 설정하면 user1, user2는 정상적으로 DB에 삽입되고 user3는 DB에 저장되지 않고 rollback 될 것이다. 즉, UserDAO(userService)의 메소드 단위로 트랜잭션이 적용되어 user3에서 예외가 발생하여도 user1,user2는 정상적으로 DB에 삽입이 되는 것이다. 본인은 트랜잭션의 범위의 설정을 메소드 단위가 아닌 내가 직접 범위를 설정하여 user3에서 예외가 발생하였다고 하면 이전의 user1,user2의 데이터도 DB에 삽입되지 않게 하고 싶은 것이다.



2.트랜잭션 범위 설정


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
public class UserDAOTest {
    @Autowired
    UserService userService;
    @Autowired
    PlatformTransactionManager transactionManager;
    
    //트랜잭션 경계설정 코드
    public void insertUserTest() {
        //트랜잭션의 시작을 알린다.(트랜잭션을 얻어오는 시점이 시작)
        TransactionStatus status=this.transactionManager.getTransaction(new DefaultTransactionDefinition());
        UserDTO user1=new UserDTO();
        UserDTO user2=new UserDTO();
        UserDTO user3=new UserDTO();
        try {
            user1.setId("test@test.com");
            user1.setPw("1111");
            user1.setNickName("tester");
            user2.setId("test1@test.com");
            user2.setPw("1111");
            user2.setNickName("tester1");
            user3.setId("test@test.com");
            user3.setPw("1111");
            user3.setNickName("tester2");
        
            userService.insertUser(user1);
            userService.insertUser(user2);
            userService.insertUser(user3);
            this.transactionManager.commit(status);
        }catch (RuntimeException e) {
            // TODO: handle exception
            this.transactionManager.rollback(status);
            throw e;
        }        
    }
}
cs



여기에 적용되는 것은 스프링의 트랜잭션 서비스 추상화 기법이다. PlatformTransactionManager(트랜잭션매니저의 최상위 인터페이스) 라는 인터페이스에 각자의 DB에 해당되는 TransactionManager 클래스(여기서는 mybatis를 이용하므로 DataSourceTransactionManager가 된다)를 의존주입 해준 후에 TransactionStatus 객체를 생성해준다.( 이시점이 트랜잭션의 시작시점이 되는 것이다.)

그리고 예외없이 메소드가 잘 수행되면 transactionManager.commit(status), 예외가 발생하였다면 transactionManager.rollback(status) 메소드를 수행시킨다. 이 시점이 트랜잭션 범위의 끝지점이 되는 것이다. 이렇게 범위를 설정하게 되면 user3 객체의 삽입에서 예외가 발생하면 user1,user2의 객체는 이전에 수행됬던 것들이 모두 rollback 된다.




posted by 여성게
:

이 문제같은 경우는 최소신장트리를 구하는 알고리즘이다. 최소신장트리를 구하는 알고리즘은 크게 프림알고리즘과 크루스칼알고리즘 두가지가 있다. 여기서 조금더 구현하기가 수월한 프림알고리즘을 이용하여 최소신장트리를 구현할것이다.





1. 최소신장 트리란?



최소 신장트리란 주어진 그래프에서 모든 정점을 연결하며 그 비용이 최소인 간선만 연결된 트리를 말한다.




2.프림 알고리즘(그림으로 설명하는 것이 편할 것같아 그림으로 ㅅ





 
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package graph;
 
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.PriorityQueue;
import java.util.Scanner;
 
class AdjVertex implements Comparable<AdjVertex> {
    int adjVertex;
    int weight;
    public AdjVertex(int adjVertex,int weight) {
        // TODO Auto-generated constructor stub
        this.adjVertex=adjVertex;
        this.weight=weight;
    }
    @Override
    public int compareTo(AdjVertex o) {
        // TODO Auto-generated method stub
        if(this.weight>o.getWeight()) return 1;
        else if(this.weight==o.getWeight()) return 0;
        else return -1;
    }
    public int getAdjVertex() {
        return adjVertex;
    }
    public void setAdjVertex(int adjVertex) {
        this.adjVertex = adjVertex;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}
public class Baekjun1197MinSpanningTree {
    
    static ArrayList<AdjVertex>[] vertexList;
    static boolean visited[];
    static int result;
    
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int V=sc.nextInt();
        int E=sc.nextInt();
        
        vertexList=new ArrayList[V+1];
        visited=new boolean[V+1];
        
        //인접리스트 초기화
        for(int i=1;i<V+1;i++) {
            vertexList[i]=new ArrayList<AdjVertex>();
        }
        for(int i=0;i<E;i++) {
            int start=sc.nextInt();
            int end=sc.nextInt();
            int weight=sc.nextInt();
            AdjVertex av=new AdjVertex(end, weight);
            AdjVertex av2=new AdjVertex(start, weight);
            vertexList[start].add(av);
            vertexList[end].add(av2);
        }
        //최소값을 빼주기 위한 우선순위 큐
        PriorityQueue<AdjVertex> pq=new PriorityQueue<AdjVertex>();
        visited[1]=true;
        Iterator<AdjVertex> it=vertexList[1].iterator();
        while(it.hasNext()) {
            pq.offer(it.next());
        }
        int count=0;
        while(!pq.isEmpty()) {
            AdjVertex pollVertex=pq.poll();
            int v=pollVertex.getAdjVertex();
            if(visited[v]==truecontinue;
            int w=pollVertex.getWeight();
            result+=w;
            visited[v]=true;
            count++;
            if(count==V+1break;
            Iterator<AdjVertex> it2=vertexList[v].iterator();
            while(it2.hasNext()) {
                pq.add(it2.next());
            }
        }
        System.out.println(result);
    }
}
 
cs


다익스트라 알고리즘과 비슷한 로직을 가집니다. 다만 큐에서 최소값을 빼주며 방문한 정점들을 체크를 해주어서 이미 체크된 정점 같은 경우는 결과값에 포함시키지않는다. 

posted by 여성게
:

이전에 게시했던 최단 거리문제가 코드가 동일하다. 단지 출력하는 부분의 코드만 살짝 수정해주면된다. 즉, 최소비용문제 또한 다익스트라 알고리즘으로 풀수 있다.






백준-1916번 최소비용 구하기




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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package graph;
 
import java.util.ArrayList;
import java.util.Iterator;
import java.util.PriorityQueue;
import java.util.Scanner;

class AdjVertex2 implements Comparable<AdjVertex2> {
    int adjVertex;
    int weight;
    public AdjVertex2(int adjVertex,int weight) {
        // TODO Auto-generated constructor stub
        this.adjVertex=adjVertex;
        this.weight=weight;
    }
    @Override
    public int compareTo(AdjVertex2 o) {
        // TODO Auto-generated method stub
        if(this.weight>o.getWeight()) return 1;
        else if(this.weight==o.getWeight()) return 0;
        else return -1;
    }
    public int getAdjVertex() {
        return adjVertex;
    }
    public void setAdjVertex(int adjVertex) {
        this.adjVertex = adjVertex;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}
public class Baekjun1916UpdateCode {
    static ArrayList<AdjVertex2>[] vertexList;
    static int[] distance;
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        PriorityQueue<AdjVertex2> pq=new PriorityQueue<AdjVertex2>();
        
        int V=sc.nextInt();
        int E=sc.nextInt();
        
        vertexList=new ArrayList[V+1];
        distance=new int[V+1];
        for(int i=1;i<V+1;i++) {
            vertexList[i]=new ArrayList<AdjVertex2>();
            distance[i]=Integer.MAX_VALUE;
        }
        
        for(int i=0;i<E;i++) {
            int u=sc.nextInt();
            int v=sc.nextInt();
            int w=sc.nextInt();
            AdjVertex2 adVertex=new AdjVertex2(v,w);
            vertexList[u].add(adVertex);
        }
        int K=sc.nextInt();
        int M=sc.nextInt();
        distance[K]=0;
        pq.offer(new AdjVertex2(K,distance[K]));
        while(!pq.isEmpty()) {
            AdjVertex2 vertexInfo=pq.poll();
            int index=vertexInfo.getAdjVertex();
            int weight=vertexInfo.getWeight();
            Iterator<AdjVertex2> it=vertexList[index].iterator();
            while(it.hasNext()) {
                AdjVertex2 adVertex=it.next();
                int index1=adVertex.getAdjVertex();
                int weight1=adVertex.getWeight();
                    
                if(distance[index1]>distance[index]+weight1) {
                distance[index1]=distance[index]+weight1;
                pq.offer(adVertex);
                }
            }
        }
        System.out.println(distance[M]);
    }
}
 
cs


출발도시에서 도착도시까지의 최소비용을 구하는 것이므로 출력부분을 도착도시의 거리를 출력해주면된다. 왜냐하면 다익스트라 알고리즘은 시작정점으로부터 모든 정점까지의 최소비용들이 모두 distance배열에 들어가기때문에 단순히 앞의 최단경로 문제 코드에서 출력부분만 수정해주면 된다. 다익스트라 알고리즘 구동방식은 최단거리문제 설명에 그림으로 설명되어있다.

posted by 여성게
:


백준-1753번 최단거리



시작정점으로부터 모든 정점까지의 최단거리를 구하는 문제이다. 이 문제는 BFS와 비슷한 다익스트라 알고리즘으로 풀 수 있는 문제이다. 다익스트라 알고리즘은 방향이있는 가중치 그래프에서 어떤 정점에서 다른 정점까지의 최단 거리를 구할 수 있는 알고리즘이다.(음수간선을 포함하는 그래프는 다익스트라 알고리즘으로 풀 수 없다.)





위의 그림과 같은 과정으로 다익스트라 알고리즘이 구동됩니다. ( 코드에서는 방문여부에 관한 check배열은 선언하지 않았음 굳이 없어도 돌아감)




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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
 
import java.util.ArrayList;
import java.util.Iterator;
import java.util.PriorityQueue;
import java.util.Scanner;
 
/*
4 5
1 2 1
4 2 -2
3 2 -8
3 4 -3
4 1 -88
*/
class AdjVertex1 implements Comparable<AdjVertex1> {
    int adjVertex;
    int weight;
    public AdjVertex1(int adjVertex,int weight) {
        // TODO Auto-generated constructor stub
        this.adjVertex=adjVertex;
        this.weight=weight;
    }
//우선순위 큐에 삽입될때 정렬할 기준을 가준치로 정해주는 것이다.
    @Override
    public int compareTo(AdjVertex1 o) {
        // TODO Auto-generated method stub
        if(this.weight>o.getWeight()) return 1;
        else if(this.weight==o.getWeight()) return 0;
        else return -1;
    }
    public int getAdjVertex() {
        return adjVertex;
    }
    public void setAdjVertex(int adjVertex) {
        this.adjVertex = adjVertex;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}
public class Baekjun1753UpdateCode {
    static ArrayList<AdjVertex1>[] vertexList;
    static int[] distance;
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        PriorityQueue<AdjVertex1> pq=new PriorityQueue<AdjVertex1>();
        
        int V=sc.nextInt();
        int E=sc.nextInt();
        int K=sc.nextInt();
        
        vertexList=new ArrayList[V+1];
        distance=new int[V+1];
//인접리스트 초기화 및 거리 배열 초기화(거리배열은 무한대값 개념으로 초기화해준다.)
        for(int i=1;i<V+1;i++) {
            vertexList[i]=new ArrayList<AdjVertex1>();
            distance[i]=Integer.MAX_VALUE;
        }
//시작정점의 거리는 0으로 초기화해준다.(자기자신으로 들어가는 거리는 0임으로)
        distance[K]=0;
        for(int i=0;i<E;i++) {
            int u=sc.nextInt();
            int v=sc.nextInt();
            int w=sc.nextInt();
            AdjVertex1 adVertex=new AdjVertex1(v,w);
            vertexList[u].add(adVertex);
        }
//시작정점 K에서 자기자신 K로 들어가는 거리는 0이다. 라는 객체를 우선순위큐에 삽입
        pq.offer(new AdjVertex1(K,distance[K]));
        while(!pq.isEmpty()) {
//가중치가 가장 작은 정점과 가중치 쌍을 poll해준다.(기준정점이 삭제된 이후의 큐는 방문하지 않은 정점중
//가중치가 가장 작은 정점을 poll한다.)
            AdjVertex1 vertexInfo=pq.poll();
            int index=vertexInfo.getAdjVertex();
            int weight=vertexInfo.getWeight();
//우선순위 큐에서 삭제한 정점의 인접한 정점들을 구한다.
            Iterator<AdjVertex1> it=vertexList[index].iterator();
            while(it.hasNext()) {
                AdjVertex1 adVertex=it.next();
                int index1=adVertex.getAdjVertex();
                int weight1=adVertex.getWeight();
//거리가 무한대이거나 OR 이전에 갱신된 거리보다 지금 방문한 거리가 작다면 거리를 다시 갱신해준다.
                if(distance[index1]>distance[index]+weight1) { distance[index1]=distance[index]+weight1; pq.offer(adVertex); } } } for(int i=1;i<V+1;i++) { if(distance[i]<Integer.MAX_VALUE) System.out.println(distance[i]); else System.out.println("INF"); } } }


BFS와 비슷하게 큐를 이용하지만 그냥 큐가 아니라 우선순위 큐를 이용하여 구현한다. 

posted by 여성게
: