Friday, August 5, 2016

UVa 10804 - Gopher Strategy

Accepted date: 2016-08-04
Run Time: 0.390
Ranking (as of 2016-08-04): 100 out of 406
Language: C++

/*
  UVa 10804 - Gopher Strategy

  To build using Visual Studio 2008:
    cl -EHsc -O2 UVa_10804_Gopher_Strategy.cpp
*/

#include <algorithm>
#include <vector>
#include <queue>
#include <utility>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;

const int m_max = 50, n_max = 50;

pair<double, double> gophers[m_max], holes[n_max];
double distances[m_max][n_max], unique_distances[m_max * n_max];

double euclidean_distance(const pair<double, double>& p, const pair<double, double>& q)
{
  double dx = p.first - q.first, dy = p.second - q.second;
  return sqrt(dx * dx + dy * dy);
}

struct edge {
  int v_; // neighboring vertex
  int capacity_; // capacity of edge
  int flow_; // flow through edge
  int residual_; // residual capacity of edge

  edge(int v, int capacity, int residual)
    : v_(v), capacity_(capacity), flow_(0), residual_(residual) {}
};

struct vertex_state {
  bool discovered_;
  int parent_;

  vertex_state() : discovered_(false), parent_(-1) {}
};

void bfs(const vector< vector<edge> >& graph, int start,
  vector<vertex_state>& states)
{
  queue<int> q;
  states[start].discovered_ = true;
  q.push(start);
  while (!q.empty()) {
    int u = q.front(); q.pop();
    for (int i = 0; i < graph[u].size(); i++) {
      const edge& e = graph[u][i];
      if (e.residual_ > 0 && !states[e.v_].discovered_) {
        states[e.v_].discovered_ = true;
        states[e.v_].parent_ = u;
        q.push(e.v_);
      }
    }
  }
}

edge& find_edge(vector< vector<edge> >& graph, int u, int v)
{
  int i;
  for (i = 0; i < graph[u].size(); i++)
    if (graph[u][i].v_ == v)
      break;
  return graph[u][i];
}

int path_volume(vector< vector<edge> >& graph, int start, int end,
  const vector<vertex_state>& states)
{
  if (states[end].parent_ == -1)
    return 0;
  edge& e = find_edge(graph, states[end].parent_, end);
  if (start == states[end].parent_)
    return e.residual_;
  else
    return min(path_volume(graph, start, states[end].parent_, states), e.residual_);
}

void augment_path(vector< vector<edge> >& graph, int start, int end,
  const vector<vertex_state>& states, int volume)
{
  if (start == end)
    return;
  edge& e = find_edge(graph, states[end].parent_, end);
  if (e.flow_ < e.capacity_)
    e.flow_ += volume;
  if (e.residual_)
    e.residual_ -= volume;
  edge& r= find_edge(graph, end, states[end].parent_);
  if (r.flow_)
    r.flow_ -= volume;
  if (r.residual_ < r.capacity_)
    r.residual_ += volume;
  augment_path(graph, start, states[end].parent_, states, volume);
}

void netflow(vector< vector<edge> >& graph, int source, int sink)
{
  while (true) {
    vector<vertex_state> states(graph.size());
    bfs(graph, source, states);
    int volume = path_volume(graph, source, sink, states);
      // calculate the volume of augmenting path
    if (volume > 0)
      augment_path(graph, source, sink, states, volume);
    else
      break;
  }
}

int total_flow(const vector< vector<edge> >& graph, int source)
{
  int flow = 0;
  const vector<edge>& edges = graph[source];
  for (int i = 0, e = edges.size(); i < e; i++)
    flow += edges[i].flow_;
  return flow;
}

