import java.io.*;
import java.util.*;

public class CF387D {
	static class A {
		ArrayList<Integer> list = new ArrayList<>();
		int u, v, d;
	}
	static final int INF = Integer.MAX_VALUE;
	static boolean bfs(A[] aa, int n) {
		ArrayDeque<Integer> q = new ArrayDeque<>();
		for (int u = 1; u <= n; u++)
			if (aa[u].v > 0)
				aa[u].d = INF;
			else {
				aa[u].d = 0;
				q.addLast(u);
			}
		aa[0].d = INF;
		while (!q.isEmpty()) {
			int u = q.removeFirst();
			for (int v : aa[u].list) {
				int w = aa[v].u;
				if (aa[w].d == INF) {
					aa[w].d = aa[u].d + 1;
					if (w == 0)
						return true;
					q.addLast(w);
				}
			}
		}
		return false;
	}
	static boolean dfs(A[] aa, int n, int u) {
		if (u == 0)
			return true;
		for (int v : aa[u].list) {
			int w = aa[v].u;
			if (aa[w].d == aa[u].d + 1 && dfs(aa, n, w)) {
				aa[u].v = v;
				aa[v].u = u;
				return true;
			}
		}
		aa[u].d = INF;
		return false;
	}
	static int matchings(A[] aa, int n) {
		int cnt = 0;
		while (bfs(aa, n))
			for (int u = 1; u <= n; u++)
				if (aa[u].v == 0 && dfs(aa, n, u))
					cnt++;
		return cnt;
	}
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		int n = Integer.parseInt(st.nextToken());
		int m = Integer.parseInt(st.nextToken());
		int[] eu = new int[m];
		int[] ev = new int[m];
		for (int j = 0; j < m; j++) {
			st = new StringTokenizer(br.readLine());
			eu[j] = Integer.parseInt(st.nextToken());
			ev[j] = Integer.parseInt(st.nextToken());
		}
		A[] aa = new A[n + 1];
		int min = m + n * 3;
		for (int ctr = 1; ctr <= n; ctr++) {
			boolean loop = false;
			boolean[] ci = new boolean[n + 1];
			boolean[] co = new boolean[n + 1];
			for (int i = 0; i <= n; i++)
				aa[i] = new A();
			int m_ = 0;
			for (int j = 0; j < m; j++) {
				int u = eu[j];
				int v = ev[j];
				if (u == ctr && v == ctr)
					loop = true;
				else if (u == ctr && v != ctr)
					ci[v] = true;
				else if (u != ctr && v == ctr)
					co[u] = true;
				else {
					aa[u].list.add(v);
					m_++;
				}
			}
			int cnt = loop ? 0 : 1;
			for (int i = 1; i <= n; i++)
				if (i != ctr) {
					if (!ci[i])
						cnt++;
					if (!co[i])
						cnt++;
				}
			int m2 = matchings(aa, n);
			cnt += (m_ - m2) + (n - 1 - m2);
			if (min > cnt)
				min = cnt;
		}
		System.out.println(min);
	}
}


import java.awt.Point;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.InputMismatchException;

public class R227_2_D {
    static ArrayList<Integer>[] graph;
    static int[] right, left;
    static boolean vis[];

    public static boolean dfs(int node) {
        if (vis[node])
            return false;
        vis[node] = true;
        for (int i = 0; i < graph[node].size(); i++) {
            int tmp = graph[node].get(i);
            if (right[tmp] == -1) {
                left[node] = tmp;
                right[tmp] = node;
                return true;
            }
        }
        for (int i = 0; i < graph[node].size(); i++) {
            int tmp = graph[node].get(i);
            if (dfs(right[tmp])) {
                left[node] = tmp;
                right[tmp] = node;
                return true;
            }
        }
        return false;
    }

    public static int getMaxMatch() {
        Arrays.fill(left, -1);
        Arrays.fill(right, -1);
        boolean done = false;
        while (!done) {
            done = true;
            Arrays.fill(vis, false);
            for (int i = 0; i < graph.length; i++) {
                if (left[i] == -1 && dfs(i)) {
                    done = false;
                }
            }
        }
        int res = 0;
        for (int i = 0; i < left.length; i++) {
            res += (left[i] != -1 ? 1 : 0);
        }
        return res;
    }

    public static void main(String[] args) {
        InputReader in = new InputReader(System.in);
        PrintWriter out = new PrintWriter(System.out);
        int V = in.readInt();
        int E = in.readInt();
        Point[] edges = new Point[E];
        for (int i = 0; i < edges.length; i++) {
            edges[i] = new Point(in.readInt() - 1, in.readInt() - 1);
        }
        int best = Integer.MAX_VALUE;
        for (int k = 0; k < V; k++) {
            int n = V - 1;
            graph = new ArrayList[n];
            left = new int[n];
            vis = new boolean[n];
            right = new int[n];
            for (int i = 0; i < graph.length; i++) {
                graph[i] = new ArrayList<Integer>();
            }
            int center = 0;
            for (int i = 0; i < E; i++) {
                if (edges[i].x == k || edges[i].y == k) {
                    center++;
                    continue;
                }
                int src = edges[i].x > k ? edges[i].x - 1 : edges[i].x;
                int dst = edges[i].y > k ? edges[i].y - 1 : edges[i].y;
                graph[src].add(dst);
            }
            int matching = getMaxMatch();
            int addToCenterEdges = 2 * V - 1 - center;
            int removed = E - center - matching;
            int added = n - matching;
            best = Math.min(best, added + removed + addToCenterEdges);
        }
        System.out.println(best);
    }

    static class InputReader {
        private InputStream stream;
        private byte[] buf = new byte[1000];
        private int curChar, numChars;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        private int read() {
            if (numChars == -1)
                throw new InputMismatchException();
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0)
                    return -1;
            }
            return buf[curChar++];
        }

        public int readInt() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public long readLong() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            long res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public String readString() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            StringBuffer res = new StringBuffer();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        private boolean isSpaceChar(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        private String readLine0() {
            StringBuffer buf = new StringBuffer();
            int c = read();
            while (c != '\n' && c != -1) {
                buf.appendCodePoint(c);
                c = read();
            }
            return buf.toString();
        }

        public String readLine() {
            String s = readLine0();
            while (s.trim().length() == 0)
                s = readLine0();
            return s;
        }

        public String readLine(boolean ignoreEmptyLines) {
            if (ignoreEmptyLines)
                return readLine();
            else
                return readLine0();
        }

        public char readCharacter() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            return (char) c;
        }

        public double readDouble() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            double res = 0;
            while (!isSpaceChar(c) && c != '.') {
                if (c == 'e' || c == 'E')
                    return res * Math.pow(10, readInt());
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            }
            if (c == '.') {
                c = read();
                double m = 1;
                while (!isSpaceChar(c)) {
                    if (c == 'e' || c == 'E')
                        return res * Math.pow(10, readInt());
                    if (c < '0' || c > '9')
                        throw new InputMismatchException();
                    m /= 10;
                    res += (c - '0') * m;
                    c = read();
                }
            }
            return res * sgn;
        }
    }
}


import java.io.*;
import java.util.*;

public class CF387D {
	static class A {
		ArrayList<Integer> list = new ArrayList<>();
		int u, v, d;
	}
	static int INF = Integer.MAX_VALUE;
	static boolean bfs(A[] aa, int n) {
		LinkedList<Integer> q = new LinkedList<>();
		for (int u = 1; u <= n; u++)
			if (aa[u].v > 0)
				aa[u].d = INF;
			else {
				aa[u].d = 0;
				q.addLast(u);
			}
		aa[0].d = INF;
		while (!q.isEmpty()) {
			int u = q.removeFirst();
			for (int v : aa[u].list) {
				int w = aa[v].u;
				if (aa[w].d == INF) {
					aa[w].d = aa[u].d + 1;
					if (w == 0)
						return true;
					q.addLast(w);
				}
			}
		}
		return false;
	}
	static boolean dfs(A[] aa, int n, int u) {
		if (u == 0)
			return true;
		for (int v : aa[u].list) {
			int w = aa[v].u;
			if (aa[w].d == aa[u].d + 1 && dfs(aa, n, w)) {
				aa[u].v = v;
				aa[v].u = u;
				return true;
			}
		}
		aa[u].d = INF;
		return false;
	}
	static int matchings(A[] aa, int n) {
		int cnt = 0;
		while (bfs(aa, n))
			for (int u = 1; u <= n; u++)
				if (aa[u].v == 0 && dfs(aa, n, u))
					cnt++;
		return cnt;
	}
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		int n = Integer.parseInt(st.nextToken());
		int m = Integer.parseInt(st.nextToken());
		int[] eu = new int[m];
		int[] ev = new int[m];
		for (int j = 0; j < m; j++) {
			st = new StringTokenizer(br.readLine());
			eu[j] = Integer.parseInt(st.nextToken());
			ev[j] = Integer.parseInt(st.nextToken());
		}
		A[] aa = new A[n + 1];
		int min = m + n * 3;
		for (int ctr = 1; ctr <= n; ctr++) {
			boolean loop = false;
			boolean[] ci = new boolean[n + 1];
			boolean[] co = new boolean[n + 1];
			for (int i = 0; i <= n; i++)
				aa[i] = new A();
			int m_ = 0;
			for (int j = 0; j < m; j++) {
				int u = eu[j];
				int v = ev[j];
				if (u == ctr && v == ctr)
					loop = true;
				else if (u == ctr && v != ctr)
					ci[v] = true;
				else if (u != ctr && v == ctr)
					co[u] = true;
				else {
					aa[u].list.add(v);
					m_++;
				}
			}
			int cnt = loop ? 0 : 1;
			for (int i = 1; i <= n; i++)
				if (i != ctr) {
					if (!ci[i])
						cnt++;
					if (!co[i])
						cnt++;
				}
			int m2 = matchings(aa, n);
			cnt += (m_ - m2) + (n - 1 - m2);
			if (min > cnt)
				min = cnt;
		}
		System.out.println(min);
	}
}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.LinkedList;
import java.util.StringTokenizer;

