Cod sursa(job #1477936)

Utilizator bciobanuBogdan Ciobanu bciobanu Data 27 august 2015 13:46:32
Problema Ubuntzei Scor 95
Compilator cpp Status done
Runda Arhiva de probleme Marime 4.96 kb
#include <fstream>
#include <vector>
#include <queue>
#include <bitset>

const int MAX_K = 15;
const int MAX_N = 2000;
const int MAX_M = 10000;
const int BUFFSIZE = 4096;
const int INF = 2000000000;
const int NIL = -1;
const char IN [ ] = "ubuntzei.in" ;
const char OUT[ ] = "ubuntzei.out";

using namespace std;

#define pb emplace_back
#define mp make_pair

typedef pair < int, int > PII ;

ofstream cout( OUT );

//pentru parsare
char buffer [ BUFFSIZE ];
int pos = BUFFSIZE;

// date citite
int n;                          // numarul de noduri
int node [ MAX_K + 2 ];         // nodurile prin care trebuie trecut

struct {
    int v, cost, next;
} l [ 2 * MAX_M ];

int adj [ MAX_N ];

// pentru dinamica
int dist [ MAX_K + 2 ][ MAX_K + 2 ];                // dist [ i ][ j ] = distana minima pornind din nodul node [ i ] pana in node [ j ]
int din  [ ( 1 << ( MAX_K + 2 ) ) ][ MAX_K + 2 ];   // din [ mask ][ j ] = distana minima folosind un drum de configuratie mask pana in node [ j ]

// pentru dijkstra
int d [ MAX_N ]; // distante minime
struct cmp  // prioriteaza distana minima
{
    bool operator () ( const int &a, const int &b )
    {
        return d [ a ] > d [ b ];
    }
};
priority_queue < int, vector < int > , cmp > heap;
bitset < MAX_N > seen ;

void dijkstra ( int nod )
{
    for ( int i = 0; i < n; i ++ )
        d [ i ] = INF;

    seen.reset ( );

    heap.push ( nod );
    d [ nod ] = 0;
    while ( !heap.empty ( ) )
    {
        nod = heap.top ( );
        heap.pop ( );

        if ( seen [ nod ] ) continue ;

        seen [ nod ] = 1;

        for ( int i = adj [ nod ]; i != NIL; i = l [ i ].next )
        {
            int son = l [ i ].v;
            int newCost = d [ nod ] + l [ i ].cost;

            if ( d [ son ] > newCost )
            {
                d [ son ] = newCost;
                heap.push ( son );
            }
        }
    }
}

inline char getChr ( )
{
    if ( pos == BUFFSIZE )
    {
        fread ( buffer, 1, BUFFSIZE, stdin );
        pos = 0;
    }
    return buffer [ pos ++ ];
}

inline int readInt ( )
{
    int q = 0;
    char c;

    do
    {
        c = getChr ( );
    } while ( !isdigit ( c ) );

    do
    {
        q = ( q << 1 ) + ( q << 3 ) + ( c - '0' );
        c = getChr ( );
    } while ( isdigit ( c ) );
    return q;
}

inline void addEdge ( int u, int v, int cost, int pos )
{
    l [ pos ].v = v;
    l [ pos ].cost = cost;
    l [ pos ].next = adj [ u ];
    adj [ u ] = pos;
}

int main ( void )
{
    freopen ( IN, "r", stdin );
    int m, k;
    int u, v, c;

    n = readInt ( );
    m = readInt ( );
    k = readInt ( );

    node [ 0 ] = 0;
    for ( int i = 1; i <= k; i ++ )
        node [ i ] = readInt ( ) - 1;
    node [ k + 1 ] = n - 1;

    for ( int i = 0; i < n; i ++ )  // initializare capete lista
        adj [ i ] = NIL;

    for ( int i = 0; i < m; i ++ )
    {
        u = readInt ( ) - 1;
        v = readInt ( ) - 1;
        c = readInt ( );
        addEdge ( u, v, c, 2 * i );
        addEdge ( v, u, c, 2 * i + 1 );
    }
    fclose ( stdin );

    for ( int i = 0; i <= k; i ++ )
    {
        dijkstra ( node [ i ] ); // facem un dijkstra pentru fiecare nod prin care trebuie sa trecem
        for ( int j = 0; j <= k + 1; j ++ )
        {
            dist [ i ][ j ] = d [ node [ j ] ]; // si retinem distanta intre 2 noduri prin care trebuie sa trecem
        }
    }

    for ( int i = 1; i < ( 1 << k ); i ++ )
    {
        for ( int j = 0; j < k; j ++ )
            din [ i ][ j ] = INF;
    }

    for ( int i = 1; i < ( 1 << k ); i ++ )
    {
        for ( int j = 0; j < k; j ++ )
        {
            if ( ( i >> j ) & 1 )   // daca j face parte din drumul curent
            {
                if ( !( i & ( i - 1 ) ) ) // daca drumul curent il contine doar pe j, atunci luam distanta de la nodul 0 la node [ j ]
                {
                    din [ i ][ j ] = dist [ 0 ][ j + 1 ];
                }
                else
                {
                    for ( int p = 0; p < k; p ++ )
                    {
                        if ( ( i >> p ) & 1 ) // daca p face parte din drumul curent
                            // consideram costul drumului curent fara j la care adaugam costul de la node [ p ] la node [ j ]
                            din [ i ][ j ] = min ( din [ i ][ j ], din [ i & ~( 1 << j ) ][ p ] + dist [ p + 1 ][ j + 1 ] );
                    }
                }
            }
        }
    }

    if ( !k ) // nu este nevoie sa trecem prin vreun nod, deci luam distanta minima de la 0 la n - 1
        u = dist [ 0 ][ 1 ];
    else
    {
        u = INF;
        for ( int i = 1; i <= k; i ++ ) // aflam min distantei de la oricare capat al traseului la nodul final
            u = min ( u, din [ ( 1 << k ) - 1 ][ i - 1 ] + dist [ i ][ k + 1 ] );
    }
    cout << u << '\n';
    cout.close ( );
    return 0;
}