参考资料:
匈牙利算法主要用求二分图的最大匹配和最小覆盖
一种形象的表述:把二分图中的一部分假设为男,另一部分为女,男生和女生之间有暧昧关系,求最大匹配就是求最多能促成多少对情侣。
先到先得,能让则让。
// 邻接矩阵存边
bool mat[maxn][maxn];
// vis[i]表示:i号女生是否已经配对
bool vis[maxn];
// p[i]表示:与i号女生配对的男生编号
int p[maxn];bool match(int i){for(int j=1;j<=n;j++){// 该女生未配对,且这对男女之间有暧昧关系if(!vis[j] && mat[i][j]){vis[j] = 1;/* match(p[j])表示让之前与i号女生配对的男生换匹配对象 */if(p[j]==0 || match(p[j])){p[j] = i;return true;}}}return false;
}int Hungarian(){int res = 0;for(int i=1;i<=n;i++){memset(vis, 0, sizeof(vis));// 统计匹配成功的男生数if(match(i)) res++;}return res;
}
给定一个由 0 和 1 构成的方阵,可以交换行和列,问能否通过交换操作使得方阵的主对角线上全为 1 。
将矩阵转化为二分图,一部分代表各行,另一部分代表各列,如果方阵有 mat[i][j] == 1 ,则在行 i 和列 j 之间建立一条边。对这个二分图求最大匹配,如果匹配数恰好等于方阵的行列数 n ,则输出 Yes ,否则输出 No 。
#include
using namespace std;const int maxn = 205;int T, n;
bool mat[maxn][maxn];
bool vis[maxn];
int p[maxn];bool match(int i){for(int j=1;j<=n;j++){if(!vis[j] && mat[i][j]){vis[j] = 1;if(p[j]==0 || match(p[j])){p[j] = i;return true;}}}return false;
}int Hungarian(){int res = 0;for(int i=1;i<=n;i++){memset(vis, 0, sizeof(vis));if(match(i)) res++;}return res;
}int main(){cin>>T;while(T--){cin>>n;memset(p, 0, sizeof(p));for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){bool a;cin>>a;if(a) mat[i][j] = 1;else mat[i][j] = 0;}}if(Hungarian() == n) cout<<"Yes";else cout<<"No";cout<<'\n';} return 0;
}
找到最少的点,删掉所有包含这些点的边,可以删掉所有边。
结论:最小覆盖的点数等于最大匹配数。
找到最小覆盖点集的方法:从左侧一个未匹配成功的点出发,走一趟匈牙利算法的流程(即紫色的箭头),所有左侧未经过的点,和右侧经过的点,即组成最小点覆盖。
给定一个由 0 和 1 构成的 n*m 的矩阵,每次操作可以一行或一列的 1 全部变成 0 。问最少经过多少次操作才能使矩阵全 0 。
仿照矩阵游戏建立二分图,每次操作等价于删除所有包含该点的边,此时问题就转化成了求二分图的最小覆盖。
略。