public class GeorgeAndInterestingGraph {
    public static void main(String[] args) {
        MyScanner sc = new MyScanner();

        int N = sc.nextInt();
        int M = sc.nextInt();

        int[] edgeFrom = new int[M];
        int[] edgeTo = new int[M];
        for (int i = 0; i < M; i++) {
        	edgeFrom[i] = sc.nextInt();
        	edgeTo[i] = sc.nextInt();
        }

        int best = Integer.MAX_VALUE;
    	boolean[][] adjMat = makeAdjMat(N, edgeFrom, edgeTo);
        for (int i = 0; i < N; i++) {
        	boolean[][] mat = copyOfArray2d(adjMat);
        	best = Math.min(best, count(mat, M, i));
        }

        System.out.println(best);
    }

    public static boolean[][] copyOfArray2d(boolean[][] arr) {
    	int N = arr.length;
    	int M = arr[0].length;
    	boolean[][] copy = new boolean[N][M];
    	for (int i = 0; i < N; i++) {
    		System.arraycopy(arr[i], 0, copy[i], 0, M);
    	}
    	return copy;
    }

    public static int count(boolean[][] mat, int M, int center) {
//    	int N = mat.length;
//    	int M = mat[0].length;
//
//    	int centerConnect = (mat[center][center]) ? 0 : 1;
//    	for (int i = 0; i < N; i++) {
//			if (i != center) {
//				if (!mat[i][center]) {
//					centerConnect++;
//				}
//				if (!mat[center][i]) {
//					centerConnect++;
//				}
//			}
//	    	mat[i][center] = false;
//	    	mat[center][i] = false;
//		}
//
//    	int[][] adjMat = new int[2 * N + 2][2 * N + 2];
//    	for (int i = 0; i < N; i++) {
//    		for (int j = 0; j < N; j++) {
//    			int idx = N + j;
//    			adjMat[i][idx] = (mat[i][j]) ? 1 : 0;
//    		}
//    	}
//    	int s = 2 * N;
//    	int t = 2 * N + 1;
//    	for (int i = 0; i < N; i++) {
//    		adjMat[s][i] = 1;
//    	}
//    	for (int i = N; i < 2 * N; i++) {
//    		adjMat[i][t] = 1;
//    	}
//
//    	int matches = fordFulkerson(adjMat, s, t);
//
//    	return centerConnect + matches;

    	int N = mat.length;
//    	int M = mat[0].length;

    	int cntWithI = (mat[center][center]) ? 1 : 0;
    	for (int i = 0; i < N; i++) {
    		if (i != center) {
    			if (mat[i][center]) {
    				cntWithI++;
    			}
    			if (mat[center][i]) {
    				cntWithI++;
    			}
    		}
        	mat[i][center] = false;
        	mat[center][i] = false;
    	}

    	int other = M - cntWithI;

//    	int centerConnect = (mat[center][center]) ? 0 : 1;
//
//    	for (int i = 0; i < N; i++) {
//    		if (i != center) {
//    			if (!mat[i][center]) {
//    				centerConnect++;
//    			}
//    			if (!mat[center][i]) {
//    				centerConnect++;
//    			}
//    		}
//        	mat[i][center] = false;
//        	mat[center][i] = false;
//    	}

    	int matches = bipartiteMatching(mat);

    	return (2 * N - 1 - cntWithI + other - matches + N - 1 - matches);
//    	return (centerConnect + N - 1 - matches);
    }

    public static boolean[][] makeAdjMat(int N, int[] edgeFrom, int[] edgeTo) {
    	boolean[][] mat = new boolean[N][N];
    	for (int i = 0; i < edgeFrom.length; i++) {
    		int from = edgeFrom[i] - 1;
    		int to = edgeTo[i] - 1;
    		mat[from][to] = true;
    	}
    	return mat;
    }

    /**
     * Returns true if there is a path from the source 's' to the sink 't' in the residual graph.
     * Also fills parent[] to store the path.
     * See here for more info:  http://www.geeksforgeeks.org/ford-fulkerson-algorithm-for-maximum-flow-problem/
     */
    public static boolean fordFulkersonHelper(int[][] resid, int s, int t, int[] parent) {
    	int V = resid.length;
    	boolean[] visited = new boolean[V];
    	LinkedList<Integer> q = new LinkedList<Integer>();
    	q.push(s);
    	visited[s] = true;
    	parent[s] = -1;

    	while (!q.isEmpty()) {
    		int u = q.pop();
    		for (int v = 0; v < V; v++) {
    			if (!visited[v] && resid[u][v] > 0) {
    				q.push(v);
    				parent[v] = u;
    				visited[v] = true;
    			}
    		}
    	}

    	return visited[t];
    }

    /**
     * Returns the maximum flow from 's' to 't' in the given graph.
     * See here for more info:  http://www.geeksforgeeks.org/ford-fulkerson-algorithm-for-maximum-flow-problem/
     */
    public static int fordFulkerson(int[][] graph, int s, int t) {
    	int V = graph.length;
    	int[][] resid = new int[V][V];
    	int[] parent = new int[V];
    	int maxFlow = 0;

    	for (int u = 0; u < V; u++) {
    		for (int v = 0; v < V; v++) {
    			resid[u][v] = graph[u][v];
    		}
    	}

    	while (fordFulkersonHelper(resid, s, t, parent)) {
    		int pathFlow = Integer.MAX_VALUE;
    		for (int v = t; v != s; v = parent[v]) {
    			int u = parent[v];
    			pathFlow = Math.min(pathFlow,  resid[u][v]);
    		}
    		for (int v = t; v != s; v = parent[v]) {
    			int u = parent[v];
    			resid[u][v] -= pathFlow;
    			resid[v][u] += pathFlow;
    		}
    		maxFlow += pathFlow;
    	}

    	return maxFlow;
    }

    /**
     * Returns true if a matching for vertex 'u' is possible.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static boolean bipartiteMatchingHelper(boolean[][] bpGraph, int u, boolean[] seen, int[] matchR) {
    	int N = bpGraph[0].length;
    	for (int v = 0; v < N; v++) {
    		if (bpGraph[u][v] && !seen[v]) {
    			seen[v] = true;
    			if (matchR[v] < 0 || bipartiteMatchingHelper(bpGraph, matchR[v], seen, matchR)) {
    				matchR[v] = u;
    				return true;
    			}
    		}
    	}
    	return false;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Note:  bpGraph[i][j] = true if there is an edge from i to j.
     * Note:  matchIJ (array of length M) is an output variable containing the matchings, such that matchIJ[i] = j means that there is a match from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph, int[] matchIJ, int[] matchJI) {
    	int ans = bipartiteMatching(bpGraph, matchJI);

    	for (int i = 0; i < matchJI.length; i++) {
    		matchIJ[i] = -1;
    	}

    	for (int j = 0; j < matchJI.length; j++) {
    		int i = matchJI[j];
    		if (i >= 0) {
    			matchIJ[i] = j;
    		}
    	}

    	return ans;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Note:  bpGraph[i][j] = true if there is an edge from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph, int[] matchJI) {
    	int M = bpGraph.length;
    	int N = bpGraph[0].length;

    	for (int i = 0; i < N; i++) {
    		matchJI[i] = -1;
    	}

    	int ans = 0;
    	for (int u = 0; u < M; u++) {
    		boolean[] seen = new boolean[N];
    		if (bipartiteMatchingHelper(bpGraph, u, seen, matchJI)) {
    			ans++;
    		}
    	}

    	return ans;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Overload of the bipartiteMatching function without output parameters.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph) {
    	int N = bpGraph[0].length;
    	int[] matchJI = new int[N];
    	return bipartiteMatching(bpGraph, matchJI);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     */
    public static int bipartiteMatching(int[][] intGraph) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     */
    public static int bipartiteMatching(int[][] intGraph, int[] matchJI) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph, matchJI);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     * Note:  matchIJ (array of length M) is an output variable containing the matchings, such that matchIJ[i] = j means that there is a match from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     */
    public static int bipartiteMatching(int[][] intGraph, int[] matchIJ, int[] matchJI) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph, matchIJ, matchJI);
    }

    /**
     * Converts an integer adjacency matrix of 1's and 0's to a boolean adjacency matrix.
     * Useful with bipartiteMatching, which takes adjancency matrix of boolean[][] as input (instead of int[][]).
     */
    public static boolean[][] intToBooleanAdjMat(int[][] mat) {
    	int M = mat.length;
    	int N = mat[0].length;
    	boolean[][] bMat = new boolean[M][N];
    	for (int i = 0; i < M; i++) {
    		for (int j = 0; j < N; j++) {
    			bMat[i][j] = (mat[i][j] != 0);
    		}
    	}
    	return bMat;
    }

    public static class MyScanner {
        BufferedReader br;
        StringTokenizer st;

        public MyScanner() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        String nextLine() {
            String str = "";
            try { str = br.readLine(); }
            catch (IOException e) { e.printStackTrace(); }
            return str;
        }
    }
}


import java.util.ArrayList;
import java.util.Scanner;

public class Main {

    static ArrayList<Edge> graph;
    static ArrayList<ArrayList<Integer>> graphForKuhn;
    static int n, m, u = 0;
    static int mt[];
    static int used[];

    public static void main(String[] args) {
        formGraph();
        System.out.println(getAnswer());
    }

    static boolean kuhn(int start) {
        if (used[start] == u)
            return false;
        used[start] = u;
        for (int i=0; i< graphForKuhn.get(start).size(); i++) {
            int to = graphForKuhn.get(start).get(i);
            if (mt[to] == -1 || kuhn(mt[to])) {
                mt[to] = start;
                return true;
            }
        }
        return false;
    }

    private static int getAnswer() {
        int currentAnswer = Integer.MAX_VALUE;
        for (int cur= 0; cur<n; cur++) {
            int adj = 0, otheradj = 0, answer = 0;
            for (int j=0; j<n; j++) {
                graphForKuhn.get(j).clear();
                mt[j] = -1;
            }
            for (int j=0; j<m; j++) {
                if (graph.get(j).from == cur || graph.get(j).to == cur)
                    adj++;
                else {
                    graphForKuhn.get(graph.get(j).from).add(graph.get(j).to);
                    otheradj++;
                }
            }
            for (int j=0; j<n; j++) {
                u++;
                kuhn(j);
            }
            int tsz = 0;
            for (int j=0; j<n; j++) {
                if (mt[j] != -1)
                    tsz++;
            }
            answer = 2*(n-1)+1-adj+otheradj-2*tsz+(n-1);
            currentAnswer = Math.min(answer, currentAnswer);
        }
        return currentAnswer;
    }

