SPOJ AMR12A The Black Riders(二分+二分匹配)

有n个人,m个洞。每个洞能容纳一个人,每个人到每个洞需要花费一些时间。每个人到达一个洞后可以花C的时间来挖洞,这样该洞的容量就变成2了。求能使至少K个人进洞的最短时间。

还是将求极值问题转化为判定问题。二分枚举时间,能否在time的时间内让至少K个人进洞?求进洞人数当然用二分匹配做。g[i][j]为第i个人到第j个洞的时间。如果g[i][j] <= time,那么由i向j连边。挖洞的情况呢?如果g[i][j] + C <= time的话,由i向j和j+m分别连边,这样就能保证在时间够的情况下改变洞的容量了。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<fstream>
#include<sstream>
#include<cstdlib>
#include<vector>
#include<string>
#include<cstdio>
#include<bitset>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<set>
#define FF(i, a, b) for(int i=a; i<b; i++)
#define FD(i, a, b) for(int i=a; i>=b; i--)
#define REP(i, n) for(int i=0; i<n; i++)
#define CLR(a, b) memset(a, b, sizeof(a))
#define debug puts("**debug**")
#define LL long long
#define PB push_back
#define MP make_pair
using namespace std;

const int maxn = 222;
int n, m, K, C, g[maxn][maxn];

struct BPM
{
  int n, m;               // 左右顶点个数
  int G[maxn][maxn];      // 邻接表
  int left[maxn];         // left[i]为右边第i个点的匹配点编号,-1表示不存在
  bool T[maxn];           // T[i]为右边第i个点是否已标记

  void init(int n, int m) {
    this->n = n;
    this->m = m;
    memset(G, 0, sizeof(G));
  }

  inline void add(int u, int v)
  {
      G[u][v] = 1;
  }

  bool match(int u){
    for(int v = 0; v < m; v++) if(G[u][v] && !T[v]) {
      T[v] = true;
      if (left[v] == -1 || match(left[v])){
        left[v] = u;
        return true;
      }
    }
    return false;
  }
  // 求最大匹配
  int solve() {
    memset(left, -1, sizeof(left));
    int ans = 0;
    for(int u = 0; u < n; u++) { // 从左边结点u开始增广
      memset(T, 0, sizeof(T));
      if(match(u)) ans++;
    }
    return ans;
  }
}solver;

bool ok(int time)
{
    solver.init(n, m*2);
    REP(i, n) REP(j, m)
    {
        if(g[i][j] <= time) solver.add(i, j);
        if(g[i][j] + C <= time) solver.add(i, j+m);
    }
    return solver.solve() >= K;
}

int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        int L = 0, R = -1, M, ans;
        scanf("%d%d%d%d", &n, &m, &K, &C);
        REP(i, n) REP(j, m)
        {
            scanf("%d", &g[i][j]);
            R = max(R, g[i][j]);
        }
        while(L <= R)
        {
            M = (L + R) >> 1;
            if(ok(M)) ans = M, R = M - 1;
            else L = M + 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值