矩阵游戏|ZJOI2007|BZOJ1059|codevs1433|luoguP1129|二分图匹配|匈牙利算法|Elena

时间:2023-03-09 15:19:30
矩阵游戏|ZJOI2007|BZOJ1059|codevs1433|luoguP1129|二分图匹配|匈牙利算法|Elena

1059: [ZJOI2007]矩阵游戏

Time Limit: 10 Sec  Memory Limit: 162 MB

Description

  小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏。矩阵游戏在一个N
*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:行交换操作:选择
矩阵的任意两行,交换这两行(即交换对应格子的颜色)列交换操作:选择矩阵的任意行列,交换这两列(即交换
对应格子的颜色)游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑
色。对于某些关卡,小Q百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!!于是小Q决定写一个程
序来判断这些关卡是否有解。

Input

  第一行包含一个整数T,表示数据的组数。接下来包含T组数据,每组数据第一行为一个整数N,表示方阵的大
小;接下来N行为一个N*N的01矩阵(0表示白色,1表示黑色)。

Output

  输出文件应包含T行。对于每一组数据,如果该关卡有解,输出一行Yes;否则输出一行No。

Sample Input

2
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0

Sample Output

No
Yes
【数据规模】
对于100%的数据,N ≤ 200

思路:这个题目表面上看叫人丝毫没有丝绸之路qwq可是网络流啊匈牙利算法啥的题目写多了被套路多了之后就会变得熟练_(:з」∠)_

题目中给我们的操作是可以交换任意两行和任意两列,我们可以确定同一行的两个格子永远在同一行,同一列的也是一样,同一行的两个格子永远不会跑到不同的两行里去,所以两个格子我们只能取一个来用,另外一个是没有作用的,不存在把这个格子放在这个对角线的一个地方,另一个格子放在对角线上的另一个地方。因为对角线上的格子横纵坐标都是不相同的。

题目中要求的形状是正对角线也就是从左上角到右下角的连线。我们可以把每一个1格子的横坐标和纵坐标连边,然后匈牙利算法找出二分图最大匹配,如果最大匹配数等于n,则答案是yes,反之则是no。

为什么最大匹配数为n就是yes呢?因为我们可以这么想:假设有一个3*3的矩阵,从左上角到右下角的连线经过的格子依次是(1,1),(2,2)和(3,3)这3个格子。假如这时候(1,2)也有一个1号格子,照我们前面的说法,把这些格子的横纵坐标依次连边。然后照二分图匹配的思想,如果(1,2)配对成功的话,就只能配对成(1,2)和(3,3)这两对了,也只取了这两个格子,无法组成连线,是不符合条件的,所以(1,2)不能配对,我们应该找最大匹配。

因为当最大匹配数等于n时,就说明正好有n个格子的横纵坐标是匹配的,这些格子的横纵坐标不会相同,就会组成一条线,我们可以把交换行列看成改变格子的位置,所以如果组成的是左下角到右上角的线,可以通过交换列来达到理想位置。那条线符合答案要求。因为我们可以通过换行换列来使格子到达该到的地方。可以通过交换行列来改变格子的位置,但是同行列的格子间会受到影响,所以只有不同行列的格子改变到理想的位置后不会影响到其他的格子。

 #include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
int read()
{
int x=,f=; char c=getchar();
while (c<''||c>'') {if (c=='-') f=-; c=getchar();}
while (c>=''&&c<='') {x=x*+c-'';c=getchar();}
return x*f;
}
int num_edge,head[],T,sum,n,match[];
bool book[];
struct Edge
{
int next;
int to;
}edge[];
void add_edge(int from,int to)
{
edge[++num_edge].next=head[from];
edge[num_edge].to=to;
head[from]=num_edge;
}
bool dfs(int u)
{
for (int i=head[u]; i; i=edge[i].next)
if (book[edge[i].to]==) {
book[edge[i].to]=;
if (match[edge[i].to]==||dfs(match[edge[i].to])) {
match[edge[i].to]=u;
match[u]=edge[i].to;
return ;
}
}
return ;
}
int main()
{
T=read();
while (T--) {
bool p=;
sum=;
num_edge=;
n=read();
memset(match,,sizeof(match));
memset(head,,sizeof(head));
for (int i=; i<=n; i++)
for (int j=; j<=n; j++) {
int x=read();
if (x==) {
add_edge(i,j+n);
add_edge(j+n,i);
}
}
for (int i=; i<=n; i++) {
memset(book,,sizeof(book));
book[i]=;
if (!dfs(i)) {
p=;
break;
}
}
if (p) puts("Yes"); else puts("No");
}
return ;
}

矩阵游戏

注意:每一组数据开始时都要清空边表的num_edge和head数组。

注意:检查代码时就算是定义部分也要检查,别自以为是。

有问题可以直接在评论里面提问,有需要转载的请得到我的允许,否则按侵权处理。


Elena loves NiroBC forever!