    private static void formGraph() {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        m = in.nextInt();
        graph = new ArrayList<Edge>(m);
        for (int i=0; i<m; i++) {
            int x = in.nextInt();
            int y = in.nextInt();
            graph.add(new Edge(x-1, y-1));
        }
        graphForKuhn = new ArrayList<ArrayList<Integer>>(n);
        for (int i=0; i<n; i++) graphForKuhn.add(new ArrayList<Integer>(n));
        mt = new int[n];
        used = new int[n];
        in.close();
    }

}

class Edge {
    int from;
    int to;

    public Edge(int from, int to) {
        this.from = from;
        this.to = to;
    }
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * @author Don Li
 */
public class GeorgeInterestingGraph {

    int N = 505;
    int INF = (int) 1e9;

    List<Integer>[] G = new List[N];
    int[] match = new int[N];
    int[] used = new int[N];
    int cur = 0;

    {
        for (int i = 0; i < N; i++) G[i] = new ArrayList<>(1);
    }

    void solve() {
        int n = in.nextInt(), m = in.nextInt();
        int[] fr = new int[m], to = new int[m];
        for (int i = 0; i < m; i++) {
            fr[i] = in.nextInt() - 1;
            to[i] = in.nextInt() - 1;
        }

        int ans = INF;
        for (int i = 0; i < n; i++) {
            int cnt = 0;
            for (int j = 0; j < n; j++) {
                G[j].clear();
                match[j] = -1;
            }
            for (int j = 0; j < m; j++) {
                if (fr[j] == i || to[j] == i) {
                    cnt++;
                } else {
                    G[fr[j]].add(to[j]);
                }
            }

            int other = m - cnt;

            int max = 0;
            for (int j = 0; j < n; j++) {
                cur++;
                if (augment(j)) max++;
            }

            ans = Math.min(ans, 2 * (n - 1) + 1 - cnt + other - max + (n - 1) - max);
        }
        out.println(ans);
    }

