/** 题目:SAM I AM UVA - 11419 链接:https://vjudge.net/problem/UVA-11419 题意:给定n*n的矩阵,'X'表示障碍物,'.'表示空格;你有一把枪,每一发子弹可以消除一行或者一列的障碍物, 问最少需要多少颗子弹可以清空障碍物?以及输出具体的哪些行,哪些列。 思路:最小点集覆盖问题,等价于最大匹配。 求具体的哪些行,哪些列,需要借助于匈牙利树。从X中所有未匹配的点出发扩展匈牙利树,标记 树中的所有点,则X中所有的未标记点和Y中的所有标记点就是结果。证明。。留给读者思考~~~(来自lrj训练指南P356) */ #include<iostream> #include<cstdio> #include<algorithm> #include<map> #include<vector> #include<queue> #include<set> #include<cstring> using namespace std; const int MAXN = 1005; int f[MAXN][MAXN]; int vis[MAXN], vit[MAXN], S[MAXN], T[MAXN], flag[MAXN]; int N; ///模板 bool Find(int x)///走交替路,寻找增广路 { vis[x] = 1;///标记走交替路过程中左边经过的点。 for(int i = 1; i <= N; i++){///n表示右侧点数。 if(f[x][i]&&vit[i]==0){ vit[i] = 1;///标记走交替路过程中右边经过的点。 if(T[i]==0||Find(T[i])){ T[i] = x;///右边第i个点和左边第x个点匹配成功。 S[x] = i;///左边第x个点和右边第i个点匹配成功。 return true; } } } return false; } int main() { int n, m, k; while(scanf("%d%d%d",&n,&m,&k)==3) { if(n==0&&m==0&&k==0) break; N = n; int x, y; memset(f, 0, sizeof f); for(int i = 1; i <= k; i++){ scanf("%d%d",&x,&y); f[x][y] = 1; } int ans = 0; memset(T, 0, sizeof T); memset(S, 0, sizeof S); memset(flag, 0, sizeof flag); ///模板 for(int i = 1; i <= N; i++){ memset(vis, 0, sizeof vis); memset(vit, 0, sizeof vit); if(Find(i)) ans++; } printf("%d",ans); memset(vis, 0, sizeof vis); memset(vit, 0, sizeof vit); for(int i = 1; i <= N; i++){ if(S[i]==0){///从未匹配点出发,走交替路,不可能找到增广路,否则原先的匹配不是最大的。所以S,T数组的值不会更新。 Find(i); } } for(int i = 1; i <= N; i++){ if(vis[i]==0){ printf(" r%d",i); } } for(int i = 1; i <= N; i++){ if(vit[i]){ printf(" c%d",i); } } printf(" "); } return 0; }