[题解] LuoguP3705 [SDOI2017]新生舞会

https://www.luogu.com.cn/problem/P3705

挺套路一题......

把他想象成一个(n imes n)的矩阵,然后每行每列只能选一个数,让你最大化选出的(a)的和与(b)的和的商,也就是

[C = frac{sum a}{sum b} ]

然后就是非常套路的分数规划.....

先二分一个(mid),那么

[frac{sum a}{sum b} ge mid ]

[sum a ge midsum b ]

[sum a-mid imes b ge 0 ]

然后把位置((i,j))的权值改成(a_{i,j}-mid*b_{i,j})

问题变为在这个矩阵中,每行每列只能选一个数,问选出来的数的和最大是多少。

建一张二分图,(X)节点表示行,(Y)节点表示列,对于(a_{i,j})((i,j))的权值),连一条(X_i ightarrow Y_j),权值为(a_{i,j})的边。

然后判断这张图的最大全完美匹配是不是大于(0)就好了。

不会KM ==...然后就写了又长又慢的费用流...

另外二分的bound不要瞎开...容易T...

#include <bits/stdc++.h>

using namespace std;

typedef double db;
const db INF = 1e9;

template <typename cap_t, typename cost_t>
struct MCMF {
  static const int E = 1e5 + 10, V = 1e5 + 10;
  
  struct edge {
    int from, to, nxt;
    cap_t cap, flow;
    cost_t cost;
  } e[E << 1];
  int fst[V], n, cnt, s, t;
  
  void init(int S, int T, int sz) {
    n = sz, cnt = 1;
    s = S, t = T;
    for (int i = 0; i <= n; i++) fst[i] = 0;
  }
  
  void ade(int u, int v, cap_t w, cost_t c) {
    e[++cnt] = {u, v, fst[u], w, 0, c};
    fst[u] = cnt;
  }
  void addedge(int u, int v, cap_t w, cost_t c) {
    ade(u, v, w, c), ade(v, u, 0, -c);
  }
  
  cap_t incf[V]; cost_t d[V];
  int pre[V]; bool vis[V];
  
  bool spfa() {
    for (int i = 0; i <= n; i++) {
      incf[i] = (cap_t) INF, d[i] = (cost_t) INF;
      vis[i] = 0, pre[i] = 0;
    }
    d[s] = 0, vis[s] = 1;
    vector<int> q;
    q.push_back(s);
    for (int cur = 0; cur < q.size(); cur++) {
      int u = q[cur];
      vis[u] = 0;
      for (int i = fst[u]; i; i = e[i].nxt) {
        int v = e[i].to; cost_t w = e[i].cost;
        if (d[v] > d[u] + w && e[i].cap > e[i].flow) {
          d[v] = d[u] + w;
          pre[v] = i;
          incf[v] = min(e[i].cap - e[i].flow, incf[u]);
          if (!vis[v]) vis[v] = 1, q.push_back(v);
        }
      }
    }
    return d[t] < (cost_t) INF;
  }
  
  cap_t maxflow; cost_t mincost;
  
  void augment() {
    int u = t;
    for (; u != s; u = e[pre[u]].from) {
      e[pre[u]].flow += incf[t];
      e[pre[u]^1].flow -= incf[t];
    }
    maxflow += incf[t], mincost += incf[t] * d[t];
  }
  
  pair<cap_t, cost_t> mcmf() {
    maxflow = mincost = 0;
    while (spfa()) augment();
    return make_pair(maxflow, mincost);
  }
};

MCMF<int, db> g;

const db eps = 1e-7;
const int N = 110;

int a[N][N], b[N][N], n;

bool chk(db mid) {
  int s = 0, t = n*2 + 1;
  g.init(s, t, n*2 + 2);
  for (int i = 1; i <= n; i++) g.addedge(s, i, 1, 0);
  for (int i = 1; i <= n; i++) g.addedge(i + n, t, 1, 0);
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++) g.addedge(i, j+n, 1, b[i][j]*mid - a[i][j]);
  auto ans = g.mcmf();
  return -ans.second >= 0;
}

int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) 
    for (int j = 1; j <= n; j++) scanf("%d", &a[i][j]);
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++) scanf("%d", &b[i][j]);
  db l = 0, r = 1e4;
  while (l + eps < r) {
    db mid = (l+r) / 2;
    if (chk(mid)) l = mid;
    else r = mid;
  }
  printf("%.6f
", l);
  return 0;
}

原文地址:https://www.cnblogs.com/wxq1229/p/12813383.html