    boolean augment(int u) {
        if (used[u] == cur) return false;
        used[u] = cur;
        for (int v : G[u]) {
            if (match[v] < 0 || augment(match[v])) {
                match[v] = u;
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        in = new FastScanner(new BufferedReader(new InputStreamReader(System.in)));
        out = new PrintWriter(System.out);
        new GeorgeInterestingGraph().solve();
        out.close();
    }

    static FastScanner in;
    static PrintWriter out;

    static class FastScanner {
        BufferedReader in;
        StringTokenizer st;

        public FastScanner(BufferedReader in) {
            this.in = in;
        }

        public String nextToken() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(in.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(nextToken());
        }

        public long nextLong() {
            return Long.parseLong(nextToken());
        }

        public double nextDouble() {
            return Double.parseDouble(nextToken());
        }
    }
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.*;

public class D {
	BufferedReader reader;
    StringTokenizer tokenizer;
    PrintWriter out;

	public void solve() throws IOException {
		int N = nextInt();
		int M = nextInt();
		boolean[][] graph = new boolean[N][N];
		for (int i = 0; i < M; i++) {
			graph[nextInt()-1][nextInt()-1] = true;
		}

		int best = Integer.MAX_VALUE;
		for (int c = 0; c < N; c++) {
			int withC = 0;
			for (int i = 0; i < N; i++) {
				if (i == c) {
					if (graph[c][i]) withC++;
				} else {
					if (graph[c][i]) withC++;
					if (graph[i][c]) withC++;
				}
			}
			int notC = M - withC;

			List<Integer>[] g = new List[N];
			for (int i = 0; i < N; i++) {
				g[i] = new ArrayList<Integer>();
			}
			for (int i = 0; i < N; i++) {
				if (i == c) continue;
				for (int j = 0; j < N; j++) {
					if (j == c) continue;
					if (!graph[i][j]) continue;
					g[i].add(j);
				}
			}
			int glen = maxMatching(g, N);

//			int src = 2*N;
//			int dst = 2*N+1;
//			int[][] cap  = new int[2*N+2][2*N+2];
//			int[][] cost = new int[2*N+2][2*N+2];
//			for (int i = 0; i < N; i++) {
//				cap[src][i] = 1;
//				cost[src][i] = 1;
//				cap[N+i][dst] = 1;
//				cost[N+i][dst] = 1;
//			}
//			for (int i = 0; i < N; i++) {
//				if (i == c) continue;
//				for (int j = 0; j < N; j++) {
//					if (j == c) continue;
//					if (!graph[i][j]) continue;
//					cap[i][N+j] = 1;
//					cost[i][N+j] = 1;
//				}
//			}
//			MinCostMaxFlow flow = new MinCostMaxFlow();
//			int result[] = flow.getMaxFlow(cap, cost, src, dst);
//			int glen = result[0];

			int need = (2*N-1 - withC) + (notC - glen) + (N - 1 - glen);
			best = Math.min(best, need);
		}
		out.println(best);
	}

	static boolean findPath(List<Integer>[] g, int u1, int[] matching, boolean[] vis) {
	    vis[u1] = true;
	    for (int v : g[u1]) {
	      int u2 = matching[v];
	      if (u2 == -1 || !vis[u2] && findPath(g, u2, matching, vis)) {
	        matching[v] = u1;
	        return true;
	      }
	    }
	    return false;
	  }

	  public static int maxMatching(List<Integer>[] g, int n2) {
	    int n1 = g.length;
	    int[] matching = new int[n2];
	    Arrays.fill(matching, -1);
	    int matches = 0;
	    for (int u = 0; u < n1; u++) {
	      if (findPath(g, u, matching, new boolean[n1]))
	        ++matches;
	    }
	    return matches;
	  }

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new D().run();
	}

	public void run() {
        try {
            reader = new BufferedReader(new InputStreamReader(System.in));
            tokenizer = null;
            out = new PrintWriter(System.out);
            solve();
            reader.close();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    int nextInt() throws IOException {
        return Integer.parseInt(nextToken());
    }

    long nextLong() throws IOException {
        return Long.parseLong(nextToken());
    }

    double nextDouble() throws IOException {
        return Double.parseDouble(nextToken());
    }

    String nextToken() throws IOException {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            tokenizer = new StringTokenizer(reader.readLine());
        }
        return tokenizer.nextToken();
    }

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * @author Don Li
 */
public class GeorgeInterestingGraph {

    int N = 505;
    int INF = (int) 1e9;

    List<Integer>[] G = new List[N];
    int[] match = new int[N];
    int[] used = new int[N];
    int cur = 0;

    {
        for (int i = 0; i < N; i++) G[i] = new ArrayList<>(1);
    }

    void solve() {
        int n = in.nextInt(), m = in.nextInt();
        int[] fr = new int[m], to = new int[m];
        for (int i = 0; i < m; i++) {
            fr[i] = in.nextInt() - 1;
            to[i] = in.nextInt() - 1;
        }

        int ans = INF;
        for (int i = 0; i < n; i++) {
            int cnt = 0;
            for (int j = 0; j < n; j++) {
                G[j].clear();
                match[j] = -1;
            }
            for (int j = 0; j < m; j++) {
                if (fr[j] == i || to[j] == i) {
                    cnt++;
                } else {
                    G[fr[j]].add(to[j]);
                }
            }

            int other = m - cnt;

            int max = 0;
            for (int j = 0; j < n; j++) {
                cur++;
                augment(j);
            }
            for (int j = 0; j < n; j++) if (match[j] >= 0) max++;

            ans = Math.min(ans, 2 * (n - 1) + 1 - cnt + other - max + (n - 1) - max);
        }
        out.println(ans);
    }

    boolean augment(int u) {
        if (used[u] == cur) return false;
        used[u] = cur;
        for (int v : G[u]) {
            if (match[v] < 0 || augment(match[v])) {
                match[v] = u;
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        in = new FastScanner(new BufferedReader(new InputStreamReader(System.in)));
        out = new PrintWriter(System.out);
        new GeorgeInterestingGraph().solve();
        out.close();
    }

    static FastScanner in;
    static PrintWriter out;

    static class FastScanner {
        BufferedReader in;
        StringTokenizer st;

        public FastScanner(BufferedReader in) {
            this.in = in;
        }

        public String nextToken() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(in.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(nextToken());
        }

        public long nextLong() {
            return Long.parseLong(nextToken());
        }

        public double nextDouble() {
            return Double.parseDouble(nextToken());
        }
    }
}


//Author: net12k44
import java.io.*;
import java.util.*;
public
class Main{//}

static PrintWriter out;

static int c[][];
static int x[] , y[] , n;
static int degIn[];
static ArrayList< ArrayList<Integer> >a = new ArrayList< ArrayList<Integer> >();
static int cnt = 0;
static boolean b[];

private static boolean dfs(int i) {
	if (i == -1) return true;
	if (b[i]) return false;
	b[i] = true;
	for(Integer j : a.get(i) )
		if ( dfs( y[j] ) ) {
			x[i] = j;
			y[j] = i;
			return true;
		}
	return false;
}

private static void find(int k) {
	int _cnt = -1;
	if (k>=0) { x[k] = k; y[k] = k; }
	while (_cnt != cnt) {
		_cnt = cnt;
		Arrays.fill( b , false );
		if (k>=0) b[k] = true;
		for(int i = 0; i < n; ++i)
		   if (x[i] == -1 && dfs(i) ) ++cnt;
	}
	if (k>=0) { x[k] = -1; y[k] = -1; }

}

public static void solve() {
	n = in.nextInt();
	int m = in.nextInt();

	b = new boolean[n]; c = new int [n][n];
	degIn = new int[n]; x = new int[n]; y = new int[n];
	Arrays.fill(x , -1); Arrays.fill(y , -1);
	for(int i = 0; i < n; ++i) a. add( new ArrayList< Integer > () );

	while (m-- > 0) {
		int i = in.nextInt()-1 , j = in.nextInt()-1;
		a.get(i).add(j);
		degIn[j]++;
		c[i][j] = 1;
	}

	find(-1);
	int kq = Integer.MAX_VALUE;

	for(int k = 0; k < n; ++k) {
		if (x[k] != -1) {y[ x[k] ] = -1; x[k] = -1; cnt--; }
		if (y[k] != -1) {x[ y[k] ] = -1; y[k] = -1; cnt--; }
		find(k);

		int t = n*2 - 1 - a.get(k).size() - degIn[k] + c[k][k];
		for(int i = 0; i < n; ++i)
		 if (i!=k)
			t = t + a.get(i).size() - c[i][k];
		t = t - cnt + (n-1) - cnt;
		if (kq > t) kq = t;
	}
	out.println(kq);

}

public static void main (String[] args) throws java.lang.Exception {
	long startTime = System.currentTimeMillis();

	out = new PrintWriter(System.out);
	solve();
	//out.println((String.format("%.2f",(double)(System.currentTimeMillis()-startTime)/1000)));
	out.close();
}

static class in {
	static BufferedReader reader = new BufferedReader( new InputStreamReader(System.in) ) ;
	static StringTokenizer tokenizer = new StringTokenizer("");
	static String next() {
		while ( !tokenizer.hasMoreTokens() )
			try { tokenizer = new StringTokenizer( reader.readLine() ); }
			catch (IOException e){
				throw new RuntimeException(e);
			}
		return tokenizer.nextToken();
	}
	static int nextInt() { return Integer.parseInt( next() ); }
	static double nextDouble(){ return Double.parseDouble( next() ); }
}
//////////////////////////////////////////////////
}//


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.StringTokenizer;

public class GeorgeAndInterestingGraph {
    public static void main(String[] args) {
        MyScanner sc = new MyScanner();

        int N = sc.nextInt();
        int M = sc.nextInt();

        int[] edgeFrom = new int[M];
        int[] edgeTo = new int[M];
        for (int i = 0; i < M; i++) {
        	edgeFrom[i] = sc.nextInt();
        	edgeTo[i] = sc.nextInt();
        }

        int best = Integer.MAX_VALUE;
        for (int i = 0; i < N; i++) {
        	boolean[][] mat = makeAdjMat(N, edgeFrom, edgeTo);
        	best = Math.min(best, count(mat, M, i));
        }

        System.out.println(best);
    }

    public static int count(boolean[][] mat, int M, int center) {
    	int N = mat.length;

    	int centerCount = (mat[center][center]) ? 1 : 0;
    	for (int i = 0; i < N; i++) {
    		if (i != center) {
    			if (mat[i][center]) {
    				centerCount++;
    			}
    			if (mat[center][i]) {
    				centerCount++;
    			}
    		}
        	mat[i][center] = false;
        	mat[center][i] = false;
    	}
    	int other = M - centerCount;

    	int matches = bipartiteMatching(mat);

    	return (2 * N - 1 - centerCount + other - matches + N - 1 - matches);
    }

    public static boolean[][] makeAdjMat(int N, int[] edgeFrom, int[] edgeTo) {
    	boolean[][] mat = new boolean[N][N];
    	for (int i = 0; i < edgeFrom.length; i++) {
    		int from = edgeFrom[i] - 1;
    		int to = edgeTo[i] - 1;
    		mat[from][to] = true;
    	}
    	return mat;
    }

    /**
     * Returns true if a matching for vertex 'u' is possible.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static boolean bipartiteMatchingHelper(boolean[][] bpGraph, int u, boolean[] seen, int[] matchR) {
    	int N = bpGraph[0].length;
    	for (int v = 0; v < N; v++) {
    		if (bpGraph[u][v] && !seen[v]) {
    			seen[v] = true;
    			if (matchR[v] < 0 || bipartiteMatchingHelper(bpGraph, matchR[v], seen, matchR)) {
    				matchR[v] = u;
    				return true;
    			}
    		}
    	}
    	return false;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Note:  bpGraph[i][j] = true if there is an edge from i to j.
     * Note:  matchIJ (array of length M) is an output variable containing the matchings, such that matchIJ[i] = j means that there is a match from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph, int[] matchIJ, int[] matchJI) {
    	int ans = bipartiteMatching(bpGraph, matchJI);

    	for (int i = 0; i < matchJI.length; i++) {
    		matchIJ[i] = -1;
    	}

    	for (int j = 0; j < matchJI.length; j++) {
    		int i = matchJI[j];
    		if (i >= 0) {
    			matchIJ[i] = j;
    		}
    	}

    	return ans;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Note:  bpGraph[i][j] = true if there is an edge from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph, int[] matchJI) {
    	int M = bpGraph.length;
    	int N = bpGraph[0].length;

    	for (int i = 0; i < N; i++) {
    		matchJI[i] = -1;
    	}

    	int ans = 0;
    	for (int u = 0; u < M; u++) {
    		boolean[] seen = new boolean[N];
    		if (bipartiteMatchingHelper(bpGraph, u, seen, matchJI)) {
    			ans++;
    		}
    	}

    	return ans;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Overload of the bipartiteMatching function without output parameters.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph) {
    	int N = bpGraph[0].length;
    	int[] matchJI = new int[N];
    	return bipartiteMatching(bpGraph, matchJI);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     */
    public static int bipartiteMatching(int[][] intGraph) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     */
    public static int bipartiteMatching(int[][] intGraph, int[] matchJI) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph, matchJI);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     * Note:  matchIJ (array of length M) is an output variable containing the matchings, such that matchIJ[i] = j means that there is a match from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     */
    public static int bipartiteMatching(int[][] intGraph, int[] matchIJ, int[] matchJI) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph, matchIJ, matchJI);
    }

    /**
     * Converts an integer adjacency matrix of 1's and 0's to a boolean adjacency matrix.
     * Useful with bipartiteMatching, which takes adjancency matrix of boolean[][] as input (instead of int[][]).
     */
    public static boolean[][] intToBooleanAdjMat(int[][] mat) {
    	int M = mat.length;
    	int N = mat[0].length;
    	boolean[][] bMat = new boolean[M][N];
    	for (int i = 0; i < M; i++) {
    		for (int j = 0; j < N; j++) {
    			bMat[i][j] = (mat[i][j] != 0);
    		}
    	}
    	return bMat;
    }

    public static class MyScanner {
        BufferedReader br;
        StringTokenizer st;

        public MyScanner() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        String nextLine() {
            String str = "";
            try { str = br.readLine(); }
            catch (IOException e) { e.printStackTrace(); }
            return str;
        }
    }
}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.StringTokenizer;

public class GeorgeAndInterestingGraph {
    public static void main(String[] args) {
        MyScanner sc = new MyScanner();

        int N = sc.nextInt();
        int M = sc.nextInt();

        int[] edgeFrom = new int[M];
        int[] edgeTo = new int[M];
        for (int i = 0; i < M; i++) {
        	edgeFrom[i] = sc.nextInt();
        	edgeTo[i] = sc.nextInt();
        }

        int best = Integer.MAX_VALUE;
    	boolean[][] adjMat = makeAdjMat(N, edgeFrom, edgeTo);
        for (int i = 0; i < N; i++) {
        	boolean[][] mat = copyOfArray2d(adjMat);
        	best = Math.min(best, count(mat, M, i));
        }

        System.out.println(best);
    }

    public static boolean[][] copyOfArray2d(boolean[][] arr) {
    	int N = arr.length;
    	int M = arr[0].length;
    	boolean[][] copy = new boolean[N][M];
    	for (int i = 0; i < N; i++) {
    		System.arraycopy(arr[i], 0, copy[i], 0, M);
    	}
    	return copy;
    }

    public static int count(boolean[][] mat, int M, int center) {
    	int N = mat.length;

    	int centerCount = (mat[center][center]) ? 1 : 0;
    	for (int i = 0; i < N; i++) {
    		if (i != center) {
    			if (mat[i][center]) {
    				centerCount++;
    			}
    			if (mat[center][i]) {
    				centerCount++;
    			}
    		}
        	mat[i][center] = false;
        	mat[center][i] = false;
    	}
    	int other = M - centerCount;

    	int matches = bipartiteMatching(mat);

    	return (2 * N - 1 - centerCount + other - matches + N - 1 - matches);
    }

    public static boolean[][] makeAdjMat(int N, int[] edgeFrom, int[] edgeTo) {
    	boolean[][] mat = new boolean[N][N];
    	for (int i = 0; i < edgeFrom.length; i++) {
    		int from = edgeFrom[i] - 1;
    		int to = edgeTo[i] - 1;
    		mat[from][to] = true;
    	}
    	return mat;
    }

    /**
     * Returns true if a matching for vertex 'u' is possible.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static boolean bipartiteMatchingHelper(boolean[][] bpGraph, int u, boolean[] seen, int[] matchR) {
    	int N = bpGraph[0].length;
    	for (int v = 0; v < N; v++) {
    		if (bpGraph[u][v] && !seen[v]) {
    			seen[v] = true;
    			if (matchR[v] < 0 || bipartiteMatchingHelper(bpGraph, matchR[v], seen, matchR)) {
    				matchR[v] = u;
    				return true;
    			}
    		}
    	}
    	return false;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Note:  bpGraph[i][j] = true if there is an edge from i to j.
     * Note:  matchIJ (array of length M) is an output variable containing the matchings, such that matchIJ[i] = j means that there is a match from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph, int[] matchIJ, int[] matchJI) {
    	int ans = bipartiteMatching(bpGraph, matchJI);

    	for (int i = 0; i < matchJI.length; i++) {
    		matchIJ[i] = -1;
    	}

    	for (int j = 0; j < matchJI.length; j++) {
    		int i = matchJI[j];
    		if (i >= 0) {
    			matchIJ[i] = j;
    		}
    	}

    	return ans;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Note:  bpGraph[i][j] = true if there is an edge from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph, int[] matchJI) {
    	int M = bpGraph.length;
    	int N = bpGraph[0].length;

    	for (int i = 0; i < N; i++) {
    		matchJI[i] = -1;
    	}

    	int ans = 0;
    	for (int u = 0; u < M; u++) {
    		boolean[] seen = new boolean[N];
    		if (bipartiteMatchingHelper(bpGraph, u, seen, matchJI)) {
    			ans++;
    		}
    	}

    	return ans;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Overload of the bipartiteMatching function without output parameters.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph) {
    	int N = bpGraph[0].length;
    	int[] matchJI = new int[N];
    	return bipartiteMatching(bpGraph, matchJI);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     */
    public static int bipartiteMatching(int[][] intGraph) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     */
    public static int bipartiteMatching(int[][] intGraph, int[] matchJI) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph, matchJI);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     * Note:  matchIJ (array of length M) is an output variable containing the matchings, such that matchIJ[i] = j means that there is a match from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     */
    public static int bipartiteMatching(int[][] intGraph, int[] matchIJ, int[] matchJI) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph, matchIJ, matchJI);
    }

