Alex_tz307

Tobruk - explained

Oct 29th, 2020 (edited)
164
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.32 KB | None | 0 0
  1. #include <bits/stdc++.h>
  2. #define int long long
  3.  
  4. // Despre flux: https://www.youtube.com/watch?v=LdOnanfc5TM&list=PLDV1Zeh2NRsDGO4--qE8yH72HFL1Km93P&index=33
  5. /* Edmonds-Karp: https://www.youtube.com/watch?v=RppuJYwlcI8&list=PLDV1Zeh2NRsDGO4--qE8yH72HFL1Km93P&index=38,
  6.                  https://cp-algorithms.com/graph/edmonds_karp.html */
  7. // Implementare personala la Edmonds-Karp: https://pastebin.com/1dDz6zL5
  8. /* Dinic: https://www.youtube.com/watch?v=M6cm8UeeziI&list=PLDV1Zeh2NRsDGO4--qE8yH72HFL1Km93P&index=42,
  9.           https://cp-algorithms.com/graph/dinic.html */
  10. // Implementare personala la Dinic: https://pastebin.com/4yXgZWyz
  11. /* Optional, flux maxim de cost minim:
  12.     * infoarena: https://www.infoarena.ro/problema/fmcm
  13.     * cp-algorithms: https://cp-algorithms.com/graph/min_cost_flow.html
  14.     * implementare personala:
  15.         - SPFA: https: //www.infoarena.ro/problema/fmcm - mai ineficient
  16.         - SPFA + Dijkstra: https://pastebin.com/ue5TRmLC - eficient
  17. */
  18.  
  19. using namespace std;
  20.  
  21. ifstream fin("tobruk.in");
  22. ofstream fout("tobruk.out");
  23.  
  24. const int INF = 1e18L;
  25.  
  26. class FlowEdge { // structura unei muchii din graful rezidual pentru Dinic
  27.     public:
  28.         int u, v, capacity;
  29.  
  30.         FlowEdge(int u, int v, int capacity) {
  31.             this -> u = u;
  32.             this -> v = v;
  33.             this -> capacity = capacity;
  34.         }
  35. };
  36.  
  37. class Dinic { // Flux maxim cu algoritmul lui Dinic, foarte eficient
  38.     public:
  39.         vector < FlowEdge > edges; // toate muchiile
  40.         vector < vector < int > > adj; // lista de adiacenta a muchiilor
  41.         int N, M = 0, source, sink; // numarul de noduri, de muchii, nodul sursa si cel destinatie
  42.         vector < int > level, ptr; // nivelul fiecarui nod
  43.         queue < int > Q; // coada pentru BFS
  44.  
  45.         Dinic(int N, int source, int sink) { // Inititalizare
  46.             this -> N = N;
  47.             this -> source = source;
  48.             this -> sink = sink;
  49.             adj.resize(N + 1);
  50.             level.resize(N + 1);
  51.             ptr.resize(N + 1);
  52.         }
  53.  
  54.         void add_edge(int u, int v, int capacity) { // Adaugam muchiile in graful rezidual
  55.             if(u == v)
  56.                 return; // muchiile de la nod la nod nu le punem
  57.             edges.emplace_back(u, v, capacity); // adaugam muchia din input
  58.             edges.emplace_back(v, u, 0); // adaugam muchia de cost 0 pentru graful rezidual
  59.             adj[u].emplace_back(M++); // muchiile de cost 0 vor fi folosite pentru
  60.             adj[v].emplace_back(M++); // "intoarcerea din decizii proaste"
  61.         }
  62.  
  63.         bool BFS() { // BFS pentru a calcula nivelele
  64.             while(!Q.empty()) {
  65.                 int curr = Q.front(); // nodul curent
  66.                 Q.pop();
  67.                 for(int id : adj[curr])
  68.                     if(level[edges[id].v] == -1 && edges[id].capacity) { // daca capacitatea este 0,
  69.                         level[edges[id].v] = level[curr] + 1; // muchia este saturata si trebuie ignorata
  70.                         Q.emplace(edges[id].v); // daca se poate ajunge la nod il adaugam in coada
  71.                     }
  72.             }
  73.             return level[sink] != -1; // daca nu putem ajunge la sink nu mai exista alte
  74.             // drumuri de ameliorare si algoritmul trebuie oprit
  75.         }
  76.  
  77.         int DFS(int u, int curr_flow) { // Cautam drumuri de ameliorare
  78.             if(curr_flow == 0)
  79.                 return 0; // s-a ajuns la flux 0 deci nu mai are rost sa mai continuam
  80.             if(u == sink)
  81.                 return curr_flow; // am ajuns la sink deci ne oprim
  82.             for(int& p = ptr[u]; p < (int)adj[u].size(); ++p) { // nu consideram mereu toate muchhiile
  83.                 int id = adj[u][p],                             // deci mutam pointer-ul
  84.                     v = edges[id].v;
  85.                 if(level[u] + 1 != level[v] || edges[id].capacity <= 0)
  86.                     continue; // daca nu poti ajunge de la u la v sau muhcia u -> v este saturata ignoram
  87.                 int new_flow = DFS(v, min(curr_flow, edges[id].capacity));
  88.                 // noul flux este minimul dintre fluxul de pana acum(curr_flow) si fluxul obtinut ulterior
  89.                 if(new_flow == 0)
  90.                     continue; // daca ajungem la flux nou 0 putem trece mai departe
  91.                 edges[id].capacity -= new_flow; // capacitatea muchiei u -> v scade cu noua cantitate de flux
  92.                 edges[id ^ 1].capacity += new_flow;
  93.                 // capacitatea muchiei reziduale v -> u creste cu noua cantitate de flux
  94.                 return new_flow; // returnam fluxul obtinut
  95.             }
  96.             return 0;
  97.         }
  98.  
  99.         int max_flow() { // Fluxul maxim
  100.             int flow_max = 0;
  101.             while(true) {
  102.                 fill(level.begin(), level.end(), -1);
  103.                 level[source] = 0; // initializam nivelele
  104.                 Q.emplace(source); // pornim de la sursa
  105.                 if(!BFS())
  106.                     break; // daca nu mai putem ajunge la sink salut
  107.                 fill(ptr.begin(), ptr.end(), 0); // initial toti pointerii sunt la 0(inceput, deci consideram toate muchiile)
  108.                 while(int curr_flow = DFS(source, INF))
  109.                     flow_max += curr_flow; // pana mai gasim drumuri de ameliorare crestem fluxul maxim
  110.             }
  111.             return flow_max; // returnam rezultatul
  112.         }
  113. };
  114.  
  115. inline void min_self(int& a, int b) {
  116.     a = min(a, b);
  117. }
  118.  
  119. inline void max_self(int& a, int b) {
  120.     a = max(a, b);
  121. }
  122.  
  123. class squads {
  124.     public:
  125.         int position, strength, fuel, price;
  126. };
  127.  
  128. class bases {
  129.     public:
  130.         int position, defense, oil;
  131.  
  132.         bool operator < (const bases& A) const { // comparator pentru sortare(crescator dupa puterea defensiva)
  133.             return this -> defense < A.defense;
  134.         }
  135. };
  136.  
  137. int32_t main() {
  138.     fin.sync_with_stdio(false);
  139.     fout.sync_with_stdio(false);
  140.     fin.tie(nullptr);
  141.     fout.tie(nullptr);
  142.     int N, M;
  143.     fin >> N >> M;
  144.     vector < vector < int > > cost(N + 1, vector < int >(N + 1, INF));
  145.     // cost[i][j] - lungimea drumului minim de la nodul i la nodul j
  146.     for(int i = 1; i <= N; ++i)
  147.         cost[i][i] = 0;
  148.     while(M--) {
  149.         int u, v;
  150.         fin >> u >> v;
  151.         cost[u][v] = cost[v][u] = min(cost[u][v], 1LL);
  152.     }
  153.     for(int i = 1; i <= N; ++i) // Folosim algoritmul lui Roy-Floyd pentru a calcula drumul minim dintre 2 noduri
  154.         for(int j = 1; j <= N; ++j)
  155.             for(int k = 1; k <= N; ++k)
  156.                 min_self(cost[i][j], cost[i][k] + cost[k][j]);
  157.     int S, B, K;
  158.     fin >> S >> B >> K;
  159.     vector < squads > squad(S + 1);
  160.     for(int i = 1; i <= S; ++i)
  161.         fin >> squad[i].position >> squad[i].strength >> squad[i].fuel >> squad[i].price;
  162.     vector < bases > base(B + 1);
  163.     for(int i = 1; i <= B; ++i)
  164.         fin >> base[i].position >> base[i].defense >> base[i].oil;
  165.     sort(base.begin(), base.end()); // sortam bazele crescator dupa puterea defensiva
  166.     vector < vector < pair < int , int > > > base_place(N + 1);
  167.     for(int i = 1; i <= B; ++i) {
  168.         int poz = base[i].position;
  169.         if(base_place[poz].empty() || base_place[poz].back().second < base[i].oil)
  170.             base_place[poz].emplace_back(base[i].defense, base[i].oil);
  171.         /* Adaugam greedy la fiecare nod din graf bazele care sunt pozitionate in acesta si au puteri defensive cat mai
  172.         mici si cantitati de petrol cat mai mari. Astfel daca am o baza cu putere defensiva mai mare si cantitate de
  173.         petrol mai mica ca ultima aduagata, adaugarea acesteia este inutila, deci nu o facem. */
  174.     }
  175.     int source = 0, sink = S + 1; // sursa 0 si sink S + 1
  176.     Dinic Graph(S + 2, source, sink); // initializam reteaua pentru flux
  177.     int ans = 0;
  178.     vector < int > val(S + 1);
  179.     for(int i = 1; i <= S; ++i) { // iteram prin trupe
  180.         bool found = false;
  181.         for(int p = 1; p <= N; ++p) { // iteram prin nodurile grafului initial
  182.             if(cost[squad[i].position][p] > squad[i].fuel)
  183.                 continue; // daca nu putem ajunge de la echipa la nod salut, mai departe
  184.             auto it = upper_bound(base_place[p].begin(), base_place[p].end(), make_pair(squad[i].strength, INF));
  185.             /* cautam binar(cu o functie ms STL) prima baza ce se afla in nodul p si are
  186.                puterea defensiva <= puterea de asalt a trupei i */
  187.             if(it != base_place[p].begin()) { // daca o si gasim
  188.                 --it;
  189.                 found = true; // marcam ca am gasit ceva pentru trupa i
  190.                 max_self(val[i], it -> second); // val[i] o setam la maximul valorilor de pana acum
  191.             }
  192.         }
  193.         val[i] -= squad[i].price; // profitul e val[i] - price[i] - din enunt
  194.         if(!found)
  195.             val[i] = -INF; // daca nu gasim avem profit -INF
  196.         if(val[i] >= 0) {
  197.             ans += val[i]; // daca gasim muchie cu valoare pozitiva crestem rezultatul
  198.             Graph.add_edge(source, i, val[i]); // adaugam muchia in retea
  199.         }
  200.         else
  201.             Graph.add_edge(i, sink, -val[i]);
  202.     }
  203.     while(K--) {
  204.         int u, v;
  205.         fin >> u >> v; // citim dependentele
  206.         Graph.add_edge(u, v, INF); // adaugam si dependentele in retea ca muchii de capacitate infinita
  207.     }
  208.     fout << ans - Graph.max_flow(); // rezultatul este ANS de pana acum - (costul taieturii minime, care e la fel cu fluxul maxim)
  209. }
  210.  
Add Comment
Please, Sign In to add comment