Cod sursa(job #1882039)

Utilizator jurjstyleJurj Andrei jurjstyle Data 16 februarie 2017 21:58:29
Problema Ubuntzei Scor 100
Compilator cpp Status done
Runda Arhiva de probleme Marime 3.83 kb
//Complexitate O(((M log2 N) * K) + (2^K) * (K^2))
#include <fstream>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;

ifstream f("ubuntzei.in");
ofstream g("ubuntzei.out");

const int NMAX = 2002;
const int KMAX = 16;
const int inf = 0x3f3f3f3f;

int N, M, K;

vector <pair<int,int>> G[NMAX];
vector <int> Localitati;
vector <int> Distanta_Initiala; //distantele minime din nodul sursa
vector <int> Distanta_Localitati[KMAX]; //distantele minime din fiecare localitate din cele K

int dp[KMAX][1 << KMAX]; //dp[i][s] - costul minim de a ajunge in nodul i , unde s este o submultime a celor K localitati prin care trebuie sa trecem
                         //fiecare K[i] - localitate va fi reprezentata de al i-lea bit din valoarea lui s
//recurenta este : dp[i][j] = dp[j][s-{i}] + dist_min(j, i) , unde j apartine lui s si i != j (nu exista cicluri)


void Dijkstra(int, vector <int> & );

int main()
{
    //citim
    f >> N >> M >> K;
    Localitati = vector <int> (K + 1);
    for (int i = 0; i < K; ++i)
        f >> Localitati[i];
    for (int i = 1; i <= M; ++i)
    {
        int x, y, c;
        f >> x >> y >> c;
        G[x].push_back({y, c});
        G[y].push_back({x, c});
    }
    //aflam vectorii de distante - O((M log2 N) * K)
    Dijkstra(1, Distanta_Initiala);
    if (K == 0)
    {
        g << Distanta_Initiala[N];
        return 0;
    }
    for (int i = 0; i < K; ++i)
        Dijkstra(Localitati[i], Distanta_Localitati[i]);
    //aplicam dinamica - O((2^K) * (K^2))
    //initializare a starii formate doar din nodul i prin drumul de la 1
    for (int i = 0; i < K; ++i)
        dp[i][(1 << i)] = Distanta_Initiala[Localitati[i]];
    for (int s = 1; s < (1 << K); ++s) //pt orice stare
    {
        for (int i = 0; i < K; ++i) //pt orice localitate
            if ((1 << i) < s) //daca starea actuala e diferita de cazul cand multimea e formata doar din localitatea initiala
            {
                dp[i][s] = inf;
                if ( ((s >> i) & 1) == 1) //daca al i-lea bit a lui s este 1 (deci starea respectiva trece prin i)
                    for (int j = 0; j < K; ++j) //cautam pt orice alta localitate
                        if (j != i && ((s >> j) & 1 ) == 1) //o alta stare intermediara precedenta unde si al j-lea bit a lui s este 1
                        {
                            //daca da inseamna ca am ajuns intr-o recurenta buna
                            int cost_curent = dp[j][s - (1 << i)] + Distanta_Localitati[j][Localitati[i]]; //calculam un cost posibil dupa recurenta scrisa sus
                            dp[i][s] = min(dp[i][s], cost_curent); //alegem minimul
                        }
            }
    }
    //calculam bestul care minimul dintre toate starile dp[i][(1 << K) - 1] + dist(i,N) , unde (1 << K) - 1 reprezinta 2^K - 1, deci o reprezentare plina de 1 : adica toate cele K localitati
    int best = inf;                                                         //dist(i,N) - distanta ramasa din ultima localitate obligatorie pana la destinatia finala
    for (int i = 0; i < K; ++i)
        best = min(best, dp[i][(1 << K) - 1] + Distanta_Localitati[i][N]);
    g << best;
    f.close();
    g.close();
}

void Dijkstra(int start, vector <int> & D)
{
    priority_queue <pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> Q;
    D = vector <int> (NMAX, inf);
    D[start] = 0;
    Q.push({0, start});
    while (!Q.empty())
    {
        int x = Q.top().second, dx = Q.top().first;
        Q.pop();
        if (dx > D[x])
            continue;
        for (const pair<int,int> & p : G[x])
        {
            int y = p.first, w = p.second;
            if (D[y] > D[x] + w)
            {
                D[y] = D[x] + w;
                Q.push({D[y], y});
            }
        }
    }
}