[ZJOI2007]矩阵游戏
这道题是一个不错的题,难点就在于建模。
交换操作过程中,同一行的黑块是不会被拆开,同理纵块也是。
接着目标状态就是一条对角线上全都是黑块。
我们倒过来想,看看能否从目标状态变成初始状态。
对于所有的黑块$(x,y)$(左边行右边列,点分行列),我们连条边$x \leftarrow \rightarrow y$。
显然目标状态应该是完全匹配的。
交换两行$i$、$j$,所有在行$i$、$j$的黑块的横坐标交换,纵坐标不变就是将所有指向行$i$的点的指向改成行$j$,所有指向行$j$的点改指向行$i$。
同理纵坐标也是。
等效替代下发现实质上与交换点$i$和点$j$的效果一样。
也就是从目标状态无论怎样变换匹配数不变且满。
且以上操作可逆。
所以我们检查下原图是否能全部匹配即可。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 200 + 5; 6 7 int vis[maxn], lk[maxn], e[maxn][maxn]; 8 int n, T; 9 10 bool find(int u, int tag) { 11 for (register int v = 1; v <= n; ++v) 12 if (e[u][v] && vis[v] != tag) { 13 vis[v] = tag; 14 if (!lk[v] || find(lk[v], tag)) { 15 lk[v] = u; 16 return true; 17 } 18 } 19 return false; 20 } 21 22 int main() { 23 scanf("%d", &T); 24 for (register int kase = 1; kase <= T; ++kase) { 25 scanf("%d", &n); 26 for (register int i = 1; i <= n; ++i) 27 for (register int j = 1; j <= n; ++j) { 28 int x; 29 scanf("%d", &x); 30 e[i][j] = x; 31 } 32 memset(vis, 0, sizeof(vis)); 33 memset(lk, 0, sizeof(lk)); 34 register int cnt = 0; 35 for (register int i = 1; i <= n; i++) 36 if (find(i, i)) cnt++; 37 if (cnt == n) printf("Yes\n"); 38 else printf("No\n"); 39 } 40 return 0; 41 }