    /**
     * Converts an integer adjacency matrix of 1's and 0's to a boolean adjacency matrix.
     * Useful with bipartiteMatching, which takes adjancency matrix of boolean[][] as input (instead of int[][]).
     */
    public static boolean[][] intToBooleanAdjMat(int[][] mat) {
    	int M = mat.length;
    	int N = mat[0].length;
    	boolean[][] bMat = new boolean[M][N];
    	for (int i = 0; i < M; i++) {
    		for (int j = 0; j < N; j++) {
    			bMat[i][j] = (mat[i][j] != 0);
    		}
    	}
    	return bMat;
    }

    public static class MyScanner {
        BufferedReader br;
        StringTokenizer st;

        public MyScanner() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        String nextLine() {
            String str = "";
            try { str = br.readLine(); }
            catch (IOException e) { e.printStackTrace(); }
            return str;
        }
    }
}


//package codeforces;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.*;

public class D {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    PrintWriter writer = new PrintWriter(System.out);
    StringTokenizer stringTokenizer;

    String next() throws IOException {
        while (stringTokenizer == null || !stringTokenizer.hasMoreTokens()) {
            stringTokenizer = new StringTokenizer(reader.readLine());
        }
        return stringTokenizer.nextToken();
    }

    int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    long nextLong() throws IOException {
        return Long.parseLong(next());
    }

    final int MOD = 1000 * 1000 * 1000 + 7;
    int sum(int a, int b) {
        a += b;
        return a >= MOD ? a - MOD : a;
    }

    @SuppressWarnings("unchecked")
    void solve() throws IOException {
        final int n = nextInt();
        int m = nextInt();
        int[] from = new int[m];
        int[] to = new int[m];
        for(int i = 0; i < m; i++) {
            from[i] = nextInt();
            to[i] = nextInt();
        }
        int ans = solve(n, m, from, to);
        writer.println(ans);
        writer.close();
    }

    private int solve(final int n, int m, int[] from, int[] to) {
        final List<List<Integer>> g = new ArrayList<>();
        final List<List<Integer>> rg = new ArrayList<>();
        for(int i = 0; i <= n; i++) {
            g.add(new ArrayList<Integer>());
            rg.add(new ArrayList<Integer>());
        }
        int[] c = new int[n + 1];
        int[] loop = new int[n + 1];
        for(int i = 0; i < m; i++) {
            int u = from[i];
            int v = to[i];
            g.get(u).add(v);
            rg.get(v).add(u);
            c[u]++;
            c[v]++;
            if(u == v) {
                loop[u]++;
            }
        }
        class Utils {
            int[] prev = new int[n + 1];
            int[] next = new int[n + 1];
            int[] used = new int[n + 1];
            int mark;
            int forbidden;
            int maxMatch() {
                maxMatch = 0;
                for(int i = 1; i <= n; i++) {
                    mark = i;
                    if(findPath(i)) {
                        maxMatch++;
                    }
                }
                return maxMatch;
            }
            boolean findPath(int u) {
                if(u == forbidden) {
                    return false;
                }
                used[u] = mark;
                for (int v : g.get(u)) {
                    if(v == forbidden) {
                        continue;
                    }
                    if(prev[v] == 0 || (used[prev[v]] != mark && findPath(prev[v]))) {
                        prev[v] = u;
                        next[u] = v;
                        return true;
                    }
                }
                return false;
            }
            int maxMatch = 0;
            void amend(int u) {
//                Arrays.fill(used, false);
                if(findPath(u)) {
                    maxMatch++;
                }
            }
            void cancel(int u) {
                forbidden = u;
                int v = next[u];
                if(v != 0) {
                    maxMatch--;
                    prev[v] = 0;
                    next[u] = 0;
                    for (int i : rg.get(v)) {
                        if(next[i] == 0) {
                            amend(i);
                        }
                    }
//                    amend(v);
                }
                if(prev[u] != 0) {
                    maxMatch--;
                    amend(prev[u]);
                    prev[u] = 0;
                }
            }
        }
//        Utils utils = new Utils();
//        for(int i = 1; i <= n; i++) {
//            utils.amend(i);
//        }
        int ans = Integer.MAX_VALUE;
        for(int i = 1; i <= n; i++) {
//            utils.cancel(i);
//            utils.amend(i - 1);
            Utils utils = new Utils();
            utils.forbidden = i;
            utils.maxMatch();
            ans = Math.min(ans, (2 * n - 1 - c[i] + loop[i]) + (m - c[i] + loop[i] - utils.maxMatch) + (n - 1 - utils.maxMatch));
        }
        return ans;
    }

