#include <bits/stdc++.h>

///    ____ ____ ____ ____ ____
///   ||a |||t |||o |||d |||o ||
///   ||__|||__|||__|||__|||__||
///   |/__\|/__\|/__\|/__\|/__\|
///

using namespace std;

const int N_MAX = 302;
const int M_MAX = 302;

int n, m;

int a[N_MAX][M_MAX];

bool del[N_MAX];

int pos[N_MAX];

int cnt[M_MAX];

queue <int> q;

bool f (int mx)
{
    for(int i = 1; i <= m; i++)
    {
        del[i] = false;
        cnt[i] = 0;
    }
    while(!q.empty())
        q.pop();
    for(int i = 1; i <= n; i++)
    {
        pos[i] = 1;
        if(++cnt[a[i][1]] == mx + 1)
            q.push(a[i][1]);
    }
    while(!q.empty())
    {
        int f = q.front();
        q.pop();
        del[f] = true;
        for(int i = 1; i <= n; i++)
            if(del[a[i][pos[i]]] == true)
            {
                while(pos[i] <= m && del[a[i][pos[i]]] == true)
                    pos[i]++;
                if(pos[i] == m + 1)
                    return false;
                if(++cnt[a[i][pos[i]]] == mx + 1)
                    q.push(a[i][pos[i]]);
            }
    }
    return true;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> a[i][j];
    int l = 1, r = n;
    while(l < r)
    {
        int mid = ((l + r) >> 1);
        if(f(mid) == true)
            r = mid;
        else
            l = mid + 1;
    }
    cout << l << "\n";
    return 0;
}

#include <cstdio>           // printf(), scanf()
#include <vector>
#include <algorithm>        // sort()

using namespace std;
static const int MAX_E = 100000;

class disjointSet
{
public:
	vector<int> rank;
	vector<int> par;

	disjointSet() {}
	disjointSet(int size)
	{
		rank.resize(size, 0);
		par.resize(size, 0);
		for (int i = 0; i < size; ++i)
		{
			par[i] = i;
			rank[i] = 0;
		}
	}

	int find(int x)
	{
		if (x != par[x])
			par[x] = find(par[x]);

		return par[x];
	}

	void unite(int x, int y)
	{
		x = find(x);
		y = find(y);
		if (x == y)
			return;

		if (rank[x] < rank[y])
			par[x] = y;
		else
		{
			par[y] = x;
			if (rank[x] == rank[y])
				rank[x]++;
		}
	}

	bool same(int x, int y)
	{
		return find(x) == find(y);
	}
};

struct edge
{
	int from, to;
	int cost;
};

edge es[MAX_E];
int V, E;

bool
cmp_func(const edge &e1, const edge &e2)
{
	return e1.cost < e2.cost;
}

int
kruskal()
{
	sort(es, es + E, cmp_func);
	disjointSet dset = disjointSet(V);
	int res = 0;

	for (int i = 0; i < E; ++i)
	{
		edge e = es[i];
		if (!dset.same(e.from, e.to))
		{
			dset.unite(e.from, e.to);
			res += e.cost;
		}
	}

	return res;
}

int
main(int argc, char** argv)
{
	edge e;
	int s, t, w;
	int i;

	scanf("%d %d", &V, &E);
	for (i = 0; i < E; ++i)
	{
		scanf("%d %d %d", &s, &t, &w);
		e.from = s;
		e.to = t;
		e.cost = w;
		es[i] = e;
	}

	printf("%d\n", kruskal());
	return 0;
}