Cod sursa(job #3323851)

Utilizator Latyn76Tinica Alexandru Stefan Latyn76 Data 20 noiembrie 2025 08:19:08
Problema Algoritmul lui Dijkstra Scor 0
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 8.78 kb
#include <iostream>
#include <vector>
#include <tuple>
#include <map>
#include <queue>
#include <algorithm>
#include <functional>
#include <thread>
#include <mutex>
#include <atomic>
#include <memory>
#include <chrono>
using namespace std;

// Structura Union-Find pentru Kruskal
class UnionFind
{
public:
    vector<int> parent, rank;

    UnionFind(int n)
    {
        parent.resize(n + 1);
        rank.resize(n + 1, 0);
        for (int i = 0; i <= n; i++)
        {
            parent[i] = i;
        }
    }

    int find(int x)
    {
        if (parent[x] != x)
        {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    bool unite(int x, int y)
    {
        int px = find(x);
        int py = find(y);
        if (px == py)
            return false;

        if (rank[px] < rank[py])
        {
            parent[px] = py;
        }
        else if (rank[px] > rank[py])
        {
            parent[py] = px;
        }
        else
        {
            parent[py] = px;
            rank[px]++;
        }
        return true;
    }
};

// Functie pentru a numara toate APM-urile unui graf
long long countMSTs(int n, vector<pair<int, int>> &edges)
{
    // Atribuim cost 1 tuturor muchiilor pentru a avea mai multe APM-uri posibile
    vector<tuple<int, int, int>> weightedEdges;
    for (int i = 0; i < (int)edges.size(); i++)
    {
        weightedEdges.push_back(make_tuple(1, edges[i].first, edges[i].second));
    }

    // Grupam muchiile dupa cost
    map<int, vector<pair<int, int>>> edgesByWeight;
    for (auto &edge : weightedEdges)
    {
        int w = get<0>(edge);
        int u = get<1>(edge);
        int v = get<2>(edge);
        edgesByWeight[w].push_back(make_pair(u, v));
    }

    // Numaram APM-urile folosind backtracking pe muchiile cu acelasi cost
    function<long long(UnionFind &, vector<pair<int, int>> &, int, int)> countAPMs;
    countAPMs = [&](UnionFind &uf, vector<pair<int, int>> &sameWeightEdges, int idx, int neededEdges) -> long long
    {
        if (neededEdges == 0)
            return 1;
        if (idx >= (int)sameWeightEdges.size())
            return 0;

        long long count = 0;
        int u = sameWeightEdges[idx].first;
        int v = sameWeightEdges[idx].second;

        // Incercam sa nu adaugam aceasta muchie
        count += countAPMs(uf, sameWeightEdges, idx + 1, neededEdges);

        // Incercam sa adaugam aceasta muchie
        if (uf.find(u) != uf.find(v))
        {
            UnionFind newUf = uf;
            newUf.unite(u, v);
            count += countAPMs(newUf, sameWeightEdges, idx + 1, neededEdges - 1);
        }

        return count;
    };

    // Pentru simplitate, toate muchiile au cost 1
    // Numarul de APM-uri = numarul de moduri de a alege n-1 muchii care formeaza un arbore
    UnionFind uf(n);
    long long totalMSTs = countAPMs(uf, edgesByWeight[1], 0, n - 1);

    return totalMSTs;
}

// Functie pentru a verifica daca un graf este conex
bool isConnected(int n, vector<pair<int, int>> &edges)
{
    // Cream lista de adiacenta
    vector<vector<int>> adj(n + 1);
    for (auto &edge : edges)
    {
        adj[edge.first].push_back(edge.second);
        adj[edge.second].push_back(edge.first);
    }

    // BFS pentru a verifica conexitatea
    vector<bool> visited(n + 1, false);
    queue<int> q;
    q.push(1);
    visited[1] = true;
    int visitedCount = 1;

    while (!q.empty())
    {
        int node = q.front();
        q.pop();

        for (size_t i = 0; i < adj[node].size(); i++)
        {
            int neighbor = adj[node][i];
            if (!visited[neighbor])
            {
                visited[neighbor] = true;
                q.push(neighbor);
                visitedCount++;
            }
        }
    }

    return visitedCount == n;
}

// Functie pentru a procesa un batch de grafuri
void processBatch(int n, vector<pair<int, int>> allEdges,
                  vector<vector<int>> selectors,
                  atomic<int> &graphCount,
                  atomic<int> &connectedGraphCount,
                  atomic<long long> &maxMSTs,
                  shared_ptr<vector<pair<int, int>>> bestGraph,
                  mutex &bestGraphMutex)
{
    long long localMaxMSTs = 0;
    vector<pair<int, int>> localBestGraph;

    for (size_t s = 0; s < selectors.size(); s++)
    {
        vector<int> &selector = selectors[s];
        graphCount++;

        // Extragem muchiile selectate pentru acest graf
        vector<pair<int, int>> selectedEdges;
        for (size_t i = 0; i < allEdges.size(); i++)
        {
            if (selector[i] == 1)
            {
                selectedEdges.push_back(allEdges[i]);
            }
        }

        // Verificam daca graful este conex
        if (isConnected(n, selectedEdges))
        {
            connectedGraphCount++;

            // Numaram APM-urile pentru acest graf
            long long numMSTs = countMSTs(n, selectedEdges);

            // Verificam daca are cele mai multe APM-uri local
            if (numMSTs > localMaxMSTs)
            {
                localMaxMSTs = numMSTs;
                localBestGraph = selectedEdges;
            }
        }
    }

    // Actualizam rezultatul global daca am gasit ceva mai bun
    if (localMaxMSTs > 0)
    {
        lock_guard<mutex> lock(bestGraphMutex);
        long long currentMax = maxMSTs.load();
        if (localMaxMSTs > currentMax)
        {
            maxMSTs = localMaxMSTs;
            *bestGraph = localBestGraph;
        }
    }
}

// Functie pentru a genera toate grafurile cu n noduri si m muchii (multithreaded)
void generateGraphs(int n, int m)
{
    int maxEdges = n * (n - 1) / 2;
    if (m > maxEdges)
    {
        cout << "Nu se pot genera grafuri cu " << m << " muchii pentru " << n << " noduri!" << endl;
        return;
    }

    vector<pair<int, int>> allEdges;
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            allEdges.push_back(make_pair(i, j));
        }
    }

    vector<int> selector(allEdges.size(), 0);
    fill(selector.begin(), selector.begin() + m, 1);

    cout << "Generare combinatii..." << endl;
    vector<vector<int>> allSelectors;
    do
    {
        allSelectors.push_back(selector);
    } while (prev_permutation(selector.begin(), selector.end()));

    cout << "Total combinatii: " << allSelectors.size() << endl;
    cout << "Procesare cu multithreading..." << endl;

    atomic<int> graphCount(0);
    atomic<int> connectedGraphCount(0);
    atomic<long long> maxMSTs(0);
    auto bestGraph = make_shared<vector<pair<int, int>>>();
    mutex bestGraphMutex;

    unsigned int numThreads = thread::hardware_concurrency();
    if (numThreads == 0)
        numThreads = 4;

    cout << "Folosim " << numThreads << " thread-uri" << endl
         << endl;

    size_t batchSize = allSelectors.size() / numThreads;
    vector<thread> threads;

    auto startTime = chrono::high_resolution_clock::now();

    for (unsigned int i = 0; i < numThreads; i++)
    {
        size_t start = i * batchSize;
        size_t end = (i == numThreads - 1) ? allSelectors.size() : (i + 1) * batchSize;

        vector<vector<int>> batch(allSelectors.begin() + start, allSelectors.begin() + end);

        threads.emplace_back(processBatch, n, allEdges, batch,
                             ref(graphCount), ref(connectedGraphCount),
                             ref(maxMSTs), bestGraph, ref(bestGraphMutex));
    }

    for (size_t i = 0; i < threads.size(); i++)
    {
        threads[i].join();
    }

    auto endTime = chrono::high_resolution_clock::now();
    auto duration = chrono::duration_cast<chrono::seconds>(endTime - startTime);

    cout << "========================================" << endl;
    cout << "Total grafuri generate: " << graphCount << endl;
    cout << "Total grafuri conexe: " << connectedGraphCount << endl;
    cout << "Timp de executie: " << duration.count() << " secunde" << endl;
    cout << "========================================" << endl
         << endl;

    cout << "GRAFUL CU CELE MAI MULTE APM-URI:" << endl;
    cout << "Numar maxim de APM-uri: " << maxMSTs << endl;
    cout << "Muchii: ";
    for (size_t i = 0; i < bestGraph->size(); i++)
    {
        cout << "(" << (*bestGraph)[i].first << "," << (*bestGraph)[i].second << ") ";
    }
    cout << endl;
}

int main()
{
    int n = 7;  // numarul de noduri
    int m = 10; // numarul de muchii

    cout << "Generare grafuri CONEXE cu " << n << " noduri si " << m << " muchii" << endl;
    cout << "========================================" << endl
         << endl;

    generateGraphs(n, m);

    return 0;
}