    void test() {
        final int N = 4;
        final int[] ef = new int[N * N];
        final int[] et = new int[N * N];
        for(int i = 1; i <= N; i++) {
            for(int j = 1; j <= N; j++) {
                ef[(i - 1) * N + j - 1] = i;
                et[(i - 1) * N + j - 1] = j;
            }
        }
        List<Integer> good = new ArrayList<>();
        for(int mask = 0; mask < 1 << N * N; mask++) {
            int[] in = new int[N + 1];
            int[] out = new int[N + 1];
            for(int i = 0; i < N * N; i++) {
                if((mask >> i) % 2 == 1) {
                    out[ef[i]]++;
                    in[et[i]]++;
                }
            }
            boolean ok = false;
            for(int i = 1; i <= N; i++) {
                if(in[i] == N && out[i] == N) {
                    in[i] = 2;
                    out[i] = 2;
                    ok = true;
                    break;
                }
            }
            for(int i = 1; i <= N; i++) {
                if(in[i] != 2 || out[i] != 2) {
                    ok = false;
                }
            }
            if(ok) {
                good.add(mask);
            }
        }
        System.out.println("good graphs count: " + good.size());
        for (int mask : good) {
            int m = Integer.bitCount(mask);
            int[] from = new int[m];
            int[] to = new int[m];
            int index = 0;
            for(int i = 0; i < N * N; i++) {
                if((mask >> i) % 2 == 1) {
                    from[index] = ef[i];
                    to[index] = et[i];
                    index++;
                }
            }
            if(solve(N, m, from, to) != 0) {
                writer.println(N + " " + m);
                for(int i = 0; i < m; i++) {
                    writer.println(from[i] + " " + to[i]);
                }
                writer.close();
                return;
            }
        }
        for(int mask = 0; mask < 1 << N * N; mask++) {
            int optimal = Integer.MAX_VALUE;
            for (Integer i : good) {
                optimal = Math.min(optimal, Integer.bitCount(i ^ mask));
            }
            int m = Integer.bitCount(mask);
            int[] from = new int[m];
            int[] to = new int[m];
            int index = 0;
            for(int i = 0; i < N * N; i++) {
                if((mask >> i) % 2 == 1) {
                    from[index] = ef[i];
                    to[index] = et[i];
                    index++;
                }
            }
            final int fast = solve(N, m, from, to);
            if(optimal != fast) {
                System.out.println("fast = " + fast + ", optimal = " + optimal);
                writer.println(N + " " + m);
                for(int i = 0; i < m; i++) {
                    writer.println(from[i] + " " + to[i]);
                }
                writer.close();
                return;
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new D().solve();
    }
}


import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.lang.System.out;
import static java.util.Collections.*;
import java.io.*;
import java.math.*;
import java.util.*;

public class Main {
	static boolean LOCAL = System.getSecurityManager() == null;
	Scanner in = new Scanner(System.in);
	private int[] B;
	private int[] A;
	private int n;
	private int m;

	void run() {
		n = in.nextInt();
		m = in.nextInt();
		A = new int[m];
		B = new int[m];
		for (int i = 0; i < m; i++) {
			A[i] = in.nextInt() - 1;
			B[i] = in.nextInt() - 1;
		}
		int ans = Integer.MAX_VALUE;
		for (int i = 0; i < n; i++) {
			ans = min(ans, solve(i));
		}
		out.println(ans);
	}
	int solve(int x) {
		int ans = 3 * (n - 1)  + 1 + m;
		V[] vs = new V[n * 2];
		for (int i = 0; i < vs.length; i++) vs[i] = new V();
		for (int i = 0; i < m; i++) {
			if (A[i] == x || B[i] == x) ans -= 2;
			else vs[A[i]].connect(vs[n + B[i]]);
		}
		return ans - 2 * bipartiteMatching(vs);
	}
	class V extends ArrayList<V> {
		V pair;
		boolean used;
		void connect(V v) {
			add(v);
			v.add(this);
		}
	}
	int bipartiteMatching(V[] vs) {
		int match = 0;
		for (V v : vs) if (v.pair == null) {
			for (V u : vs) u.used = false;
			if (dfs(v)) match++;
		}
		return match;
	}
	boolean dfs(V v) {
		v.used = true;
		for (V u : v) {
			V w = u.pair;
			if (w == null || !w.used && dfs(w)) {
				v.pair = u;
				u.pair = v;
				return true;
			}
		}
		return false;
	}
	void debug(Object... os) {
		System.err.println(deepToString(os));
	}

	public static void main(String[] args) {
		if (LOCAL) {
			try {
				System.setIn(new FileInputStream("./../../in.txt"));
				// System.setOut(new PrintStream("./../../out"));
			} catch (Throwable e) {
				LOCAL = false;
			}
		}
		long start = 0;
		if (LOCAL)
			start = System.nanoTime();
		new Main().run();
		if (LOCAL)
			System.err.printf("[Time : %.6f s]%n",
					(System.nanoTime() - start) * 1e-9);
	}
}

class Scanner {
	BufferedReader br;
	StringTokenizer st;

	Scanner(InputStream in) {
		br = new BufferedReader(new InputStreamReader(in));
		eat("");
	}

	void eat(String s) {
		st = new StringTokenizer(s);
	}

	String nextLine() {
		try {
			return br.readLine();
		} catch (IOException e) {
			return null;
		}
	}

	boolean hasNext() {
		while (!st.hasMoreTokens()) {
			String s = nextLine();
			if (s == null)
				return false;
			eat(s);
		}
		return true;
	}

	String next() {
		hasNext();
		return st.nextToken();
	}

	int nextInt() {
		return Integer.parseInt(next());
	}

	long nextLong() {
		return Long.parseLong(next());
	}

	double nextDouble() {
		return Double.parseDouble(next());
	}
}


//package codeforces;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.*;

public class D {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    PrintWriter writer = new PrintWriter(System.out);
    StringTokenizer stringTokenizer;

    String next() throws IOException {
        while (stringTokenizer == null || !stringTokenizer.hasMoreTokens()) {
            stringTokenizer = new StringTokenizer(reader.readLine());
        }
        return stringTokenizer.nextToken();
    }

    int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    long nextLong() throws IOException {
        return Long.parseLong(next());
    }

    final int MOD = 1000 * 1000 * 1000 + 7;
    int sum(int a, int b) {
        a += b;
        return a >= MOD ? a - MOD : a;
    }

    @SuppressWarnings("unchecked")
    void solve() throws IOException {
        final int n = nextInt();
        int m = nextInt();
        int[] from = new int[m];
        int[] to = new int[m];
        for(int i = 0; i < m; i++) {
            from[i] = nextInt();
            to[i] = nextInt();
        }
        int ans = solve(n, m, from, to);
        writer.println(ans);
        writer.close();
    }

    private int solve(final int n, int m, int[] from, int[] to) {
        final List<List<Integer>> g = new ArrayList<>();
        for(int i = 0; i <= n; i++) {
            g.add(new ArrayList<Integer>());
        }
        int[] c = new int[n + 1];
        int[] loop = new int[n + 1];
        for(int i = 0; i < m; i++) {
            int u = from[i];
            int v = to[i];
            g.get(u).add(v);
            c[u]++;
            c[v]++;
            if(u == v) {
                loop[u]++;
            }
        }
        class Utils {
            int[] prev = new int[n + 1];
            int[] used = new int[n + 1];
            int mark;
            int forbidden;
            int maxMatch() {
                maxMatch = 0;
                for(int i = 1; i <= n; i++) {
                    mark = i;
                    if(findPath(i)) {
                        maxMatch++;
                    }
                }
                return maxMatch;
            }
            boolean findPath(int u) {
                if(u == forbidden) {
                    return false;
                }
                used[u] = mark;
                for (int v : g.get(u)) {
                    if(v == forbidden) {
                        continue;
                    }
                    if(prev[v] == 0 || (used[prev[v]] != mark && findPath(prev[v]))) {
                        prev[v] = u;
                        return true;
                    }
                }
                return false;
            }
            int maxMatch = 0;
        }
        int ans = Integer.MAX_VALUE;
        for(int i = 1; i <= n; i++) {
            Utils utils = new Utils();
            utils.forbidden = i;
            utils.maxMatch();
            ans = Math.min(ans, (2 * n - 1 - c[i] + loop[i]) + (m - c[i] + loop[i] - utils.maxMatch) + (n - 1 - utils.maxMatch));
        }
        return ans;
    }

    public static void main(String[] args) throws IOException {
        new D().solve();
    }
}


import java.io.*;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.sql.Time;
import java.util.*;

import java.math.BigInteger;

import static java.lang.Math.*;
import static java.math.BigInteger.*;
import static java.util.Arrays.*;

public class Main{

    void run(){
        Locale.setDefault(Locale.US);
        boolean oj = System.getProperty("ONLINE_JUDGE") != null;
//        boolean oj = true;
        try{
            if( oj ){
                sc  = new FastScanner( new InputStreamReader(System.in  ) );
                out = new PrintWriter(   new OutputStreamWriter(System.out) );
            } else{
                sc  = new FastScanner(new FileReader("in.txt") );
//                sc  = new FastScanner(new FileReader("D:\\JavaOlymp\\FatalError\\output.txt") );
                out = new PrintWriter(   new FileWriter("out.txt") );
            }
        } catch (Exception e) {
            System.exit(-1);
        }
        long tB = System.currentTimeMillis();
        solve();
        if( !oj ) System.err.println( "Time: " + (System.currentTimeMillis()-tB)/1e3 );
        out.flush();
    }

    class FastScanner{
        BufferedReader br;
        StringTokenizer st = new StringTokenizer("");
        FastScanner( InputStreamReader a ){
            br = new BufferedReader(a);
        }
        FastScanner( FileReader a ){
            br = new BufferedReader(a);
        }
        String next(){
            while( !st.hasMoreTokens() )
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    return null;
                }
            return st.nextToken();
        }
        String readLine(){
            try {
                return br.readLine();
            } catch (Exception e) {
                return null;
            }
        }
        int nextInt(){ return Integer.parseInt(next()); }
        long nextLong(){ return Long.parseLong(next()); }
    }

    FastScanner sc;
    PrintWriter out;

    public static void main(String[] args){
        new Main().run();
//        new Thread( null, new Runnable() {
//            @Override
//            public void run() {
//                new Main().run();
//            }
//            }, "LOL", 256L * 1024 * 1024 / 2 ).run();
//        }, "LOL", 2000 * 1024 * 1024 ).run();
    }

    void TLE(){ for(;;); }

    void MLE(){
        int[][] adj = new int[1024*1024][];
        for( int i = 0; i < adj.length; ++i )
            adj[i] = new int[1024*1024];
    }

    void exit( int val ){
        out.flush();
        System.exit(val);
    }
    //////////////////////////////////////////////////////////////////////////////////////////

    int n, m;
    boolean[][] grid;
    ArrayList<Integer>[] gr;
    int c;
    int[] mt;
    boolean[] u;

    boolean try_kuhn( int v ){
        if( u[v] ) return false;
        u[v] = true;
        for( int to : gr[v] ){
            if( to == c || !grid[v][to] ) continue;
            if( mt[to]==-1 || try_kuhn(mt[to]) ){
                mt[to] = v;
                return true;
            }
        }
        return false;
    }

    void solve(){
        n = sc.nextInt();
        m = sc.nextInt();
        grid = new boolean[n+1][n+1];
        gr = new ArrayList[n+1];
        for( int v = 1; v <= n; ++v ) gr[v] = new ArrayList<Integer>();
        for( int it = 0; it < m; ++it ){
            int a = sc.nextInt();
            int b = sc.nextInt();
            grid[a][b] = true;
            gr[a].add(b);
        }

        int ans = Integer.MAX_VALUE;
        for( c = 1; c <= n; ++c ){
            int curAns = 0;
            for( int v = 1; v <= n; ++v )
                if( v != c ){
                    if( !grid[c][v] ) ++curAns;
                    if( !grid[v][c] ) ++curAns;
                }
            if( !grid[c][c] ) ++curAns;

            mt = new int[n+1];
            fill( mt, -1 );
            for( int i = 1; i <= n; ++i )
                if( i != c ){
                    u = new boolean[n+1];
                    try_kuhn(i);
                }

            int szMt = 0;
            for( int i = 1; i <= n; ++i )
                if( mt[i] != -1 )
                    ++szMt;
            curAns += n - 1 - szMt;

            for( int a = 1; a <= n; ++a ){
            for( int b : gr[a] ){
                if( a==c || b==c || !grid[a][b]
                ) continue;
                if(!( a==mt[b] ))
                    ++curAns;
            }
            }

//            out.printf( "%d %d\n", c, curAns );
            ans = min( ans, curAns );
        }
        out.println( ans );
    }

}


//package round227;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.InputMismatchException;

public class D {
	InputStream is;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni(), m = ni();
		int[] from = new int[m];
		int[] to = new int[m];
		for(int i = 0;i < m;i++){
			from[i] = ni()-1;
			to[i] = ni()-1;
		}
		int[] nfrom = new int[m];
		int[] nto = new int[m];
		int min = 999999999;
		for(int i = 0;i < n;i++){
			int p = 0;
			for(int j = 0;j < m;j++){
				if(from[j] != i && to[j] != i){
					nfrom[p] = from[j];
					nto[p] = to[j];
					p++;
				}
			}
			int[][] g = packD(n, nfrom, nto, p);
			int mat = doBipartiteMatchingHKNoRec(g, n);

			int ch = p - mat + (n-1-mat);
			ch += 2*n-1 - (m - p);
			min = Math.min(min, ch);
		}
		out.println(min);
	}

