排列数字
利用深搜,设置一个数组用来存储走过的“路径”,
还有一个标记数组用来看这个数有没有用过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> using namespace std;int n; const int N = 10 ;int path[N], st[N];void dfs (int x) { if (x > n) { for (int i = 1 ; i<= n; i++) cout << path[i] << " " ; cout << endl; return ; } for (int i = 1 ; i <= n; i++) { if (!st[i]) { path[x] = i; st[i] = true ; dfs (x + 1 ); st[i] = false ; } } } int main () { cin >> n; dfs (1 ); return 0 ; }
n皇后
设置三个参数,一个用来表示横行、一个竖行、一个表示当前已经有的皇后个数,一有冲突就让y++,当y走到头就让y重新回到0,然后让x向下移,设置四个bool数组,表示横行、纵行、正斜线、反斜线是否已经放了皇后
法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <iostream> #include <cstring> using namespace std;const int N = 20 ;char place[N][N];bool row[N], col[N], dg[N], udg[N];int n; void dfs (int x, int y, int s) { if (y == n) y = 0 , x++; if (x == n) { if (s == n) { for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < n; j++) { cout << place[i][j]; } cout << endl; } cout << endl; } return ; } if (!row[x] && !col[y] && !dg[x - y + n] && ! udg[x + y]) { row[x] = col[y] = dg[x - y + n] = udg[x + y] = true ; place[x][y] = 'Q' ; dfs (x, y + 1 , s + 1 ); row[x] = col[y] = dg[x - y + n] = udg[x + y] = false ; place[x][y] = '.' ; } dfs (x , y + 1 , s); } int main () { cin >> n; for (int i = 0 ;i < n; i++) for (int j = 0 ; j < n; j++) place[i][j] = '.' ; dfs (0 , 0 , 0 ); return 0 ; }
法二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> using namespace std;const int N = 20 ;char place[N][N];bool row[N], col[N], dg[N], udg[N];int n;void dfs (int x) { if (x == n) { for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < n; j++) { cout << place[i][j]; } cout << '\n' ; } cout << '\n' ; return ; } for (int y = 0 ; y < n; y++) { if (!row[x] && !col[y] && !dg[x + y] && !udg[y - x + n]) { row[x] = col[y] = dg[x + y] = udg[y - x + n] = true ; place[x][y] = 'Q' ; dfs (x + 1 ); row[x] = col[y] = dg[x + y] = udg[y - x + n] = false ; place[x][y] = '.' ; } } } int main () { cin >> n; for (int i = 0 ; i < n; i++) for (int j = 0 ; j < n; j++) place[i][j] = '.' ; dfs (0 ); return 0 ; }
走迷宫
广度优先搜索,利用队列,先将第一层的数加入到队列中,然后在出队的时候把与第一层有关的第二层的数加入到队列中,“探索”它的上下左右,遍历到的地方不遍历第二遍,因为是层序遍历,第一次遍历到的一定是最小的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> #include <algorithm> #include <queue> #include <cstring> using namespace std;const int N = 110 ;int g[N][N];int d[N][N];typedef pair<int , int > PII;queue<PII> q; int n, m;int dx[4 ] = {0 , 0 , 1 , -1 };int dy[4 ] = {1 , -1 , 0 , 0 };int bfs () { q.push ({0 , 0 }); d[0 ][0 ] = 0 ; while (q.size ()) { auto [a, b] = q.front (); q.pop (); for (int i = 0 ; i <4 ; i++) { int x = a + dx[i], y = b + dy[i]; if (x >= 0 && x < n && y >= 0 && y < m && !g[x][y] && d[x][y] == -1 ) { d[x][y] = d[a][b] + 1 ; q.push ({x, y}); } } } return d[n - 1 ][m - 1 ]; } int main () { memset (d, -1 , sizeof (d)); cin >> n >> m; for (int i = 0 ; i < n; i++) for (int j = 0 ; j < m; j++) cin >> g[i][j]; cout << bfs (); return 0 ; }
八数码
将初始的状态和最后的状态转换成字符串,找到字符串中字符的位置和9宫格中字符位置的关系,对字符串进行操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <iostream> #include <cstring> #include <queue> #include <unordered_map> using namespace std;string ed = "12345678x" ; int dx[4 ] = {1 , -1 , 0 , 0 };int dy[4 ] = {0 , 0 , 1 , -1 };unordered_map<string, int > d; queue<string> q; string start; int bfs () { d[start] = 0 ; q.push (start); while (q.size ()) { string t = q.front (); q.pop (); int dis = d[t]; if (t == ed) return dis; int k = t.find ('x' ); for (int i = 0 ; i < 4 ; i++) { int a = k / 3 , b = k % 3 ; int x = a + dx[i], y = b + dy[i]; if (x >= 0 && x < 3 && y >= 0 && y < 3 ) { swap (t[k], t[x * 3 + y]); if (!d.count (t)) { d[t] = dis + 1 ; q.push (t); } swap (t[k], t[x * 3 + y]); } } } return -1 ; } int main () { for (int i = 0 ; i < 9 ; i++) { char x; cin >> x; start += x; } cout << bfs (); return 0 ; }
树的重心
每个结点的每个分支都是一个连通块,剩下的上面那部分是另一个连通块;对于每个结点,找到它所区分的连通块中的最大值;对于所有结点,将他们对应的连通块最大值进行比较,找到其中的最小值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> #include <cstring> using namespace std;const int N = 1e5 + 10 , M = 2 * N;int h[N], to[M], ne[M], idx;bool st[N];int n, ans = N;void add (int x, int y) { to[idx] = y; ne[idx] = h[x]; h[x] = idx++; } int dfs (int u) { st[u] = true ; int size = 0 , sum = 0 ; for (int i = h[u]; i != -1 ; i = ne[i]) { int j = to[i]; if (st[j]) continue ; int s = dfs (j); size = max (size, s); sum += s; } size = max (size, n - sum - 1 ); ans = min (ans, size); return sum + 1 ; } int main () { memset (h, -1 , sizeof h); cin >> n; for (int i = 0 ; i < n - 1 ; i++) { int a, b; cin >> a >> b; add (a, b); add (b, a); } dfs (1 ); cout << ans; return 0 ; }
图中点的层次
图的广度优先搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <iostream> #include <cstring> #include <queue> using namespace std;const int N = 1e5 + 10 , M = N * 2 ;int to[N], h[M], ne[M], idx;int d[N];int n, m;void add (int a, int b) { to[idx] = b, ne[idx] = h[a], h[a] = idx++; } int bfs (int x) { d[1 ] = 0 ; queue<int > q; q.push (1 ); while (q.size ()) { int t = q.front (); q.pop (); for (int i = h[t]; i != -1 ; i = ne[i]) { int j = to[i]; if (d[j] == -1 ) { d[j] = d[t] + 1 ; q.push (j); } } } return d[n]; } int main () { memset (h, -1 , sizeof h); memset (d, -1 , sizeof d); cin >> n >> m; while (m--) { int a, b; cin >> a >> b; add (a, b); } cout << bfs (1 ); return 0 ; }
图的拓扑排序
每次将度为0的节点加入到“队列”中
用链式前向星存图,不要忘记初始化head数组!!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> #include <cstring> using namespace std;const int N = 1e5 + 10 , M = 2 * N;int to[N], ne[M], head[M], idx;int d[N], q[N];int n, m;void add (int x, int y) { to[idx] = y, ne[idx] = head[x], head[x] = idx++; } bool TopSort () { int hh = 0 , tt = -1 ; for (int i = 1 ; i <= n; i++) if (!d[i]) q[++tt] = i; while (hh <= tt) { int t = q[hh++]; for (int i = head[t]; i != -1 ; i = ne[i]) { int j = to[i]; if (--d[j] == 0 ) q[++tt] = j; } } return tt == n - 1 ; } int main () { memset (head, -1 , sizeof head); cin >> n >> m; while (m--) { int a, b; cin >> a >> b; add (a, b); d[b]++; } if (!TopSort ()) cout << -1 ; else for (int i = 0 ; i < n; i++) cout << q[i] << " " ; return 0 ; }
Dijkstra求最短路\(O(n^2)\)
设置一个集合,st为true就是在集合里面,否则就是不在那个集合里面;每次求不在集合里面的点到1结点的最短距离,加入到集合中之后,更新与这个结点有关的距离,直到所有点都在集合中(贪心的策略)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <cstring> using namespace std;const int N = 510 ;int g[N][N];bool st[N];int dist[N];int n, m;int dijkstra () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; for (int i = 0 ; i < n - 1 ; i++) { int t = -1 ; for (int j = 1 ; j <= n; j++) { if (!st[j] && (t == -1 || dist[j] < dist[t])) t = j; } st[t] = true ; for (int j = 1 ; j <= n; j++) { if (dist[j] > dist[t] + g[t][j]) dist[j] = dist[t] + g[t][j]; } } if (dist[n] == 0x3f3f3f3f ) return -1 ; else return dist[n]; } int main () { cin >> n >> m; memset (g, 0x3f3f , sizeof g); while (m--) { int x, y, z; cin >> x >> y >> z; g[x][y] = min (g[x][y], z); } cout << dijkstra (); return 0 ; }
Dijsktra求最短路(堆优化版)(O(mlogm))
用priority_queue进行优化,使得找到距离1最近点的时间复杂度变为O(1),以类似于bfs的形式进行遍历,更新距离,加入到队列中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> #include <cstring> #include <queue> #include <algorithm> using namespace std;const int N = 1e5 + 5e4 + 10 , M = 2 * N;int to[N], h[M], ne[M], w[M], idx;int dist[N];bool st[N];int n, m; typedef pair<int , int > PII;priority_queue<PII, vector<PII>, greater<PII>> q; void add (int x, int y, int z) { to[idx] = y; ne[idx] = h[x]; w[idx] = z; h[x] = idx++; } int dijkstra () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; q.push ({0 , 1 }); while (q.size ()) { auto t = q.top (); int d = t.first, x = t.second; q.pop (); if (st[x]) continue ; st[x] = true ; for (int i = h[x]; i != -1 ; i = ne[i]) { int j = to[i]; if (dist[j] > dist[x] + w[i]) { dist[j] = dist[x] + w[i]; q.push ({dist[j], j}); } } } if (dist[n] != 0x3f3f3f3f ) return dist[n]; return -1 ; } int main () { memset (h, -1 , sizeof h); cin >> n >> m; while (m--) { int x, y, z; cin >> x >> y >> z; add (x, y, z); } cout << dijkstra (); return 0 ; }
Spfa(处理存在负边权的情况)(O(mk))
因为边权可能为负值,只能用spfa算法,不能用dijkstra算法;spfa算法是以“层序”的方式进行不断更新节点距离,也就是说,会用某一层的所有结点去更新下一层次的节点,而不是像dijkstra一样使用贪心的策略,这样就可以保证即使前面的边权大、后面有负边权也可以更新到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> #include <cstring> #include <queue> using namespace std;const int N = 1e5 + 10 ;int to[N], ne[N], w[N], head[N], idx;int dist[N];bool st[N];int n, m;queue<int > q; void add (int x, int y, int z) { to[idx] = y, ne[idx] = head[x], w[idx] = z, head[x] = idx++; } void spfa () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; st[1 ] = true ; q.push (1 ); while (q.size ()) { int t = q.front (); q.pop (); st[t] = false ; for (int i = head[t]; i != -1 ; i = ne[i]) { int j = to[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; if (!st[j]) { st[j] = true ; q.push (j); } } } } } int main () { memset (head, -1 , sizeof head); cin >> n >> m; while (m--) { int x, y, z; cin >> x >> y >> z; add (x, y, z); } spfa (); if (dist[n] > 0x3f3f3f3f / 2 ) cout << "impossible" ; else cout << dist[n]; return 0 ; }
Spfa判断是否存在负环(O(n)~O(mn))
如果存在负环,那么一旦进入这个负环,就会一直循环,因为只要在负环里面dist就会一直减,所以可以计算每个节点距离最短的路上有几个结点,如果大于等于n必定存在负环,因为如果只存在正环或者不存在环,那么最长的应该是n-1;因为负环不一定是从结点1开始的,所以在起始的时候需要把所有结点加入到队列中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include <iostream> #include <queue> #include <cstring> using namespace std;const int N = 2010 ,M = 10010 ;int to[M], ne[M], w[M], head[N], idx;bool st[N];int dist[N], cnt[N];int n, m;void add (int x, int y, int z) { to[idx] = y, ne[idx] = head[x], w[idx] = z, head[x] = idx++; } bool spfa () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; queue<int > q; for (int i = 1 ; i <= n; i++) { q.push (i); st[i] = true ; } while (q.size ()) { int t = q.front (); q.pop (); st[t] = false ; for (int i = head[t]; i != -1 ; i = ne[i]) { int j = to[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; cnt[j] = cnt[t] + 1 ; if (cnt[j] >= n) return true ; if (!st[j]) { q.push (j); st[j] = true ; } } } } return false ; } int main () { memset (head, -1 , sizeof head); cin >> n >> m; while (m--) { int x, y, z; cin >> x >> y >> z; add (x, y, z); } if (spfa ()) cout << "Yes" ; else cout << "No" ; return 0 ; }
Spfa的SLF优化
对于要加入到队列中的u,如果dist[u]要小于队首元素v的dist[v],则加入到队头,否则加入到队尾;这时候就需要用双端队列dque
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 deque<int > q; void spfa () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; st[1 ] = true ; q.push_back (1 ); while (q.size ()) { int t = q.front (); q.pop_front (); st[t] = false ; for (int i = head[t]; i != -1 ; i = ne[i]) { int j = to[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; if (!st[j]) { st[j] = true ; if (q.size () && dist[j] < dist[q.front ()]) q.push_front (j); else q.push_back (j); } } } } }
BellmanFord(处理有限制条件的情况)(O(mn))
利用一个循环,保证循环k次,每一次只往后“走”一步,并且每次更新需要用上一次更新的结果更新这一次的结果(不是用这一次更新这一次),所以需要每次先拷贝再更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> #include <cstring> using namespace std;const int N = 510 , M = 10010 ;int n, m, k;int dist[N], cpy[N];struct Edge { int a, b, w; }edges[M]; void BellmanFord () { memset (dist, 0x3f3f , sizeof dist); dist[1 ] = 0 ; for (int i = 0 ; i < k; i++) { memcpy (cpy, dist, sizeof dist); for (int j = 0 ; j < m; j++) { auto e = edges[j]; dist[e.b] = min (dist[e.b], cpy[e.a] + e.w); } } } int main () { cin >> n >> m >> k; int x, y, z; for (int i = 0 ; i < m; i++) { cin >> x >> y >> z; edges[i].a = x, edges[i].b = y, edges[i].w = z; } BellmanFord (); if (dist[n] > 0x3f3f3f3f / 2 ) puts ("impossible" ); else cout << dist[n]; return 0 ; }
Floyd\((O(n^3))\)
不能处理负边权回路
\(d[i][j]\) 表示从i到j的距离
floyd中必须先遍历k,因为最后遍历k会导致更新i、j时的距离不是最优距离
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> using namespace std;const int N = 210 , M = 20010 , INF = 1e9 ;int d[N][N];int n, m, x, y, z;void floyd () { for (int k = 1 ; k <= n; k++) { for (int i = 1 ; i <= n; i++) { for (int j = 1 ; j <= n; j++) { d[i][j] = min (d[i][j], d[i][k] + d[k][j]); } } } } int main () { int k; cin >> n >> m >> k; for (int i = 1 ; i <= n; i++) { for (int j = 1 ; j <= n; j++) { if (i == j) d[i][j] = 0 ; else d[i][j] = INF; } } while (m--) { cin >> x >> y >> z; d[x][y] = min (d[x][y], z); } floyd (); while (k--) { cin >> x >> y; if (d[x][y] > INF / 2 ) cout << "impossible" << '\n' ; else cout << d[x][y] << '\n' ; } return 0 ; }
Prim求最小生成树
类似于dijkstra算法,每次循环找到一个最近的节点加入到最小生成树中,要考虑孤立点!!!需判断这个点的距离是不是无穷大,即判断是否连通,如果不联通就输出impossible,如果符合要求就更新距离
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> #include <cstring> using namespace std;const int N = 510 , M = 1e5 + 10 ;int dist[N], st[N];int g[N][N];int n, m, u, v, w;void prim () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; int res = 0 ; for (int i = 0 ; i < n; i++) { int t = -1 ; for (int j = 1 ; j <= n; j++) { if (!st[j] && (t == -1 || dist[j] < dist[t])) t = j; } if (dist[t] == 0x3f3f3f3f ) { cout << "impossible" ; return ; } st[t] = 1 ; res += dist[t]; for (int i = 1 ; i <= n; i++) { if (dist[i] > g[i][t] && !st[i]) dist[i] = g[i][t]; } } cout << res; } int main () { cin >> n >> m; memset (g, 0x3f , sizeof g); while (m--) { cin >> u >> v >> w; g[u][v] = g[v][u] = min (g[u][v], w); } prim (); return 0 ; }
Kruskal求最小生成树
根据边的权值大小从小到大排序
每次取一条边,利用并查集判断是否会形成环(即它的两个节点是否在一个集合)
最后根据取的边的数量,如果小于n - 1就不连通,无最小生成树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <iostream> #include <algorithm> using namespace std;const int N = 1e5 + 20 ;int p[N];struct E { int a, b, w; bool operator < (const E& e) { return this ->w < e.w; } }edges[N * 2 ]; int n, m;int res = 0 , cnt = 0 ;int find (int x) { if (x != p[x]) p[x] = find (p[x]); return p[x]; } void Kruskal () { for (int i = 1 ; i <= m; i++) { int pa = find (edges[i].a); int pb = find (edges[i].b); if (pa != pb) { res += edges[i].w; cnt++; p[pa] = pb; } } } int main () { cin >> n >> m; for (int i = 1 ; i <= n; i++) p[i] = i; for (int i = 1 ; i <= m; i++) { int u, v, w; cin >> u >> v >> w; edges[i] = {u, v, w}; } sort (edges + 1 , edges + m + 1 ); Kruskal (); if (cnt < n - 1 ) cout << "impossible" ; else cout << res; return 0 ; }
染色法判定二分图
利用dfs,没染色设为0,a颜色为1,b颜色为2,两个相连,一个染a一个染b,即一个是1,另一个是3
-
1;进行遍历,未染就进行染色,并更改周围的颜色;染色了,就判断染的颜色是否有矛盾
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <iostream> #include <cstring> using namespace std;const int N = 100010 * 2 ;int to[N], ne[N], head[N], idx;int n, m;int color[N];void add (int x, int y) { to[idx] = y, ne[idx] = head[x], head[x] = idx++; } bool dfs (int u, int c) { color[u] = c; for (int i = head[u]; i != -1 ; i = ne[i]) { int j = to[i]; if (!color[j]) { if (!dfs (j, 3 - c)) return false ; } else if (color[j] && color[j] != 3 - c) return false ; } return true ; } int main () { memset (head, -1 , sizeof head); cin >> n >> m; while (m--) { int x, y; cin >> x >> y; add (x, y); add (y, x); } for (int i = 1 ; i <= n; i++) { if (!color[i]) { if (!dfs (i, 1 )) { cout << "No" ; return 0 ; } } } cout << "Yes" ; return 0 ; }
二分图的最大匹配
好题解!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> #include <cstring> using namespace std;const int N = 1e5 + 10 ;int to[N], ne[N], head[N], idx;int n1, n2 ,m;int st[N], match[N];void add (int x, int y) { to[idx] = y, ne[idx] = head[x], head[x] = idx++; } bool find (int x) { for (int i = head[x]; i != -1 ; i = ne[i]) { int j = to[i]; if (!st[j]) { st[j] = 1 ; if (match[j] == 0 || find (match[j])) { match[j] = x; return true ; } } } return false ; } int main () { memset (head, -1 , sizeof head); cin >> n1 >> n2 >> m; while (m--) { int a, b; cin >> a >> b; add (a, b); } int res = 0 ; for (int i = 1 ; i <= n1; i++) { memset (st, 0 , sizeof st); if (find (i)) res++; } cout << res; return 0 ; }