Cod sursa(job #3278238)

Utilizator ShAwDoRneYNacu Gabriel ShAwDoRneY Data 18 februarie 2025 20:33:04
Problema Ubuntzei Scor 100
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 3.62 kb
#include <fstream>
#include <vector>
#include <queue>

using namespace std;

ifstream fin("ubuntzei.in");
ofstream fout("ubuntzei.out");

const int MAX2 = 32768; // 2^15 (Avem 15 orase importante)

int N,M,K;
vector<int> interestPoints(20); // Punctele mele importante
vector<vector<pair<int,int> > > graph(2005, vector<pair<int,int> >()); // Graful
vector<vector<int> > dist(20, vector<int>(2005, 1e9)); // Distantele dintre oricare 2 puncte importante (start, c1, c2, ..,cn, final)
vector<vector<int> > dp(MAX2, vector<int>(20, 1e9)); // dp[mask][i] = distanta minima de a trece prin toate orasele din mask si a ajunge la nodul i

void DIJKSTRA(int node, int index) {
    // Este un DIJKSTRA normal pentru calcularea distantelor minime
    priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > > pq; // PQ de minime
    pq.push({0, node});
    dist[index][node] = 0;

    while(!pq.empty()) {

        int currentDistance = pq.top().first;
        int currentNode = pq.top().second;
        pq.pop();

        if(currentDistance > dist[index][currentNode])
            continue;

        for(auto neighbor : graph[currentNode]) {
            int cost = neighbor.first;
            int vecin = neighbor.second;

            int newCost = currentDistance + cost;

            if(newCost < dist[index][vecin]) {
                dist[index][vecin] = newCost;
                pq.push({newCost, vecin});
            }
        }
    }
}

void MIN_PATH() {

    // DP-ul va tine numai orasele pe care trebuie sa le vizitez. Astfel orasul i este defapt interestPoints[i+1]
    for(int i=0; i<K; i++) {
        dp[1 << i][i] = dist[0][interestPoints[i+1]]; // Distanta de a trece printr-un oras si de a ramane la el, este chiar distanta calculata de la Cluj la oras din DIJKSTRA
    }

    for(int mask=0; mask < (1 << K); mask++) { // Incerc toate combinatiile posibile de a vizita orasele
        for(int i=0; i<K; i++) {
            if(mask & (1 << i)) { // Daca sunt la un oras activ, vizitat
                for(int j=0; j<K; j++) {
                    if(!(mask & (1 << j))) { // Ma uit la un vecin care nu este deja vizitat
                        int new_mask = mask|(1<<j);
                        dp[new_mask][j] = min(dp[new_mask][j], dp[mask][i] + dist[i+1][interestPoints[j+1]]); // Daca pot ajunge la vecinul lui (j) printr-un traseu mai bun
                    }
                }
            }
        }
    }
    
    int ans = 1e9;
    int MAX = (1<<K)-1;

    // Vizitarea optima a tuturor oraselor se va termina sigur intr-un oras specific, dar nu stim in care

    for(int i=0; i<K; i++) {
        ans = min(ans, dp[MAX][i] + dist[i+1][N]); // Testam fiecare posibilitate in care sa ne fi oprit si o luam pe cea mai buna
    }

    fout << ans;

    // Sper ca v-a fost de folos. Este o problema destul de dificila. Mi-am batut si eu destul capul la ea.
    // #SPOR la codare :)
}

int main() {

    fin >> N >> M >> K;

    for(int i=1; i<=K; i++) {
        fin >> interestPoints[i];
    }

    interestPoints[0] = 1, interestPoints[K+1] = N; // Setez nodul 1 si nodul N ca fiind puncte importante

    for(int i=1; i<=M; i++) {
        int x,y,w;
        fin >> x >> y >> w;
        graph[x].push_back({w, y});
        graph[y].push_back({w, x});
    }

    for(int i=0; i<=K; i++) {
        DIJKSTRA(interestPoints[i], i); // Apelez DIJKSTRA pentru toate punctele importante, pentru a sti distantele dintre ele
    }

    if(K == 0) // Daca trebuie sa ajung direct la Vama Veche
        fout << dist[0][N];
    else
        MIN_PATH();

    return 0;
}