	public static int doBipartiteMatchingHKNoRec(int[][] g, int m)
	{
		int n = g.length;
		if(n == 0)return 0;
		int[] from = new int[m];
		int[] to = new int[n];
		Arrays.fill(to, -1);
		Arrays.fill(from, n);

		int[] d = new int[n+1];
		int mat = 0;
		int[] stack = new int[n+1];
		int[] adjind = new int[n+1];
		while(true){
			Arrays.fill(d, -1);
			int[] q = new int[n];
			int r = 0;
			for(int i = 0;i < n;i++){
				if(to[i] == -1){
					d[i] = 0;
					q[r++] = i;
				}
			}

			for(int p = 0;p < r;p++) {
				int cur = q[p];
				for(int adj : g[cur]){
					int nex = from[adj];
					if(d[nex] == -1) {
						if(nex != n)q[r++] = nex;
						d[nex] = d[cur] + 1;
					}
				}
			}
			if(d[n] == -1)break;

			for(int i = 0;i < n;i++){
				if(to[i] == -1){
					int sp = 1;
					stack[0] = i;
					adjind[0] = 0;
					boolean prevB = false;
					outer:
					while(sp >= 1){
						int cur = stack[sp-1];
						if(cur == n){
							prevB = true;
							sp--;
							continue;
						}
						for(;adjind[sp-1] < 2*g[cur].length;){
							int adj = g[cur][adjind[sp-1]/2];
							if(adjind[sp-1] % 2 == 0){
								int nex = from[adj];
								if(d[nex] == d[cur] + 1){
									stack[sp] = nex;
									adjind[sp] = 0;
									adjind[sp-1]++;
									sp++;
									continue outer;
								}else{
									adjind[sp-1]+=2;
								}
							}else{
								if(prevB){
									to[cur] = adj;
									from[adj] = cur;
									prevB = true;
									sp--;
									continue outer;
								}
								adjind[sp-1]++;
							}
						}
						d[cur] = -1;
						prevB = false;
						sp--;
					}
					if(prevB)mat++;
				}
			}
		}

		return mat;
	}

	public static int[][] packD(int n, int[] from, int[] to, int sup)
	{
		int[][] g = new int[n][];
		int[] p = new int[n];
		for(int i = 0;i < sup;i++)p[from[i]]++;
		for(int i = 0;i < n;i++)g[i] = new int[p[i]];
		for(int i = 0;i < sup;i++){
			g[from[i]][--p[from[i]]] = to[i];
		}
		return g;
	}

	void run() throws Exception
	{
//		int n = 500, m = 1000;
//		Random gen = new Random();
//		StringBuilder sb = new StringBuilder();
//		sb.append(n + " ");
//		sb.append(m + " ");
//		boolean[][] g = new boolean[n][n];
//		for(int i = 0;i < m;i++){
//			while(true){
//				int r = gen.nextInt(n);
//				int c = gen.nextInt(n);
//				if(!g[r][c]){
//					g[r][c] = true;
//					break;
//				}
//			}
//		}
//		for(int i = 0;i < n;i++){
//			for(int j = 0;j < n;j++){
//				if(g[i][j]){
//					sb.append((i+1) + " ");
//					sb.append((j+1) + " ");
//				}
//			}
//		}
//		INPUT = sb.toString();

		is = oj ? System.in : new ByteArrayInputStream(INPUT.getBytes());
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception { new D().run(); }

	private byte[] inbuf = new byte[1024];
	private int lenbuf = 0, ptrbuf = 0;

	private int readByte()
	{
		if(lenbuf == -1)throw new InputMismatchException();
		if(ptrbuf >= lenbuf){
			ptrbuf = 0;
			try { lenbuf = is.read(inbuf); } catch (IOException e) { throw new InputMismatchException(); }
			if(lenbuf <= 0)return -1;
		}
		return inbuf[ptrbuf++];
	}

	private boolean isSpaceChar(int c) { return !(c >= 33 && c <= 126); }
	private int skip() { int b; while((b = readByte()) != -1 && isSpaceChar(b)); return b; }

	private double nd() { return Double.parseDouble(ns()); }
	private char nc() { return (char)skip(); }

	private String ns()
	{
		int b = skip();
		StringBuilder sb = new StringBuilder();
		while(!(isSpaceChar(b))){ // when nextLine, (isSpaceChar(b) && b != ' ')
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}

	private char[] ns(int n)
	{
		char[] buf = new char[n];
		int b = skip(), p = 0;
		while(p < n && !(isSpaceChar(b))){
			buf[p++] = (char)b;
			b = readByte();
		}
		return n == p ? buf : Arrays.copyOf(buf, p);
	}

	private char[][] nm(int n, int m)
	{
		char[][] map = new char[n][];
		for(int i = 0;i < n;i++)map[i] = ns(m);
		return map;
	}

	private int[] na(int n)
	{
		int[] a = new int[n];
		for(int i = 0;i < n;i++)a[i] = ni();
		return a;
	}

	private int ni()
	{
		int num = 0, b;
		boolean minus = false;
		while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
		if(b == '-'){
			minus = true;
			b = readByte();
		}

		while(true){
			if(b >= '0' && b <= '9'){
				num = num * 10 + (b - '0');
			}else{
				return minus ? -num : num;
			}
			b = readByte();
		}
	}

	private long nl()
	{
		long num = 0;
		int b;
		boolean minus = false;
		while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
		if(b == '-'){
			minus = true;
			b = readByte();
		}

		while(true){
			if(b >= '0' && b <= '9'){
				num = num * 10 + (b - '0');
			}else{
				return minus ? -num : num;
			}
			b = readByte();
		}
	}

	private boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	private void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}


import java.io.*;
import java.util.*;

public class CF387D {
	static class A {
		ArrayList<Integer> list = new ArrayList<>();
		int u, v, d;
	}
	static int INF = Integer.MAX_VALUE;
	static boolean bfs(A[] aa, int n) {
		LinkedList<Integer> queue = new LinkedList<>();
		for (int u = 1; u <= n; u++)
			if (aa[u].v > 0)
				aa[u].d = INF;
			else {
				aa[u].d = 0;
				queue.addLast(u);
			}
		aa[0].d = INF;
		while (!queue.isEmpty()) {
			int u = queue.removeFirst();
			if (aa[u].d + 1 == aa[0].d)
				break;
			for (int v : aa[u].list) {
				int w = aa[v].u;
				if (aa[w].d == INF) {
					aa[w].d = aa[u].d + 1;
					queue.addLast(w);
				}
			}
		}
		return aa[0].d != INF;
	}
	static boolean dfs(A[] aa, int n, int u) {
		if (u == 0)
			return true;
		for (int v : aa[u].list) {
			int w = aa[v].u;
			if (aa[w].d == aa[u].d + 1 && dfs(aa, n, w)) {
				aa[u].v = v;
				aa[v].u = u;
				return true;
			}
		}
		aa[u].d = INF;
		return false;
	}
	static int matchings(A[] aa, int n) {
		int cnt = 0;
		while (bfs(aa, n))
			for (int u = 1; u <= n; u++)
				if (aa[u].v == 0 && dfs(aa, n, u))
					cnt++;
		return cnt;
	}
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		int n = Integer.parseInt(st.nextToken());
		int m = Integer.parseInt(st.nextToken());
		int[] eu = new int[m];
		int[] ev = new int[m];
		for (int j = 0; j < m; j++) {
			st = new StringTokenizer(br.readLine());
			eu[j] = Integer.parseInt(st.nextToken());
			ev[j] = Integer.parseInt(st.nextToken());
		}
		A[] aa = new A[n + 1];
		int min = m + n * 3;
		for (int ctr = 1; ctr <= n; ctr++) {
			boolean loop = false;
			boolean[] ci = new boolean[n + 1];
			boolean[] co = new boolean[n + 1];
			for (int i = 0; i <= n; i++)
				aa[i] = new A();
			int m_ = 0;
			for (int j = 0; j < m; j++) {
				int u = eu[j];
				int v = ev[j];
				if (u == ctr && v == ctr)
					loop = true;
				else if (u == ctr && v != ctr)
					ci[v] = true;
				else if (u != ctr && v == ctr)
					co[u] = true;
				else {
					aa[u].list.add(v);
					m_++;
				}
			}
			int cnt = loop ? 0 : 1;
			for (int i = 1; i <= n; i++)
				if (i != ctr) {
					if (!ci[i])
						cnt++;
					if (!co[i])
						cnt++;
				}
			int m2 = matchings(aa, n);
			cnt += (m_ - m2) + (n - 1 - m2);
			if (min > cnt)
				min = cnt;
		}
		System.out.println(min);
	}
}


import java.io.*;
import java.util.*;

public class CF {

	class W implements Comparable<W> {
		int id, sz;

		W(int id, int sz) {
			this.id = id;
			this.sz = sz;
		}

		@Override
		public int compareTo(W o) {
			return -Integer.compare(sz, o.sz);
		}
	}

	int[] left;
	int center;
	boolean[] used;

	boolean go(int v) {
		if (used[v])
			return false;
		used[v] = true;
		for (int i = 0; i < g[v].size(); ++i) {
			int to = g[v].get(i);
			if (to == center)
				continue;
			if (left[to] == -1 || go(left[to])) {
				left[to] = v;
				return true;
			}
		}
		return false;
	}

	ArrayList<Integer>[] g;

	void solve() {

		int n = in.nextInt();
		int m = in.nextInt();
		used = new boolean[n];
		g = new ArrayList[n];
		for (int i = 0; i < n; i++)
			g[i] = new ArrayList<>();
		for (int i = 0; i < m; i++) {
			g[in.nextInt() - 1].add(in.nextInt() - 1);
		}
		long st = System.currentTimeMillis();
		int ans = Integer.MAX_VALUE;
		W[] a = new W[n];
		for (int i = 0; i < n; i++) {
			a[i] = new W(i, g[i].size());
		}
		left = new int[n];
		Arrays.sort(a);
		for (int centerId = 0; centerId < n; centerId++) {
			center = a[centerId].id;
			if (System.currentTimeMillis() - st > 800)
				break;
			int cost = n - g[center].size();
			Arrays.fill(left, -1);
			for (int i = 0; i < n; i++)
				if (i != center) {
					boolean has = false;
					for (int j = 0; j < g[i].size(); j++) {
						if (g[i].get(j) == center)
							has = true;
					}
					Arrays.fill(used, false);
					if (!go(i)) {
						if (has) {
							cost += g[i].size();
						} else {
							cost += g[i].size() + 2;
						}
					} else {
						if (has) {
							cost += g[i].size() - 2;
						} else {
							cost += g[i].size();
						}
					}
				}
			ans = Math.min(ans, cost);
		}
		out.println(ans);
	}

	FastScaner in;
	PrintWriter out;

	void run() {
		in = new FastScaner(System.in);
		out = new PrintWriter(System.out);

		solve();

		out.close();
	}

