Tmp isa: Difference between revisions
From Algopedia
Jump to navigationJump to search
No edit summary |
(No difference)
|
Latest revision as of 20:14, 26 November 2018
// Solutie Flux
// Tinca Matei
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 8;
int x[2*MAX_N], y[2*MAX_N];
int num[2*MAX_N];
// Clasa de flux maxim de cost minim
class MinCostMaxFlow {
int n, src, dest;
// Lista de adiacenta
vector<int> graph[1+2*MAX_N+1];
// Capacitatile pe fiecare muchie
int kappa[1+2*MAX_N+1][1+2*MAX_N+1];
// Fluxul transmis pe fiecare muchie
int flow[1+2*MAX_N+1][1+2*MAX_N+1];
// Parintele unui nod in arborele de la Bellman-Ford
int papa[1+2*MAX_N+1];
// costul unei muchii din graf
double cost[1+2*MAX_N+1][1+2*MAX_N+1];
// Cel mai bun cost de la sursa pana la nodul curent
double bestcost[1+2*MAX_N+1];
// Pompeaza flux de la sursa la destinatie pe drumul in arborele de la Bellman-Ford
void pump(int nod) {
int i = nod;
int fl = 10;
// Initial trebuie sa calculam cat flux pompam pe drumul respectiv
while(i != src) {
fl = min(fl, kappa[papa[i]][i] - flow[papa[i]][i]);
i = papa[i];
}
// Pompam fluxul propriu-zis pe drumul din arborele Bellman-Ford
while(nod != 0) {
flow[papa[nod]][nod] += fl;
flow[nod][papa[nod]] -= fl;
nod = papa[nod];
}
// Reactualizam cantitatea pompata de flux in total si costul in total
maxfl += fl;
mincost += bestcost[dest] * fl;
}
// Baga Bellman-Ford; returneaza true daca a gasit un drum pe care poate pompa flux de la sursa la destinatie
bool bellmanford() {
deque<pair<int, double> > q;
// Initializam toate costurile cu infinit, in afara de costul sursei care va fi 0
for(int i = 0; i < n; ++i)
bestcost[i] = 1000000000.0f;
bestcost[src] = 0.0f;
// Bagam sursa in coada
q.push_back(make_pair(src, 0.0f));
while(!q.empty()) {
// Scoatem primul element din coada
int nod = q.front().first;
double costnod = q.front().second;
q.pop_front();
// Reactualizam costurile minime pt fiecare vecin
for(auto it: graph[nod]) {
if(flow[nod][it] < kappa[nod][it] && bestcost[nod] + cost[nod][it] < bestcost[it]) {
bestcost[it] = bestcost[nod] + cost[nod][it];
q.push_back(make_pair(it, bestcost[it]));
papa[it] = nod;
}
}
}
// Exista drum de la sursa pana la destinatie
if(bestcost[dest] < 1000000000.0f) {
pump(dest);
return true;
}
return false;
}
public:
int maxfl;
double mincost;
// Initializeaza toate variabilele interne
MinCostMaxFlow(int _n) {
n = _n;
src = 0;
dest = n - 1;
maxfl = mincost = 0;
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j)
kappa[i][j] = flow[i][j] = cost[i][j] = 0;
}
// Adauga o muchie de la nodul a la nodul b cu capacitate k si cost c
void pushEdge(int a, int b, int k, double c) {
graph[a].push_back(b);
graph[b].push_back(a);
cost[a][b] = c;
cost[b][a] = -c;
kappa[a][b] = k;
}
// Ruleaza algoritmul de flux
void runFlow() {
while(bellmanford());
}
};
// Returneaza patratul lui x
double sqr(double x) {
return x * x;
}
// Returneaza distanta dintre doua puncte (x1, y1), (x2, y2)
double dist(double x1, double y1, double x2, double y2) {
return sqrt(sqr(x1 - x2) + sqr(y1 - y2));
}
int main() {
int n;
double rez = 1000000000.0f;
// Citirea numerelor
FILE *fin = fopen("xd.in", "r");
fscanf(fin, "%d", &n);
for(int i = 0; i < 2 * n; ++i)
fscanf(fin, "%d%d", &x[i], &y[i]);
fclose(fin);
// Impart toate casele in doua submultimi. Intre doua case din submultimi diferite
// adaug o muchie de capacitate 1 si cost egal cu distanta dintre cele doua puncte
for(int i = 0; i < (1 << (2 * n)); ++i) {
int cnt = 0;
// Renumerotam toate casele; Cele din multimea A vor fi numerotate de la 1 pana la n,
// iar cele din multimea B vor fi numerotate de la n + 1 pana la 2 * n
for(int j = 0; j < 2 * n; ++j)
if((1 << j) & i) {
cnt++;
num[j] = cnt;
} else
num[j] = n + (j + 1) - cnt;
if(cnt == n) {
MinCostMaxFlow flx(2 * n + 2);
// Construirea propriu-zisa a grafului
for(int a = 0; a < 2 * n; ++a) {
for(int b = 0; b < 2 * n; ++b)
if(((1 << a) & i) > 0 && ((1 << b) & i) == 0)
flx.pushEdge(num[a], num[b], 1, dist(x[a], y[a], x[b], y[b]));
// Adaug o muchie de la sursa la fiecare nod din multimea A
if((1 << a) & i)
flx.pushEdge(0, num[a], 1, 0);
else // Adaug o muchie de la fiecare nod din multimea B la destinatie
flx.pushEdge(num[a], 2 * n + 1, 1, 0);
}
// Rulez fluxul pe graful construit
flx.runFlow();
// In graful construit, cuplajul trebuie sa fie perfect!!!
assert(flx.maxfl == n);
// reactualizam rezultatul
rez = min(rez, flx.mincost);
}
}
// Afisam rezultatele
FILE *fout = fopen("xd.out", "w");
fprintf(fout, "%.10f", rez);
fclose(fout);
return 0;
}
/*
Graful construit:
Noi ne construim un graf bipartit format din doua multimi A si B. Noi vrem sa cuplam fiecare nod din multimea A cu un nod din multimea B
Costul unei muchii dintre A si B va fi distanta dintre cele doua puncte pe care le reprezinta.
Algoritmul de cuplaj maxim de cost minim se implementeaza cu ajutorul algoritmului de flux maxim de cost minim
Avem sursa(0) si destinatia(2*n+1) si nodurile interne (cele din multimea A(1...N) si cele din multimea B(N+1...2N))
Intre sursa si fiecare nod din multimea A avem o muchie de capacitate 1 si cost 0
Intre fiecare nod din multimea B si destinatie avem o muchie de capacitate 1 si cost 0
Intre fiecare nod din multimea A si multimea B avem o muchie de capacitate 1 si cost egal cu distanta dintre cele doua puncte
Capacitatile de la ultimul tip de muchii nu este asa de relevant, acesta poate fi infinita, atata timp cat este mai mare decat 1.
Celalalte muchii trebuie sa aiba capacitate neaparat 1
*/