#include <bits/stdc++.h>
using namespace std;
const int N=100+5,INF=1<<30;
int n;
int edge[N][N];//邻接矩阵存图(边的权值)
int ex_left[N],ex_right[N];//左右的期望值(若要配对,左右的期望值之和应该等于权值)
bool vis_left[N],vis_right[N];//每一轮参加过匹配
int match[N];//记录右边与左边的配对
int min_change;//若右边要跟左边配对需要多少期望值
bool dfs(int left)
{
vis_left[left]=true;//参加匹配
for(int right=1;right<=n;++right){
if(vis_right[right]||!edge[left][right]) continue;
int gap=ex_left[left]+ex_right[right]-edge[left][right];//两边期望值相加减去权值
if(!gap) {
vis_right[right]=true;//参加过了
if(!match[right]||dfs(match[right])){//若右边与左边没有配对或左边那位有了新的配对
match[right]=left;//可以配上
return true;
}
}
else min_change=min(min_change,gap);//若右边与左边配对,要多少期盼值
}
return false;
}
int KM()
{
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
ex_left[i]=max(ex_left[i],edge[i][j]);//左边的期望值取与之相连的最大,右边的期望值为0
for(int i=1;i<=n;++i)//为左边匹配右边(一轮)
{
min_change=INF;
while(true)
{
memset(vis_left,0,sizeof(vis_left));
memset(vis_right,0,sizeof(vis_right));
if(dfs(i)) break;//若找到了退出,继续下一轮
//若没找到,左边降低期望值
for(int j=1;j<=n;++j){
if(vis_left[j]) ex_left[j]-=min_change;//左边参加过的降低期盼值
if(vis_right[j]) ex_right[j]+=min_change;//右边参加过的加上最小期盼值
}
}
}
int res=0;
for(int i=1;i<=n;++i)
res+=edge[match[i]][i];//加上
return res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&edge[i][j]);//输入权值
int ans1=KM();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
edge[i][j]=-edge[i][j];
for(int i=1;i<=n;++i)
ex_left[i]=-INF,
ex_right[i]=match[i]=0;
printf("%d\n%d",-KM(),ans1);
return 0;
}