int get_gophers(vector< vector<edge> >& graph, int nr_gophers, int nr_holes,
  int source, int sink, double d)
{
  vector<edge>& gs =graph[source];
  for (int i = 0; i < nr_gophers; i++) {
    gs[i].capacity_ = gs[i].residual_ = 1, gs[i].flow_ = 0;
    vector<edge>& gg = graph[i];
    for (size_t j = 0; j < gg.size(); j++) {
      if (gg[j].v_ == source)
        gg[j].capacity_ = 1, gg[j].residual_ = gg[j].flow_ = 0;
      else {
        if (distances[i][gg[j].v_ - nr_gophers] <= d)
          gg[j].capacity_ = gg[j].residual_ = 1, gg[j].flow_ = 0;
        else
          gg[j].capacity_ = gg[j].residual_ = gg[j].flow_ = 0;
      }
    }
  }
  for (int i = nr_gophers; i < nr_gophers + nr_holes; i++) {
    vector<edge>& gh = graph[i];
    for (size_t j = 0; j < gh.size(); j++) {
      if (gh[j].v_ == sink)
        gh[j].capacity_ = gh[j].residual_ = 1, gh[j].flow_ = 0;
      else {
        if (distances[gh[j].v_][i - nr_gophers] <= d)
          gh[j].capacity_ = 1, gh[j].residual_ = gh[j].flow_ = 0;
        else
          gh[j].capacity_ = gh[j].residual_ = gh[j].flow_ = 0;
      }
    }
  }
  vector<edge>& gt = graph[sink];
  for (int i = 0; i < nr_holes; i++)
    gt[i].capacity_ = 1, gt[i].residual_ = gt[i].flow_ = 0;
  netflow(graph, source, sink); // apply Ford-Fulkerson's augmenting path algorithm
  int flow = total_flow(graph, source);
#ifdef DEBUG
  printf("%lf %d\n", d, flow);
#endif
  return flow;
}

int main()
{
  int N;
  scanf("%d", &N);
  for (int cn = 1; cn <= N; cn++) {
    int m, n, k;
    scanf("%d %d %d", &m, &n, &k);
    for (int i = 0; i < m; i++)
      scanf("%lf %lf", &gophers[i].first, &gophers[i].second);
    for (int i = 0; i < n; i++)
      scanf("%lf %lf", &holes[i].first, &holes[i].second);
    if (!m || m == k) {
      printf("Case #%d:\n%.3lf\n\n", cn, 0.0);
      continue;
    }
    if (n < m - k) {
      printf("Case #%d:\nToo bad.\n\n", cn);
      continue;
    }
    for (int i = 0; i < m; i++)
      for (int j = 0; j < n; j++)
        distances[i][j] = unique_distances[i * m + j] = euclidean_distance(gophers[i], holes[j]);
    int nr_unique_distances = m * n;
    sort(unique_distances, unique_distances + nr_unique_distances);
    nr_unique_distances =
      unique(unique_distances, unique_distances + nr_unique_distances) - unique_distances;
    int nr_vertices = m + n + 2, source = m + n, sink = m + n + 1;
    // indices are:
    //  0 - (m - 1): gopher vertices, m - (m + n - 1): hole vertices,
    //  (m + n): source vertex, (m + n + 1): sink vertex
    vector< vector<edge> > graph(nr_vertices);
    for (int i = 0; i < m; i++) {
      // append the edges between the source and gopher vertices
      graph[source].push_back(edge(i, 0, 0));
      graph[i].push_back(edge(source, 0, 0));
      for (int j = 0; j < n; j++) {
        // append the edges between gopher vertices and hole vertices
        graph[i].push_back(edge(m + j, 0, 0));
        graph[m + j].push_back(edge(i, 0, 0));
      }
    }
    for (int i = m; i < m + n; i++) {
      // append the edges between hole vertices and the sink
      graph[i].push_back(edge(sink, 1, 1));
      graph[sink].push_back(edge(i, 1, 0));
    }
    int min_di = 0;
    for (int low = 0, high = nr_unique_distances; low < high; ) {
      int mid = (low + high) / 2;
      int result = get_gophers(graph, m, n, source, sink, unique_distances[mid]) - (m - k);
      if (result < 0)
        low = mid + 1;
      else
        min_di = high = mid;
    }
    printf("Case #%d:\n%.3lf\n\n", cn, unique_distances[min_di]);
  }
  return 0;
}

No comments:

Post a Comment