/*
题意:k台机器和c头牛分别在自己的点,输入为路径,c头牛要到k台机器挤奶,每台机器最多挤m头牛,求最短
的最远行走距离。
题解:最大流+二分+floyd;
题目求的是每次行走路程中每头牛行走的最远距离,因此直接在最开始的时候用floyd求出从一个点到另一个点
的最短距离,这样就保证了牛走的都是最短距离,然后根据求出的最短路径来建图,并且用二分的方法把大于值
mid的边全部去掉后,判断是否能求出最大流使得所有牛都被挤过一次,源点为0,连接每台机器,权值为m,每
头牛连接汇点k+c+1,权值为1。
*/
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
#define EMAX 15000
#define VMAX 400
const int INF = 0xfffffff;
int pre[VMAX];
int map[VMAX][VMAX],nmap[VMAX][VMAX];
void floyd(int n)
{
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
if (INF != map[j][i])
for(int k=1; k<=n; k++)
if (map[j][i] + map[i][k] < map[j][k])
map[j][k] = map[j][i] + map[i][k];
}
void build(int k, int c, int m, int val)
{
memset(nmap,0,sizeof(nmap));
for(int i=1; i<=k; i++)
nmap[0][i] = m;
for(int i=1; i<=c; i++)
nmap[k+i][k+c+1] = 1;
for(int i=1; i<=k; i++)
for(int j=1; j<=c; j++)
if (map[i][k+j] <= val)
nmap[i][k+j] = 1;
}
int flow_ek(int s, int t, int n)
{
queue<int> Q;
int ret = 0;
while (true)
{
memset(pre,-1,sizeof(pre));
while (!Q.empty())
Q.pop();
Q.push(s);
while (!Q.empty())//BFS
{
int u = Q.front();
Q.pop();
for(int v=1; v<=n; v++)
{
if (nmap[u][v] && pre[v] == -1)
{
pre[v] = u;
Q.push(v);
}
}
if (pre[t] != -1)//终点的pre不为-1,表示找到一条增广路径
break;
}
if (pre[t] == -1)//当BFS后找不到增广路径则结束循环
break;
int mw = -1;
for(int v=t; v!=s; v=pre[v])//找出当前路径中的流量的最小容量
{
if (mw == -1 || mw > nmap[pre[v]][v])
mw = nmap[pre[v]][v];
}
for(int v=t; v!=s; v=pre[v])//修改路径的容量,求出残余网络
{
nmap[pre[v]][v] -= mw;
nmap[v][pre[v]] += mw;//更新逆向边
}
ret += mw;
}
return ret;
}
int bin(int k, int c, int m, int n)//二分
{
int left = 0, right = 40000,mid;
while (left < right)
{
mid = (left+right)/2;
build(k,c,m,mid);
if (c == flow_ek(0,k+c+1,n))
right = mid;
else
left = mid+1;
}
return right;
}
int main(void)
{
int k,c,m;
while (cin >> k >> c >> m)
{
for(int i=1; i<=k+c; i++)
for(int j=1; j<=k+c; j++)
{
cin >> map[i][j];
if (i != j && 0==map[i][j])
map[i][j] = INF;
}
floyd(k+c);
cout << bin(k,c,m,k+c+2) << endl;
}
return 0;
}