匈牙利算法。在二分图中每行对应一个节点,每列对应一个节点,每个1对应一条连接该1所在行和列节点的边。如果最大匹配数==n则可行,否则不行。
对于可行的情况,只需要每次交换两个列节点的编号,直到所有的匹配都是行号==列号的为止。
View Code
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
#define maxn 105
int n, ptn[maxn], ans[maxn][2], tot;
bool vis[maxn];
vector< vector<int> > G(maxn);
void input()
{
for (int i = 0; i < n; i++)
G[i].clear();
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
{
int a;
scanf("%d", &a);
if (a == 1)
G[i].push_back(j);
}
}
bool find(int a)
{
for (int i = 0; i < G[a].size(); i++)
if (!vis[G[a][i]])
{
if (ptn[G[a][i]] == -1)
{
ptn[G[a][i]] = a;
return true;
}
vis[G[a][i]] = true;
if (find(ptn[G[a][i]]))
{
ptn[G[a][i]] = a;
return true;
}
}
return false;
}
void work()
{
tot = 0;
while (1)
{
int a = -1;
for (int i = 0; i < n; i++)
if (ptn[i] != i)
{
a = i;
break;
}
if (a == -1)
return;
while (ptn[a] != a)
{
ans[tot][0] = a;
ans[tot][1] = ptn[a];
tot++;
int x = ptn[a];
swap(ptn[a], ptn[x]);
}
}
}
int main()
{
//freopen("D:\\t.txt", "r", stdin);
while (scanf("%d", &n) != EOF && n != -1)
{
input();
int ansi = 0;
memset(ptn, -1, sizeof(ptn));
for (int i = 0; i < n; i++)
{
memset(vis, 0, sizeof(vis));
if (find(i))
ansi++;
}
if (ansi < n)
{
printf("-1\n");
continue;
}
work();
printf("%d\n", tot);
for (int i = 0; i < tot; i++)
printf("C %d %d\n", ans[i][0] + 1, ans[i][1] + 1);
}
return 0;
}