[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 }

 

posted @ 2019-02-03 20:31  AC-Evil  阅读(129)  评论(0编辑  收藏  举报