Description
Input
Output
题意:给出一个0/1矩阵,问是否可以通过不断交换两行或两列得到一个左上右下对角线全是1的矩阵。如果可以,输出任意方案。(R a b交换a,b两行,C a b交换a,b两列)。有special judge,要求步骤数小于1000。
这题我一开始根本不觉得这和二分图有什么关系。。这几天搜索题做多了,第一反应是构造h函数。。然而这题确实巧妙。你可以先假定有一个主对角线全是1其他全是0的矩阵,然后乱交换几次看看,就有规律了:无论交换哪两行或者两列,得到的都是一个置换矩阵(每行每列仅一个1)。那么问题就显然是求原矩阵中是否包含一个置换矩阵。由于每行每列仅一个一,就将原图中所有行作为X部,列作为Y部,对矩阵中每个1将其行和列连边,检查是否满流即可。
至于输出方案,将那个置换矩阵取出来,再模拟一次给所有1的y坐标升序排序的过程即可。为了让交换次数尽量少,建议手写选择排序。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MAXN 250 #define MAXM 51000 int N, M, cnt; int mat[MAXN][MAXN]; struct Node { int to; Node*next; }; struct BiGraph { int match[MAXN], xn; bool vis[MAXN]; Node Edge[MAXM*2], *ecnt, *adj[MAXN]; BiGraph() { memset(adj,0,sizeof adj); ecnt=Edge; } void addedge(int a, int b) { ++ecnt; ecnt->to = b; ecnt->next = adj[a]; adj[a] = ecnt; } bool dfs(int u) { for (Node*p = adj[u]; p; p=p->next) { int &v = p->to; if (vis[v]) continue; vis[v] = 1; if (!match[v] || dfs(match[v])) { match[u] = v; match[v] = u; return 1; } } return 0; } int Maxmatch() { int ans = 0; for (int i = 1; i<=xn; ++i) if (!match[i]) { memset(vis, 0, sizeof vis); ans += dfs(i); } return ans; } } Empty, G; int a[MAXN]; int x1[1000], x2[1000]; int main() { int i, j; while (~scanf("%d", &N)) { G = Empty; G.xn = N; M = cnt = 0; for (i = 1; i<=N; ++i) for (j = 1; j<=N; ++j) { scanf("%d", &mat[i][j]); if (mat[i][j]) G.addedge(i, j+N); } if (G.Maxmatch() < N) { puts("-1"); continue; } for (i = 1; i<=N; ++i) for (j = 1; j<=N; ++j) if (mat[i][j] && G.match[i]==j+N) a[++cnt] = j; for (i = 1; i<=N; ++i) { if (a[i] == i) continue; for (j = i; j<=N; ++j) if (a[j] == i) { swap(a[i], a[j]); ++M; x1[M] = i; x2[M] = j; break; } } printf("%d\n", M); for (i = 1; i<=M; ++i) printf("R %d %d\n", x1[i], x2[i]); } return 0; }