请教 "Problem 1591 锦标赛"

0
0

我用如下方法建的图:

因为每个人必须打n-1场, 所以根据每个人最后的得分以及小Ho已经看过的比赛可以得出每一个选手还要赢几场以及还要输几场.

把每个选手拆成两个点, 假设选手id为i, 那么左边的点为i, 与源点s相连, 每条边的容量为还要赢的场数, 费用为0; 右边的点为i + n, 与汇点t相连, 每条边的容量为还要输的场数, 费用为0.

中间的边, 假设选手 i 与选手j (i != j)小Ho还没看到他们打过(就是(i, j) 而且(j, i)没出现过), 那么就建两条边, 一条 i 到j + n, 容量为1, 费用为B[i][j], 另一条 j 到 i + n, 容量为1, 费用为B[j][i]; 假设小Ho已经看他们打过了, 那么就不用建边, 并根据胜负关系把B[i][j或者B[j][i]加到最后的Ans里.

然后对图跑一遍最大费用最大流, 加上之前的Ans就是最后的答案.

然而一直WA. 请问这种方法错在哪里?

代码如下:

#include <bits/stdc++.h>

using namespace std;

const int INF = 0x7f7f7f7f;
const int N = 105;
const int M = N * N * 10;

struct Edge {
  int u, v, cap, c, nxt;

  Edge() {}
  Edge(const int& _u, const int& _v, const int& _cap, const int& _c, const int& _nxt) : u(_u), v(_v), cap(_cap), c(_c), nxt(_nxt) {}
} es[M];

int head[N + N], tot;

int n, k;
bool vis[N][N];
int sc[N], battle[N], B[N][N];
int dp[N + N], from[N + N];

void addEdge(const int& u, const int& v, const int& cap, const int& c) {
  es[tot] = Edge(u, v, cap, c, head[u]);
  head[u] = tot++;
}

void addDoubleEdge(const int& u, const int& v, const int& cap, const int& c) {
  addEdge(u, v, cap, c);
  addEdge(v, u, 0, -c);
}

int spfa(const int &s, const int &t) {
  deque <int> de;
  for (int i = s; i <= t; ++i)
    dp[i] = -INF;
  dp[s] = 0;
  de.push_back(s);
  while (!de.empty()) {
    int u = de.front();
    de.pop_front();
    for (int i = head[u]; i != -1; i = es[i].nxt) {
      if (es[i].cap == 0)
        continue;
      int v = es[i].v;
      if (dp[u] + es[i].c > dp[v]) {
        dp[v] = dp[u] + es[i].c;
        from[v] = i;
        de.push_back(v);
      }
    }
  }
  if (dp[t] == -INF)
    return -INF;
  int temp = t, ret = 0;
  while (temp != s) {
    int id = from[temp];
    es[id].cap -= 1;
    es[id ^ 1].cap += 1;
    ret += es[id].c;
    temp = es[id].u;
  }
  return ret;
}

int mfmc(const int& s, const int &t) {
  int ret = 0;
  while (true) {
    int temp = spfa(s, t);
    if (temp == -INF)
      break;
    ret += temp;
  }
  return ret;
}

int main()
{
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i)
    scanf("%d", &sc[i]);
  scanf("%d", &k);
  for (int i = 0; i < k; ++i) {
    int a, b;
    scanf("%d %d", &a, &b);
    ++battle[a]; ++battle[b];
    --sc[a];
    vis[a][b] = true;
  }
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= n; ++j)
      scanf("%d", &B[i][j]);
  int s = 0, t = n + n + 1;
  memset(head, -1, sizeof(head));
  tot = 0;
  for (int i = 1; i <= n; ++i) {
    if (sc[i] != 0)
      addDoubleEdge(s, i, sc[i], 0);
    if (n - 1 - battle[i] - sc[i] != 0)
      addDoubleEdge(i + n, t, n - 1 - battle[i] - sc[i], 0);
  }
  int ans = 0;
  for (int i = 1; i <= n; ++i) {
    for (int j = i + 1; j <= n; ++j) {
      if (vis[i][j] || vis[j][i])
        ans += vis[i][j] ? B[i][j] : B[j][i];
      else {
        addDoubleEdge(i, j + n, 1, B[i][j]);
        addDoubleEdge(j, i + n, 1, B[j][i]);
      }
    }
  }
  printf("%d\n", mfmc(s, t) + ans);
  return 0;
}

1 answer(s)

0

建模,每场比赛实际上只有流量1才对。你那样建边,可能一场比赛走了两次吧。

write answer 切换为英文 切换为中文


转发分享