题目链接

floyd+网络流+二分答案

二分枚举这个最小值,通过最大流判断是否满足条件。

建图:
源点s和每一个牛连一条边,容量为1(一头牛), 每一个机器和汇点t连一条边,容量为M
牛与机器之间通过两点之间的最短路是否小于或者等于二分枚举的距离,是的话连一条边,容量为1, 最后最大流算法求出最大流是否等于牛的总数,是的话说明距离能满足条件。

两点之间最短路用floyd。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int Maxn = 300+10;
const int INF = 0x3f3f3f3f;

struct Edge {
	int v, cap, flow, next;
} edge[Maxn*Maxn];

int h[Maxn], edge_cnt, G[Maxn][Maxn];
bool vis[Maxn];

void add (int u, int v, int c) {
	edge[edge_cnt].v = v;
	edge[edge_cnt].cap = c;
	edge[edge_cnt].flow = 0;
	edge[edge_cnt].next = h[u];
	h[u] = edge_cnt++;

	edge[edge_cnt].v = u;
	edge[edge_cnt].cap = 0;
	edge[edge_cnt].flow = 0;
	edge[edge_cnt].next = h[v];
	h[v] = edge_cnt++;
}

int dfs(int cur, int t, int f) {
	if(cur == t) return f;
	for(int i = h[cur]; i != -1; i = edge[i].next) {
		Edge &e = edge[i];
		if(!vis[e.v] && e.cap > e.flow) {
			vis[e.v] = true;
			int flow = dfs(e.v, t, min(f, e.cap-e.flow));
			if(flow > 0) {
				e.flow += flow;
				edge[i^1].flow -= flow;
				return flow;
			}
		}
	}
	return 0;
}

int ford(int s, int t) {
	int ans = 0;
	while(1) {
		memset(vis, false, sizeof(vis));
		vis[s] = true;
		int flow = dfs(s, t, INF);
		if(flow == 0) return ans;
		ans += flow;
	}
}

void floyd(int n) {
    for(int k = 1; k <= n; ++k) {
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= n; ++j)
                if(G[i][k] < INF && G[k][j] < INF) G[i][j] = min(G[i][j], G[i][k]+G[k][j]);
        }
    }
}

bool ok(int dis, int K, int C, int M) {
    memset(h, -1, sizeof(h));
    edge_cnt = 0;
    for(int u = K+1; u <= K+C; ++u) {
        add(0, u, 1);
        for(int v = 1; v <= K; ++v) {
            if(u == K+1) add(v, K+C+1, M);
            if(G[u][v] < INF && G[u][v] <= dis) add(u, v, 1);
        }
    }
    if(ford(0, K+C+1) == C) return true;
    else return false;
}

int main(void)
{
	int K, C, M;
	scanf("%d%d%d", &K, &C, &M);
	for(int i = 1; i <= K+C; ++i) {
        for(int j = 1; j <= K+C; ++j) {
            scanf("%d", &G[i][j]);
            if(G[i][j] == 0) G[i][j] = INF;
        }
	}
	floyd(K+C);

	int L = 0, R = INF, mid;
	while(L < R) {
        mid = (L+R)/2;
        if(ok(mid, K, C, M)) R = mid;
        else L = mid+1;
	}
	printf("%d\n", R);
	return 0;
}