解题思路:
首先不知道最大匹配数概念的可以看看这篇博客:
(5条消息) 二分图及其多个扩展用法详解 + 模板题:算法竞赛进阶指南 关押罪犯 棋盘覆盖 机器任务 骑士放置 捉迷藏_wsh1931的博客-CSDN博客
若在坐标(i, j)上的数为1,则从i -> j上连一条边,最后做一遍最大匹配数,若最大匹配数 == n说明有解,否则无解。
解释:
存在最大匹配数则说明每个横坐标的点,都会向纵坐标的点连一条边,即:假如有3行3列,如图:左边为行,右边为列
最后求最大匹配数都可以变为:
即行1列2存在的数为1,行2列1存在的数为1,行3列3存在的数为1,化为矩阵
如图:
可以发现它们每行/每列交换都不影响其他的1,所以最终都可以移到对角线上
代码如下:
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N = 210, M = N * N; int n; bool st[N]; int match[N]; int h[N], e[M], ne[M], idx; void add(int a, int b)//邻接表模板 { e[idx] = b; ne[idx] = h[a]; h[a] = idx; idx ++ ; } bool find(int t)//二分图模板 { for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; if (st[j]) continue; st[j] = true; if (!match[j] || find(match[j])) { match[j] = t; return true; } } return false; } int main() { int T; cin >> T; while (T -- ) { int n; scanf("%d", &n); int idx = 0; memset(h, -1, sizeof h); memset(match, 0, sizeof match); for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= n; j ++ ) { int x; scanf("%d", &x); if (x) add(i, j);//若为1则连边 } int res = 0; for (int i = 1; i <= n; i ++ ) { memset(st, false, sizeof st); if (find(i)) res ++ ;//寻找最大匹配数 } if (res == n) puts("Yes");//最大匹配数等于n说明有解 else puts("No"); } return 0; }