//poj 2112 Optimal Milking
// floyd + 二分 + 最大流
//题意:第一行给出K,C,M。
//分别表示挤奶机台数K,奶牛数C,没台挤奶机一天能处理的奶牛数
//接下去是(K+C) * (K+C) 的矩阵,表示各个点之间的距离
//0表示不能到达。奶牛走到机器挤奶,求每头牛都要被处理所走的路最少的情况下,
//每头牛到机器距离中最长的边是多少。记得每台机器都有限制奶牛数
//思路:
//先用floyd 求出各个点之间的最小路,找出所有路径中最短和最长的边
//对最长路径进行二分,后建立source连接奶牛,容量为1;
//建sink,让机器连向sink,容量为机器一天所能处理奶牛的数量;
//奶牛向机器连边,容量为INF
//注意:
//奶牛和机器之间的边可以多次使用
//建的图是有向图,只能从奶牛指向机器有容量INF,机器指向奶牛容量为0
//2 2 1
//0 0 1 3
//0 0 2 100
//1 2 0 0
//3 100 0 0
#define infile freopen("in.txt", "r", stdin);
#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
const int INF = 1<<30;
const int N = 255;
int n, n_mach, n_cow, per_flow, eid;
int map[N][N], head[N], level[N];
struct EDGE
{
int ed, next, cap;
}edge[2*N*N];
void floyd() //floyd求各个点之间的最短路
{
for(int k = 1; k <= n; ++k)
{
for(int i = 1; i <= n; ++i)
{
if(i != k)
{
for(int j = 1; j <= n; ++j)
{
if(j != k && j != i)
{
if(map[i][j]-map[i][k] > map[k][j])
{
map[i][j] = map[i][k] + map[k][j];
}
}
}
}
}
}
}
void add_edge(int st, int ed, int cap)
{
edge[eid].ed = ed, edge[eid].cap = cap;
edge[eid].next = head[st], head[st] = eid++;
edge[eid].ed = st, edge[eid].cap = 0; //只能从牛到机器,是有向边
edge[eid].next = head[ed], head[ed] = eid++;
}
bool bfs()
{
memset(level, -1, sizeof(level));
queue<int>que;
que.push(0);
level[0] = 1;
while(!que.empty())
{
int now = que.front();
que.pop();
for(int i = head[now]; i != -1; i = edge[i].next)
{
int ed = edge[i].ed;
if(edge[i].cap > 0 && level[ed] == -1)
{
level[ed] = level[now] + 1;
que.push(ed);
}
}
}
if(level[n+1] == -1)
return false;
return true;
}
int dfs(int now, int flow)
{
if(now == n+1)
return flow;
int tmp = 0; //tmp为从now流出去的流量
for(int i = head[now]; i != -1; i = edge[i].next)
{
int m = min(edge[i].cap, flow - tmp);
int ed = edge[i].ed, f;
if(edge[i].cap > 0 && level[now]+1 == level[ed] && flow > tmp &&
(f = dfs(ed, m)))
{
edge[i].cap -= f;
edge[i^1].cap += f;
tmp += f;
}
}
if(tmp == 0)
level[now] = -1;
return tmp;
}
int dinic()
{
int flow = 0;
while(bfs())
flow += dfs(0, INF);
return flow;
}
void binarySearch()
{
int high = 0, low = INF;
for(int i = 1; i <= n; ++i)
{
for(int j = i+1; j <= n; ++j)
{
low = map[i][j] < low ? map[i][j] : low;
if(map[i][j] != INF)
high = map[i][j] > high ? map[i][j] : high;
}
}
while(low < high)
{
eid = 0;
memset(head, -1, sizeof(head));
int mid = low + (high-low)/2;
for(int i = n_mach+1; i <= n; ++i)
{
add_edge(0, i, 1); //source为0,连到每头牛,容量为1
for(int j = 1; j <= n_mach; ++j)
{ //注意:这里只能从奶牛走向机器,是单向边
if(map[i][j] <= mid)
{ //这里奶牛到机器的容量是INF,表示奶牛和机器之间的边可以用不止一次
add_edge(i, j, INF);
// printf("%d->%d\n", i, j);
}
}
}
for(int i = 1; i <= n_mach; ++i)//sink为n+1,每台机器连向sink,
add_edge(i, n+1, per_flow); //容量为每台机器一天所能处理的奶牛数
int flow = dinic();
if(flow < n_cow)
low = mid + 1;
else
high = mid;
}
printf("%d\n", low);
}
int main()
{
//infile
while(scanf("%d%d%d", &n_mach, &n_cow, &per_flow) != EOF)
{
n = n_mach + n_cow;
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
{
scanf("%d", &map[i][j]);
map[i][j] = map[i][j] == 0 ? INF : map[i][j];
}
}
floyd();
binarySearch();
}
return 0;
}