[ZJOI2007]矩阵游戏——非常漂亮的二分图转化
Posted miracevin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2007]矩阵游戏——非常漂亮的二分图转化相关的知识,希望对你有一定的参考价值。
题意:
小 Q 是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏――矩阵游戏。矩阵游戏在一个 N×N 黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:
行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)
列交换操作:选择矩阵的任意两列,交换这两列(即交换对应格子的颜色)
游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。
对于某些关卡,小 Q 百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!于是小 Q 决定写一个程序来判断这些关卡是否有解。
样例(1是黑色格子)
2
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0
输出:
No
Yes
题解:
这个题开始一点儿思路都没有。。。。。
然后偷看了算法标签。。。
二分图???匈牙利算法???
——————醍醐灌顶,恍然大悟,一语惊醒梦中人——————
可以观察到:
如果我们能在棋盘上找到形似这样的1的分布:
0 0 1
0 1 0
1 0 0
就是,每行每列有且只有1个1。这就足够了。
其他的1都是跑腿的。
像这样:
0 0 1
1 1 1
0 0 1
虽然1多,但是没用啊。一样不可解。
发现,只要判断是否存在每行每列只有一个1的“有效子图””情况。
这个子图的特点是,一个行和其他所有列的交点上,有且只有一个交点是1,其他都是0
换句话说,一个行和一个列匹配了,就不能和其他的列匹配了!!!!
典型的二分图匹配性质!!
我们把所有的行看成是左部点,列看成是右部点,
那么,当这个行和这个列的交点是1的时候,行列之间连一条边。
这里匹配这条边的含义是,有效子图中,在这个位置放一个1。由于匹配的性质,这一行,这一列就不会放其他的1了,和有效子图的定义恰好一致。
看一看最大匹配,是不是n(最大也就是n)
就表明能否找到n个点,满足任意两个点不在同一行,不在同一列上,也就是能构成一个有效子图。
输出yes或者no即可。
#include<bits/stdc++.h> using namespace std; const int N=200+10; int t,n; int pre[N]; int mp[N][N]; struct node{ int nxt,to; }e[N*N]; int hd[N],cnt; void add(int x,int y){ e[++cnt].nxt=hd[x]; e[cnt].to=y; hd[x]=cnt; } bool vis[N]; bool dfs(int x){ for(int i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(!vis[y]){ vis[y]=1; if(!pre[y]||dfs(pre[y])){ pre[y]=x; return true; } } } return false; } void clear(){ memset(hd,0,sizeof hd); cnt=0; memset(pre,0,sizeof pre); } int main() { scanf("%d",&t); while(t--){ scanf("%d",&n); clear(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ scanf("%d",&mp[i][j]); if(mp[i][j]==1) add(i,j); } int ans=0; for(int i=1;i<=n;i++){ memset(vis,0,sizeof vis); if(dfs(i)) ans++; } if(ans==n){ printf("Yes "); } else printf("No "); } return 0; }
以上是关于[ZJOI2007]矩阵游戏——非常漂亮的二分图转化的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 1059 [ZJOI2007]矩阵游戏 (二分图最大匹配)
BZOJ-1059: [ZJOI2007]矩阵游戏 (二分图最大匹配)