UVA11419 SAM I AM

UVA11419 SAM I AM

给定一个 \(R\times C\) 的矩阵中的 \(N\) 个点,求最少选取多少个行或列才能使得每个给出的点都被一行或一列覆盖,输出方案

\(R,\ C\leq10^3,\ N\leq10^6\)

网络流


易知原题即为建出二分图后跑最小点覆盖求方案

这里只是记录一下如何求方案……

找出从原点开始,只经过没被使用的边,构成的连通块

如果左侧节点没被包含在连通块中,输出方案

如果右侧节点被包含在连通块中,输出方案

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 2010, inf = INT_MAX;
int R, C, N;
bool vis[maxn], isv[maxn];
int S, T, cnt, h[maxn], cur[maxn], dis[maxn];

struct edges {
  int nxt, to, w;
  edges(int x = 0, int y = 0, int z = 0) :
    nxt(x), to(y), w(z) {}
} e[1010010];

void addline(int u, int v, int w) {
  e[++cnt] = edges(h[u], v, w), h[u] = cnt;
  e[++cnt] = edges(h[v], u, 0), h[v] = cnt;
}

bool bfs() {
  static int Q[maxn];
  memcpy(cur, h, sizeof h);
  memset(dis, 0, sizeof dis);
  int l = 1, r = 1;
  Q[1] = S, dis[S] = 1;
  while (l <= r) {
    int u = Q[l++];
    for (int i = h[u]; i; i = e[i].nxt) {
      int v = e[i].to;
      if (e[i].w && !dis[v]) {
        Q[++r] = v, dis[v] = dis[u] + 1;
        if (v == T) return 1;
      }
    }
  }
  return 0;
}

int dfs(int u, int f) {
  if (u == T || !f) {
    return f;
  }
  int res = 0, tmp;
  for (int& i = cur[u]; i && f; i = e[i].nxt) {
    int v = e[i].to;
    if (e[i].w && dis[v] == dis[u] + 1) {
      if (!(tmp = dfs(v, min(f, e[i].w)))) {
        dis[v] = 0; continue;
      }
      res += tmp, f -= tmp, e[i].w -= tmp, e[i ^ 1].w += tmp;
    }
  }
  return res;
}

int dinic() {
  int res = 0;
  while (bfs()) {
    res += dfs(S, inf);
  }
  return res;
}

void find(int u) {
  vis[u] = 1;
  for (int i = h[u]; i; i = e[i].nxt) {
    int v = e[i].to;
    if (e[i].w && !vis[v]) find(v);
  }
}

void solve() {
  cnt = 1;
  memset(h, 0, sizeof h);
  memset(isv, 0, sizeof isv);
  memset(vis, 0, sizeof vis);
  S = R + C + 1, T = S + 1;
  for (int i = 1; i <= R; i++) {
    addline(S, i, 1);
  }
  for (int i = 1; i <= C; i++) {
    addline(R + i, T, 1);
  }
  for (int i = 1, x, y; i <= N; i++) {
    scanf("%d %d", &x, &y);
    isv[x] = isv[R + y] = 1;
    addline(x, R + y, 1);
  }
  printf("%d", dinic());
  find(S);
  for (int i = 1; i <= R; i++) {
    if (isv[i] && !vis[i]) printf(" r%d", i);
  }
  for (int i = R + 1; i <= R + C; i++) {
    if (isv[i] && vis[i]) printf(" c%d", i - R);
  }
  putchar(10);
}

int main() {
  while (~scanf("%d %d %d", &R, &C, &N) && R && C && N) {
    solve();
  }
  return 0;
}

转载于:https://www.cnblogs.com/Juanzhang/p/10693095.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值