矩阵游戏|ZJOI2007|BZOJ1059|codevs1433|luoguP1129|二分图匹配|匈牙利算法|Elena
1059: [ZJOI2007]矩阵游戏
Time Limit: 10 Sec Memory Limit: 162 MBDescription
Input
Output
输出文件应包含T行。对于每一组数据,如果该关卡有解,输出一行Yes;否则输出一行No。
Sample Input
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0
Sample Output
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个格子的横纵坐标是匹配的,这些格子的横纵坐标不会相同,就会组成一条线,我们可以把交换行列看成改变格子的位置,所以如果组成的是左下角到右上角的线,可以通过交换列来达到理想位置。那条线符合答案要求。因为我们可以通过换行换列来使格子到达该到的地方。可以通过交换行列来改变格子的位置,但是同行列的格子间会受到影响,所以只有不同行列的格子改变到理想的位置后不会影响到其他的格子。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 int read() 8 { 9 int x=0,f=1; char c=getchar(); 10 while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} 11 while (c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();} 12 return x*f; 13 } 14 int num_edge,head[500],T,sum,n,match[500]; 15 bool book[500]; 16 struct Edge 17 { 18 int next; 19 int to; 20 }edge[81000]; 21 void add_edge(int from,int to) 22 { 23 edge[++num_edge].next=head[from]; 24 edge[num_edge].to=to; 25 head[from]=num_edge; 26 } 27 bool dfs(int u) 28 { 29 for (int i=head[u]; i; i=edge[i].next) 30 if (book[edge[i].to]==0) { 31 book[edge[i].to]=1; 32 if (match[edge[i].to]==0||dfs(match[edge[i].to])) { 33 match[edge[i].to]=u; 34 match[u]=edge[i].to; 35 return 1; 36 } 37 } 38 return 0; 39 } 40 int main() 41 { 42 T=read(); 43 while (T--) { 44 bool p=1; 45 sum=0; 46 num_edge=0; 47 n=read(); 48 memset(match,0,sizeof(match)); 49 memset(head,0,sizeof(head)); 50 for (int i=1; i<=n; i++) 51 for (int j=1; j<=n; j++) { 52 int x=read(); 53 if (x==1) { 54 add_edge(i,j+n); 55 add_edge(j+n,i); 56 } 57 } 58 for (int i=1; i<=n; i++) { 59 memset(book,0,sizeof(book)); 60 book[i]=1; 61 if (!dfs(i)) { 62 p=0; 63 break; 64 } 65 } 66 if (p) puts("Yes"); else puts("No"); 67 } 68 return 0; 69 }
注意:每一组数据开始时都要清空边表的num_edge和head数组。
注意:检查代码时就算是定义部分也要检查,别自以为是。
有问题可以直接在评论里面提问,有需要转载的请得到我的允许,否则按侵权处理。
Elena loves NiroBC forever!