ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준 14867] 물통
    C 프로그래밍/BOJ 2022. 9. 20. 21:59
    728x90

    https://www.acmicpc.net/problem/14867

     

    14867번: 물통

    표준 입력으로 물통 A의 용량을 나타내는 정수 a(1 ≤ a < 100,000), 물통 B의 용량을 나타내는 정수 b(a < b ≤ 100,000), 최종 상태에서 물통 A에 남겨야 하는 물의 용량을 나타내는 정수 c(0 ≤ c ≤ a), 최

    www.acmicpc.net

    #include <cstdio>
    #include <queue>
    #include <set>
    int a, b, c, d;
    
    using namespace std;
    
    struct _st
    {
    	int x;
    	int y;
    	int cnt;
    };
    
    queue<_st> Q;		// 상태 저장용 큐
    	
    set<pair<int, int>> S;	// set으로 방문 체크를 대신한다
    
    
    void Fill_A(int x, int y, int cnt)
    {
    	// Fill A
    	if (S.find({ a, y }) != S.end()) return;
    	Q.push({ a, y , cnt + 1});
    	S.insert({ a, y });
    }
    
    void Fill_B(int x, int y, int cnt)
    {
    	// Fill B
    	if (S.find({ x, b }) != S.end()) return;
    	Q.push({ x, b , cnt + 1});
    	S.insert({ x, b });
    }
    
    
    void Empty_A(int x, int y, int cnt)
    {
    	// Empty A
    	if (S.find({ 0, y }) != S.end()) return;
    	Q.push({ 0, y, cnt + 1 });
    	S.insert({ 0, y });
    }
    
    void Empty_B(int x, int y, int cnt)
    {
    	// Empty B
    	if (S.find({ x, 0 }) != S.end()) return;
    	Q.push({ x, 0, cnt + 1 });
    	S.insert({ x, 0 });
    }
    
    
    void Move_AtoB(int x, int y, int cnt)
    {
    	// A -> B
    	if (x <= b - y && S.find({ 0, y + x }) == S.end()) {
    		Q.push({ 0, y + x, cnt + 1});
    		S.insert({ 0, y + x });
    	}
    	else if (x > b - y && S.find({ x - (b - y), b }) == S.end()) {
    		Q.push({ x - (b - y), b, cnt + 1});
    		S.insert({ x - (b - y), b });
    	}
    }
    
    
    
    void Move_BtoA(int x, int y, int cnt)
    {
    	// B -> A
    	if (y <= a - x && S.find({ x + y, 0 }) == S.end()) {
    		Q.push({ x + y, 0, cnt + 1});
    		S.insert({ x + y , 0 });
    	}
    	else if (y > a - x && S.find({ a, y - (a - x) }) == S.end()) {
    		Q.push({ a, y - (a - x), cnt + 1});
    		S.insert({ a, y - (a - x) });
    	}
    }
    
    
    
    int BFS(void)
    {	
    	Q.push({ 0, 0, 0 });	// 큐에 초기 상태 삽입
    	S.insert({ 0, 0 });	// 집합에 초기 상태 삽입 
    
    	while (!Q.empty()) {
    		_st data = Q.front();
    		Q.pop();
    
    		if (data.x == c && data.y == d) return data.cnt;
    
    		Fill_A(data.x, data.y, data.cnt);
    		Fill_B(data.x, data.y, data.cnt);
    		Empty_A(data.x, data.y, data.cnt);
    		Empty_B(data.x, data.y, data.cnt);
    		Move_AtoB(data.x, data.y, data.cnt);
    		Move_BtoA(data.x, data.y, data.cnt);
    	
    	}
    	return -1;
    }
    
    
    
    int main()
    {
    	scanf("%d %d %d %d", &a, &b, &c, &d);	// (a, b) 용량 (c, d) 목적 상태
    	int ans = BFS();
    	printf("%d", ans);
    	return 0;
    }

    사실 로직은 빨리 나왔는데 2차원 방문배열을 만들어서 A 물통에 남아있는 물의 양/ B 물통에 남아있는 물의 양을 기준으로 작업횟수를 기록하려고 하니, 메모리 제한 때문에 chk[100000 + 2][100000 + 2] 배열이 생성되지 않았다.

    그래서 원소의 중복 저장이 불가한 set을 사용하여, 해당 pair가 집합의 원소라면 무시하고 집합의 원소가 아니라면 삽입하여 상태 발전(작업 진행, 작업 횟수 증가)을 하도록 코드를 구현했다. 

     

     

    // 컨테이너 선언

    구조체 _st 는 A 물통에 남아있는 물의 양, B 물통에 남아있는 물의 양, 작업 횟수를 기록하기 위함이다. 이러한 타입의 queue를 만들어 상태발전을 위해 사용하였다. 

    set S는 int의 pair 타입으로 생성해 주었다. 사실 set도 구조체로 만들고 싶었지만,,, 뭐 커스텀 함수 만들고 그래야 한대서,,, 작업횟수는 그냥 큐에 하기로.

     

     

    // BFS()

    사실 이 문제는 컨테이너 선언이 전부이다. 다른건 일반 BFS 풀이처럼 진행하면 된다. 

    한 가지 다른 점은 기존에는 chk 배열을 확인하면서 현재 진행된 상태의 작업횟수가 기존 배열에 저장된 수 보다 작아야만 갱신을 해줬다. 그런데 여기서는 방문 표시를 위해 set을 사용한 만큼 A와 B에 남아있는 양(pair)이 set의 원소로 존재한다면, 현재 그 상태가 만들어지기까지의 작업상태보다 훨씬 더 이전에 해당 상태가 되어 set에 담겼다고 이해할 수 있다. 따라서 이 경우 각 상태발전 함수를 바로 리턴해준다. 

    한편 현재의 상태가 set의 원소로 존재하지 않으면 이전에 그 상태가 한번도 만들어지지 않았다는 것이므로, 현재 작업횟수가 최선의 횟수가 된다. 따라서 이를 Q와 S에 각각 삽입해준다. 

     

     

    728x90

    'C 프로그래밍 > BOJ' 카테고리의 다른 글

    [자료구조] TREE  (0) 2022.09.21
    [C++ STL] LIST  (0) 2022.09.20
    [백준 14502] 연구소  (0) 2022.09.20
    [백준 10043] 택시  (0) 2022.09.19
    [C++ STL] MAP  (0) 2022.09.19

    댓글

Designed by Tistory.