	void runWithFiles() {
		in = new FastScaner(new File("input.txt"));
		try {
			out = new PrintWriter(new File("output.txt"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		solve();

		out.close();
	}

	public static void main(String[] args) {
		Locale.setDefault(Locale.US);
		new CF().run();
	}

	class FastScaner {
		BufferedReader br;
		StringTokenizer st;

		FastScaner(InputStream is) {
			br = new BufferedReader(new InputStreamReader(is));
		}

		FastScaner(File f) {
			try {
				br = new BufferedReader(new FileReader(f));
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}

		String next() {
			while (st == null || !st.hasMoreElements()) {
				String s = null;
				try {
					s = br.readLine();
				} catch (IOException e) {
					e.printStackTrace();
				}
				if (s == null)
					return null;
				st = new StringTokenizer(s);
			}
			return st.nextToken();
		}

		int nextInt() {
			return Integer.parseInt(next());
		}

		long nextLong() {
			return Long.parseLong(next());
		}

		double nextDouble() {
			return Double.parseDouble(next());
		}
	}
}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.LinkedList;
import java.util.StringTokenizer;

public class GeorgeAndInterestingGraph {
    public static void main(String[] args) {
        MyScanner sc = new MyScanner();

        int N = sc.nextInt();
        int M = sc.nextInt();

        int[] edgeFrom = new int[M];
        int[] edgeTo = new int[M];
        for (int i = 0; i < M; i++) {
        	edgeFrom[i] = sc.nextInt();
        	edgeTo[i] = sc.nextInt();
        }

        int best = Integer.MAX_VALUE;
        for (int i = 0; i < N; i++) {
        	boolean[][] mat = makeAdjMat(N, edgeFrom, edgeTo);
        	best = Math.min(best, count(mat, i, M));
        }

        System.out.println(best);
    }

    public static int count(boolean[][] mat, int center, int M) {
//    	int N = mat.length;
//    	int M = mat[0].length;
//
//    	int centerConnect = (mat[center][center]) ? 0 : 1;
//    	for (int i = 0; i < N; i++) {
//			if (i != center) {
//				if (!mat[i][center]) {
//					centerConnect++;
//				}
//				if (!mat[center][i]) {
//					centerConnect++;
//				}
//			}
//	    	mat[i][center] = false;
//	    	mat[center][i] = false;
//		}
//
//    	int[][] adjMat = new int[2 * N + 2][2 * N + 2];
//    	for (int i = 0; i < N; i++) {
//    		for (int j = 0; j < N; j++) {
//    			int idx = N + j;
//    			adjMat[i][idx] = (mat[i][j]) ? 1 : 0;
//    		}
//    	}
//    	int s = 2 * N;
//    	int t = 2 * N + 1;
//    	for (int i = 0; i < N; i++) {
//    		adjMat[s][i] = 1;
//    	}
//    	for (int i = N; i < 2 * N; i++) {
//    		adjMat[i][t] = 1;
//    	}
//
//    	int matches = fordFulkerson(adjMat, s, t);
//
//    	return centerConnect + matches;

    	int N = mat.length;
//    	int M = mat[0].length;

    	int cntWithI = (mat[center][center]) ? 1 : 0;
    	for (int i = 0; i < N; i++) {
    		if (i != center) {
    			if (mat[i][center]) {
    				cntWithI++;
    			}
    			if (mat[center][i]) {
    				cntWithI++;
    			}
    		}
        	mat[i][center] = false;
        	mat[center][i] = false;
    	}

    	int other = M - cntWithI;

//    	int centerConnect = (mat[center][center]) ? 0 : 1;
//
//    	for (int i = 0; i < N; i++) {
//    		if (i != center) {
//    			if (!mat[i][center]) {
//    				centerConnect++;
//    			}
//    			if (!mat[center][i]) {
//    				centerConnect++;
//    			}
//    		}
//        	mat[i][center] = false;
//        	mat[center][i] = false;
//    	}

    	int matches = bipartiteMatching(mat);

    	return (2 * N - 1 - cntWithI + other - matches + N - 1 - matches);
//    	return (centerConnect + N - 1 - matches);
    }

    public static boolean[][] makeAdjMat(int N, int[] edgeFrom, int[] edgeTo) {
    	boolean[][] mat = new boolean[N][N];
    	for (int i = 0; i < edgeFrom.length; i++) {
    		int from = edgeFrom[i] - 1;
    		int to = edgeTo[i] - 1;
    		mat[from][to] = true;
    	}
    	return mat;
    }

    /**
     * Returns true if there is a path from the source 's' to the sink 't' in the residual graph.
     * Also fills parent[] to store the path.
     * See here for more info:  http://www.geeksforgeeks.org/ford-fulkerson-algorithm-for-maximum-flow-problem/
     */
    public static boolean fordFulkersonHelper(int[][] resid, int s, int t, int[] parent) {
    	int V = resid.length;
    	boolean[] visited = new boolean[V];
    	LinkedList<Integer> q = new LinkedList<Integer>();
    	q.push(s);
    	visited[s] = true;
    	parent[s] = -1;

    	while (!q.isEmpty()) {
    		int u = q.pop();
    		for (int v = 0; v < V; v++) {
    			if (!visited[v] && resid[u][v] > 0) {
    				q.push(v);
    				parent[v] = u;
    				visited[v] = true;
    			}
    		}
    	}

    	return visited[t];
    }

    /**
     * Returns the maximum flow from 's' to 't' in the given graph.
     * See here for more info:  http://www.geeksforgeeks.org/ford-fulkerson-algorithm-for-maximum-flow-problem/
     */
    public static int fordFulkerson(int[][] graph, int s, int t) {
    	int V = graph.length;
    	int[][] resid = new int[V][V];
    	int[] parent = new int[V];
    	int maxFlow = 0;

    	for (int u = 0; u < V; u++) {
    		for (int v = 0; v < V; v++) {
    			resid[u][v] = graph[u][v];
    		}
    	}

    	while (fordFulkersonHelper(resid, s, t, parent)) {
    		int pathFlow = Integer.MAX_VALUE;
    		for (int v = t; v != s; v = parent[v]) {
    			int u = parent[v];
    			pathFlow = Math.min(pathFlow,  resid[u][v]);
    		}
    		for (int v = t; v != s; v = parent[v]) {
    			int u = parent[v];
    			resid[u][v] -= pathFlow;
    			resid[v][u] += pathFlow;
    		}
    		maxFlow += pathFlow;
    	}

    	return maxFlow;
    }

    /**
     * Returns true if a matching for vertex 'u' is possible.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static boolean bipartiteMatchingHelper(boolean[][] bpGraph, int u, boolean[] seen, int[] matchR) {
    	int N = bpGraph[0].length;
    	for (int v = 0; v < N; v++) {
    		if (bpGraph[u][v] && !seen[v]) {
    			seen[v] = true;
    			if (matchR[v] < 0 || bipartiteMatchingHelper(bpGraph, matchR[v], seen, matchR)) {
    				matchR[v] = u;
    				return true;
    			}
    		}
    	}
    	return false;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Note:  bpGraph[i][j] = true if there is an edge from i to j.
     * Note:  matchIJ (array of length M) is an output variable containing the matchings, such that matchIJ[i] = j means that there is a match from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph, int[] matchIJ, int[] matchJI) {
    	int ans = bipartiteMatching(bpGraph, matchJI);

    	for (int i = 0; i < matchJI.length; i++) {
    		matchIJ[i] = -1;
    	}

    	for (int j = 0; j < matchJI.length; j++) {
    		int i = matchJI[j];
    		if (i >= 0) {
    			matchIJ[i] = j;
    		}
    	}

    	return ans;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Note:  bpGraph[i][j] = true if there is an edge from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph, int[] matchJI) {
    	int M = bpGraph.length;
    	int N = bpGraph[0].length;

    	for (int i = 0; i < N; i++) {
    		matchJI[i] = -1;
    	}

    	int ans = 0;
    	for (int u = 0; u < M; u++) {
    		boolean[] seen = new boolean[N];
    		if (bipartiteMatchingHelper(bpGraph, u, seen, matchJI)) {
    			ans++;
    		}
    	}

    	return ans;
    }

    /**
     * Returns the maximum bipartite matching from an an adjacency matrix.
     * Overload of the bipartiteMatching function without output parameters.
     * See here for more info:  http://www.geeksforgeeks.org/maximum-bipartite-matching/
     */
    public static int bipartiteMatching(boolean[][] bpGraph) {
    	int N = bpGraph[0].length;
    	int[] matchJI = new int[N];
    	return bipartiteMatching(bpGraph, matchJI);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     */
    public static int bipartiteMatching(int[][] intGraph) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     */
    public static int bipartiteMatching(int[][] intGraph, int[] matchJI) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph, matchJI);
    }

    /**
     * Overload of the bipartiteMatching function taking an adjacency matrix of int[][] instead of boolean[][].
     * Note:  matchIJ (array of length M) is an output variable containing the matchings, such that matchIJ[i] = j means that there is a match from i to j.
     * Note:  matchJI (array of length N) is an output variable containing the matchings, such that matchJI[j] = i means that there is a match from i to j.
     */
    public static int bipartiteMatching(int[][] intGraph, int[] matchIJ, int[] matchJI) {
    	boolean[][] bpGraph = intToBooleanAdjMat(intGraph);
    	return bipartiteMatching(bpGraph, matchIJ, matchJI);
    }

    /**
     * Converts an integer adjacency matrix of 1's and 0's to a boolean adjacency matrix.
     * Useful with bipartiteMatching, which takes adjancency matrix of boolean[][] as input (instead of int[][]).
     */
    public static boolean[][] intToBooleanAdjMat(int[][] mat) {
    	int M = mat.length;
    	int N = mat[0].length;
    	boolean[][] bMat = new boolean[M][N];
    	for (int i = 0; i < M; i++) {
    		for (int j = 0; j < N; j++) {
    			bMat[i][j] = (mat[i][j] != 0);
    		}
    	}
    	return bMat;
    }

    public static class MyScanner {
        BufferedReader br;
        StringTokenizer st;

        public MyScanner() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        String nextLine() {
            String str = "";
            try { str = br.readLine(); }
            catch (IOException e) { e.printStackTrace(); }
            return str;
        }
    }
}