看到题目想了一会感觉就是二分图的方向,但是这个图的建模抽象了好久。
后面我直接对等于1的行列连边,就发现了这样连边是合法的。且需要明白的是交换行的操作实际上是和交换列等价的,所以我们只需要去交换列判断是否成立即可
对于每个行i,我们要找到一列能使他这一行的第i列为1,那么仔细思考之后发现,其实就是找到的这一列和第i列交换。
然后就可以建图去匹配了。
这里还有一个麻烦的地方,就是在一次交换之后,可能已经改变了原有的行列。
这时候需要转移匹配。
也就是说,如果当前为1 匹配 4,4 匹配 2.
那么在第一次交换1,4后1的匹配应该变成2,而4的匹配应该变成4.
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 5e6 + 5; const LL Mod = 1e9 + 7; #define INF 1e9 #define dbg(x) cout << "now this num is " << x << endl; inline LL read() { LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();} return x * f; } int n,match[105],vis[105]; vector<int> G[105]; bool Find(int x) { for(auto v : G[x]) { if(vis[v]) continue; vis[v] = 1; if(match[v] == -1 || Find(match[v])) { match[v] = x; return true; } } return false; } bool solve() { int ans = 0; memset(match,-1,sizeof(match)); for(int i = 1;i <= n;++i) { memset(vis,0,sizeof(vis)); if(Find(i)) ans++; } return ans == n; } int main() { while(~scanf("%d",&n)) { for(int i = 1;i <= n;++i) G[i].clear(); for(int i = 1;i <= n;++i) for(int j = 1;j <= n;++j) { int x;x = read(); if(x == 1) G[i].push_back(j); } if(solve()) { vector<pii> vec; for(int i = 1;i <= n;++i) { if(i != match[i]) { for(int j = 1;j <= n;++j) { if(match[j] == i) { vec.push_back(pii{i,j}); swap(match[i],match[j]); break; } } } } printf("%d ",vec.size()); for(int i = 0;i < vec.size();++i) printf("C %d %d ",vec[i].first,vec[i].second); } else printf("-1 "); } return 0; }