알고리듬/문제

[baekjoon] 2001

narmeee 2023. 4. 28. 22:12

보석 줍기

문제

n(1 ≤ n ≤ 100)개의 섬이 m(1 ≤ m ≤ 1,000)개의 다리로 연결되어 있다. 각각의 다리는 서로 다른 두 섬을 연결하고 있으며, 서로 다른 두 섬은 최대 한 개의 다리로만 직접 연결되어 있다. 각각의 다리들의 튼튼한 정도는 서로 달라서, 각각의 다리마다 견딜 수 있는 무게의 제한이 다를 수 있다.

섬들 중, K(1 ≤ K ≤ 14)개의 서로 다른 섬에 각각 한 개씩 보석이 있다. 당신은 1번 섬에서 빈손으로 출발하여 최대한 많은 보석을 줍고 1번 섬으로 돌아오려 한다. 주의할 것은, 보석을 너무 많이 줍다 보면 다리를 건널 때 다리가 무게를 견디지 못하고 무너질 수 있다는 점이다. 따라서 당신은 다리가 무너지지 않는 한도 내에서 보석을 주워야 한다.

한 번 지난 적이 있는 다리와 섬을 여러 번 지날 수 있으며, 보석이 있는 섬을 지날 때에 그 보석을 줍지 않을 수도 있다고 하자.

입력

첫째 줄에 n, m, K가 주어진다. 다음 K개의 줄에는 보석이 있는 섬의 번호가 주어진다. 다음 m개의 줄에는 각 다리에 대한 정보를 나타내는 세 자연수 a, b, c(1 ≤ c ≤ 100)가 주어진다. 이는 a번 섬과 b번 섬이 다리로 연결되어 있는데, 그 다리가 최대 c개의 보석만을 견딜 수 있다는 의미이다. 예를 들어 c가 2라면, 그 다리를 지날 때 보석을 0, 1, 2개 가지고 있어야 한다는 의미이다. 3개 이상의 보석을 가지고 그 다리를 지나려고 하면 다리가 무너진다.

출력

첫째 줄에 주울 수 있는 보석의 최대 개수를 출력한다.

예제 입력

6 7 5
1
2
3
4
5
1 2 3
3 6 2
6 2 10
2 4 1
5 1 1
4 5 1
1 6 1

예제 출력

4

풀이

이전에 풀었던 "1194번 - 달이 차오른다, 가자."와 비슷하다.

차이점은 섬을 지날때 보석을 줍고 가는경우 그냥 가능경우를 생각하고 가중치를 검사해준다.

 

#include <iostream>
#include <queue>
#include <memory.h>

using namespace std;

vector<pair<int, int>> *island; // to, weight
int *jewelryIslandList;
int islandNumber, bridgeNumber, jewelryNumber;
bool **visit;

bool isJewelryIsland(int island){
    for(int i = 0; i < jewelryNumber; i++){
        if(jewelryIslandList[i] == island)return true;
    }
    return false;
}
int getJewelryIndex(int island){
    for(int i = 0; i < jewelryNumber; i++){
        if(jewelryIslandList[i] == island)return i;
    }
    return -1;
}

int BFS(int start){
    int answer = 0;
    queue<pair<int, pair<int, int>>> q; // 섬 , 보석 수, 보석 종류

    q.push(make_pair(start, make_pair(0, 0)));
    while(!q.empty()){
        int current = q.front().first;
        int getJewelryNumber = q.front().second.first;
        int jewelryKey = q.front().second.second;

        q.pop();

        //탐색 다하고 돌아온 경우
        if(current == start && answer < getJewelryNumber){
            answer = getJewelryNumber;
        }
        
        if(visit[current][jewelryKey])continue;
        visit[current][jewelryKey] = true;

        for(auto next : island[current]){
            int nextIsland = next.first;
            int nextIslandWeight = next.second;
            // 줍지 않고 이동, 방문하지 않고 가진보석이 가중치보다 작음.
            if(!visit[nextIsland][jewelryKey] && nextIslandWeight >= getJewelryNumber){   
                q.push(make_pair(nextIsland, make_pair(getJewelryNumber, jewelryKey)));
            }
            // 다음 섬에 보석이 있어서 줍는 경우
            if(isJewelryIsland(nextIsland)){
                int jewelryIndex = getJewelryIndex(nextIsland);
                int newkey = jewelryKey + (1 << jewelryIndex);
                if((jewelryKey & (1 << jewelryIndex)) || visit[nextIsland][newkey] || (nextIslandWeight < getJewelryNumber+1 && nextIsland != start))continue;
                q.push(make_pair(nextIsland, make_pair(getJewelryNumber+1, newkey)));                
            }
        }
    }
    return answer;
}



int main(){
    cin >> islandNumber >> bridgeNumber >> jewelryNumber;

    island = new vector<pair<int ,int>>[islandNumber+1];
    visit = new bool *[islandNumber + 1];
    
    int visitSize = (1 << jewelryNumber) + 1;
    for(int i = 0; i < islandNumber + 1; i++){
        visit[i] = new bool[visitSize];
        memset(visit[i], false, sizeof(bool)*visitSize);
    }
    jewelryIslandList = new int[jewelryNumber];
    //보석 입력
    for(int i = 0; i < jewelryNumber; i++)cin >> jewelryIslandList[i];
    
    //다리 입력
    for(int i = 0; i < bridgeNumber; i++){
        int i1, i2, w;
        cin >> i1 >> i2 >> w;

        island[i1].push_back(make_pair(i2, w));
        island[i2].push_back(make_pair(i1, w));
    }
    
    cout << BFS(1);

}

'알고리듬 > 문제' 카테고리의 다른 글

[baekjoon] 1655  (0) 2023.05.03
[baekjoon] 18223  (0) 2023.05.01
[baekjoon] 16118  (0) 2023.04.28
[baekjoon] 2212  (0) 2023.04.27
[baekjoon] 3020  (1) 2023.04.27