矩阵游戏[ZJOI2007]

                            题目传送门     

题目描述

小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏――矩阵游戏。矩阵游戏在一个N*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:

行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)

列交换操作:选择矩阵的任意两列,交换这两列(即交换对应格子的颜色)

游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。

对于某些关卡,小Q百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!!于是小Q决定写一个程序来判断这些关卡是否有解。

输入输出格式

输入格式:

 

第一行包含一个整数T,表示数据的组数。

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 
2
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0
输出样例#1: 
No
Yes

说明

对于20%的数据,N ≤ 7

对于50%的数据,N ≤ 50

对于100%的数据,N ≤ 200

       

 题解:

       一道不太好建模的二分图...

  首先我们可以发现直接把格子当做点是不可做的。所以我们考虑把每一列当做一个点,把每一行当做一个点。考虑末状态,可以看做第i行向第i列连了一条匹配边。行交换操作可以看做匹配边的交换。

  比如(1,4)、(2,3)是黑格子的话可以看做1->4,2->3连了匹配边(注意,箭头左右分属不同集合,即使数值相同,代表的含义也不同,在代码中的编号也是不同的)。那么交换第一第二行后黑格子就变成了(1,3)、(2,4),匹配边为1->3,2->4。可以发现交换x,y两行就是交换x,y的匹配边。交换列同理。

  所以我们可以得出的一个结论就是如果想要达到最终状态,那么一开始最大匹配数就要等于n.

  可以用网络流求最大匹配数。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 #define LL long long
 7 #define RI register int
 8 using namespace std;
 9 const int INF = 0x7ffffff ;
10 const int N = 200 + 10 ;
11 const int M = 1e7 + 10 ;
12 
13 inline int read() {
14     int k = 0 , f = 1 ; char c = getchar() ;
15     for( ; !isdigit(c) ; c = getchar())
16       if(c == '-') f = -1 ;
17     for( ; isdigit(c) ; c = getchar())
18       k = k*10 + c-'0' ;
19     return k*f ;
20 }
21 struct Edge {
22     int to, next, flow ;
23 }e[M] ;
24 int n, s, t, ansf, cnt = 1 ; int head[N<<1], dep[N<<1] ;
25 inline void add_edge(int x,int y,int ff) {
26     e[++cnt].to = y, e[cnt].next = head[x], head[x] = cnt, e[cnt].flow = ff ;
27     e[++cnt].to = x, e[cnt].next = head[y], head[y] = cnt, e[cnt].flow = 0 ;
28 }
29 
30 inline bool F_bfs() {
31     memset(dep,0,sizeof(dep)) ;
32     queue<int>q ; q.push(s) ; dep[s] = 1 ;
33     while(!q.empty()) {
34         int x = q.front() ; q.pop() ;
35         for(int i=head[x];i;i=e[i].next) {
36             int y = e[i].to ; if(dep[y] || !e[i].flow) continue ;
37             dep[y] = dep[x]+1 ; q.push(y) ;
38         }
39     }
40     return dep[t] ;
41 }
42 int F_dfs(int x,int minflow) {
43     if(x == t || !minflow) return minflow ;
44     int fflow = 0 ;
45     for(int i=head[x];i;i=e[i].next) {
46         int y = e[i].to ; if(dep[y] != dep[x]+1 || !e[i].flow) continue ;
47         int temp = F_dfs(y,min(minflow,e[i].flow)) ;
48         fflow += temp, minflow -= temp ;
49         e[i].flow -= temp, e[i^1].flow += temp ;
50         if(!minflow) break ;
51     }
52     return fflow ;
53 }
54 
55 
56 inline void solve() {
57     n = read() ; memset(head,0,sizeof(head)) ; ansf = 0 ; s = (n<<1)+1, t = s+1 ;
58     for(int i=1;i<=n;i++) {
59         add_edge(s,i,1) ; add_edge(i+n,t,1) ;
60         for(int j=1;j<=n;j++) {
61             bool x = read() ;
62             if(x) {
63                 add_edge(i,j+n,1) ;
64             }
65         }
66     }
67     while(F_bfs()) ansf += F_dfs(s,INF) ;
68     if(ansf == n) printf("Yes\n") ; else printf("No\n") ;
69 }
70 
71 int main() {
72     int t = read() ;
73     while(t--) solve() ;
74     return 0 ;
75 }

 

转载于:https://www.cnblogs.com/zub23333/p/8796578.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值