/******************************************************* Problem - MTV Slavery Solution by - Sean Falconer Algorithm: This is BY FAR the hardest problem in the set. The general idea of the problem is that you need to match various planes to various attack locations. However, you want to minimize the average distance the planes have to travel. This is a form of weighted bipartite graph matching. With normal bipartite matching, you have two groups that you wish to match (sometimes known as The Marriage Problem). There's a famous graph theorem that says that the maximum matching in a graph is equal to the maximum flow. To solve maximum flow, there's several known algorithms, one of the easiest is the Ford Fulkerson algorithm. This algorithm involves augmenting paths through the graph using BFS or DFS. For this problem, you can't use BFS or DFS to find augmenting paths, because you don't want just any augmenting path, you want the best one. Therefore, you need to use a shortest path algorithm to find your shortest path from the source to the sink. Because augmenting paths will create negative weights, you can't using Dijkstra's algorithm to find the shortest path, but you can use the Bellman Ford algorithm, which works for negative and positive edge weights. Finally, the basic algorithm is Ford Fulkerson, but I use the Bellman Ford algorithm to find augmenting paths. *******************************************************/ #include #include #include #include using namespace std; #define SOURCE 0 #define SINK 1 #define MAX 100000 int A[102][102], G[102][102], d[102], p[102]; int N, M, size; struct QItem { int vertex, prev; QItem() {} QItem(int vertex, int prev) : vertex(vertex), prev(prev) {} }; int findPath() { int i; for(i = 0; i < size; i++) { d[i] = MAX; p[i] = 0; } d[SOURCE] = 0; for(i = 1; i < size-1; i++) { for(int u = 0; u < size; u++) { for(int v = 0; v < size; v++) if(A[u][v]) { if(d[v] > d[u] + A[u][v]) { d[v] = d[u] + A[u][v]; p[v] = u; } } } } for(int u = 0; u < size; u++) { for(int v = 0; v < size; v++) { // negative cycle if(d[v] > d[u] + A[u][v]) return 0; } } return 1; } int augment() { findPath(); if(d[SINK] == MAX) return 0; int flow = 1, vertex = SINK; while(vertex) { A[p[vertex]][vertex] -= flow; A[vertex][p[vertex]] += flow; vertex = p[vertex]; } return flow; } int maxflow() { int increase = 1, result = 0; while(increase) { increase = augment(); result += increase; } return result; } void traceback() { int cost = 0; queue q; q.push(QItem(SINK, 0)); vector v(size, 0); while(!q.empty()) { QItem qi = q.front(); q.pop(); if(!v[qi.vertex]) { v[qi.vertex] = 1; // we're at a plane node if(qi.vertex > SINK && qi.vertex < N + 2) cost += G[qi.vertex][qi.prev]; else { // add adjacent nodes for(int i = 0; i < size; i++) if(!v[i] && A[qi.vertex][i]) { QItem qa = QItem(i, qi.vertex); q.push(qa); } } } } // output the average cost cout << setiosflags(ios::fixed) << setprecision(2); cout << ((double)cost/(double)M) << endl; } int main() { int T; cin >> T; for(int i = 0; i < T; i++) { cin >> N >> M; size = N + M + 2; for(int j = 0; j < size; j++) for(int k = 0; k < size; k++) { A[j][k] = 0; G[j][k] = 0; } // setup adjacencies from source to planes for(int j = 2; j < N+2; j++) { A[SOURCE][j] = 1; G[SOURCE][j] = 1; } int time; for(int j = 0; j < M; j++) { // setup adjacencies from attack zones to sink A[N+2+j][SINK] = 1; G[N+2+j][SINK] = 1; // setup adjacencies from planes to attack zones for(int k = 0; k < N; k++) { cin >> time; A[k+2][N+2+j] = time; G[k+2][N+2+j] = time; } } if(N < M) cout << "Not enough planes, we're going to be MTV slaves." << endl; else { maxflow(); traceback(); } } return 0; }