Pagini recente » Cod sursa (job #2772158) | Cod sursa (job #459637) | Cod sursa (job #2170807) | Cod sursa (job #1047301) | Cod sursa (job #2962685)
/*
* Complexitate timp O(n * m)
* Algoritm folosit: Edmonds-Karp
*
* Am creat o SuperSursa si un SuperSink, SuperSursa este conectata la TOATE nodurile din partea stanga a grafului,
* nodurile din partea stanga a grului sunt conectate la nodurile din partea dreapta a grafului conform inputului,
* TOATE nodurile din partea dreapta sunt conectate la SuperSink.
*
* Aplic algoritmul de flux pe graful rezultat.
*
* In graful rezidual, muchiile saturate care pleaca din nodurile din partea stanga a grafului sunt muchiile care
* formeaza cuplajul maxim.
*
* Identic cu clasa Flow din flux-max( exercitiul 1 ), mai putin functia getMinCut + dfs, de asemenea, am adaugat functia printGraph,
* care imi afiseaza muchiile care fac parte din cuplajul maxim
*
* P.S.: fiecare muchie are capacitatea 1, deci fluxul maxim este egal cu numarul de muchii din cuplajul maxim
* */
#include <fstream>
#include <vector>
#include <queue>
using namespace std;
ifstream in("cuplaj.in");
ofstream out("cuplaj.out");
int l, r, m;
class Flow
{
int source, sink;
int n;
int maxFlow;
std::vector<bool> visited;
std::vector<std::vector<int> > graph;
std::vector<int> father;
struct Edge {
int from, to, flow;
};
Edge edges[240005];
int edgeCount;
bool bfs()
{
visited.assign(n+1, false);
std::queue<int> q;
q.push(source);
visited[source] = true;
while(!q.empty())
{
int node = q.front();
q.pop();
for(int edgeID : graph[node])
{
Edge const e = edges[edgeID];
int to = e.to;
if(!e.flow || visited[to])
continue;
visited[to] = true;
q.push(to);
father[to] = edgeID;
if(to == sink)
return true;
}
}
return false;
}
public:
Flow(int n, int source, int sink)
{
this->source = source;
this->sink = sink;
this->n = n;
this->maxFlow = 0;
this->edgeCount = -1;
graph.resize(n + 1);
visited.resize(n + 1);
father.resize(n + 1);
}
void addEdge(int x, int y, int cost = 1)
{
graph[x].push_back(++edgeCount);
edges[edgeCount].from = x;
edges[edgeCount].to = y;
edges[edgeCount].flow = cost;
graph[y].push_back(++edgeCount);
edges[edgeCount].from = y;
edges[edgeCount].to = x;
edges[edgeCount].flow = 0;
}
int getMaxFlow()
{
maxFlow = 0;
do{
if( !bfs() )
break;
for (auto edgeID : graph[sink]) {
int to = edges[edgeID].to;
if(!edges[edgeID ^ 1].flow || !visited[to])
continue;
father[sink] = edgeID ^ 1;
int flowMin = 0x7fffffff;
for (int i = sink; i != source; i = edges[father[i]].from)
flowMin = std::min(flowMin, edges[father[i]].flow);
if (flowMin == 0)
continue;
maxFlow += flowMin;
for (int i = sink; i != source; i = edges[father[i]].from) {
edges[father[i]].flow -= flowMin;
edges[father[i] ^ 1].flow += flowMin;
}
}
} while (true);
return maxFlow;
}
int getFlow() const{
return maxFlow;
}
void printGraph() {
// i = 2 * (l + r) pentru ca am adaugat mai intai muchiile SuperSursei si ale SuperSink-ului
for (int i = 2 * (l + r); i <= edgeCount; i+=2) { // i+=2 pentru ca muchiile pare sunt muchiile "originale", muchiile impare sunt muchiile reziduale
if(edges[i].from <= l && edges[i].to > l && !edges[i].flow) // muchia este saturata si pleaca din partea stanga a grafului
out << edges[i].from << ' ' << edges[i].to - l << '\n';
}
}
};
int main() {
in >> l >> r >> m;
Flow f(l + r + 1, 0, l + r + 1);
for (int i = 1; i <= l; ++i) // SuperSursa -> nodurile din partea stanga
f.addEdge(0, i);
for (int i = l + 1; i <= l + r; ++i) // nodurile din partea dreapta -> SuperSink
f.addEdge(i, l + r + 1);
int x, y;
while (m--) { // muchiile din input
in >> x >> y;
f.addEdge(x, l + y); // nodurile din partea stanga sunt conectate la nodurile din partea dreapta
}
in.close();
int sol = f.getMaxFlow();
out << sol << '\n';
f.printGraph();
out.close();
return 0;
}