题目传送门:【BZOJ 1059】
题目大意:小 Q 很喜欢玩矩阵游戏。矩阵游戏在一个 N * N 黑白方阵进行。每次可以对该矩阵进行两种操作:行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)列交换操作:选择矩阵的任意行列,交换这两列(即交换对应格子的颜色)游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。对于某些关卡,小 Q 百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!!于是小 Q 决定写一个程序来判断这些关卡是否有解。
输入共 T 组数据。每组数据第一行为一个整数 N,表示一个 N * N 的矩阵;之后输入这个 01 矩阵(0 表示白色,1 表示黑色)。当这组数据有解时,输出 Yes;否则输出 No。
题目分析: (高能!)
(第一次做这道题中……)(结果:WA)
这道题不就是一道贪心水题吗?直接判断每行每列是否全为白色,如果是就直接输出 No,否则输出 Yes 不就行了吗?啊哈哈哈哈哈…… 啊!(被一道从天而降的 WA 闪电击中,倒地不起)
(第二次做这道题中……)(结果:AC)
经历了上次的教训之后,我仔细地看了看这道题,发现:这道题不能这样贪心!每行每列至少有一个黑色只是一项基本条件(必要条件)而已,不能直接判断!例如这组数据:
4
1 0 0 0
1 0 0 0
0 1 1 0
0 1 0 1
我们会发现,无论怎么样调整行列,都无法得到满足条件的答案。
事实上,用贪心来做这道题应该是行不通的,至少我构思了好几种办法,都失败了。
(然后继续 YY 算法中……)
~~(结合着之前做题的经验,我又接着试了试二分图匹配)~~
能不能这样呢?对于行和列构造出一个二分图,然后,如果当第 i 行第 j 列的数为 1,那么就在二分图对应的地方连一条双向边?那此时又怎么统计并判断答案呢?我惊奇地发现,只要是符合题目条件的答案,它的最大匹配数目都等于 N(换言之,它一定是一个完全匹配)。
那么又该怎么证明呢?这个问题,我还暂时没思考出答案。有兴趣的 OIer 可以自己先看看其他的博客吧 :-) 我一定会在之后补上 233333
下面附上代码:
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- using namespace std;
- const int MX = 205;
- struct Edge{
- int to,next;
- };
- Edge edge[MX * MX * 2];
- int n,maps[MX][MX],tots = 0;
- int head[MX * 5],now = 0,ans = 0,match[MX * 5];
- bool vis[MX * 5];
- void adde(int u,int v){
- edge[++now].to = v;
- edge[now].next = head[u];
- head[u] = now;
- }
- bool hungary(int u){
- for (int i = head[u];i;i = edge[i].next){
- int v = edge[i].to;
- if (!vis[v]){
- vis[v] = true;
- if (match[v] == 0 || hungary(match[v])){
- match[v] = u;
- return true;
- }
- }
- }
- return false;
- }
- bool check(){ //第一步贪心的预处理
- bool row = false,column[MX] = {0}; //这一步可以被省略掉,因为求最大匹配时已经包含了它
- for (int i = 1;i <= n;i++){ //实际上,因为它是必要条件
- row = false; //所以有了它可以增加严谨性,并不会影响答案
- for (int j = 1;j <= n;j++){ //这里可以选择把它注释掉
- if (maps[i][j]){
- row = true;
- column[j] = true;
- }
- }
- if (!row){
- return false;
- }
- }
- for (int i = 1;i <= n;i++){
- if (!column[i]) return false;
- }
- return true;
- }
- void _init(){
- memset(maps,0,sizeof(maps));
- memset(head,0,sizeof(head));
- memset(edge,0,sizeof(edge));
- memset(match,0,sizeof(match));
- now = 0,ans = 0,tots = 0;
- }
- int main(){
- int T;
- cin>>T;
- while (T–){
- scanf(”%d”,&n);
- for (int i = 1;i <= n;i++){
- for (int j = 1;j <= n;j++){
- scanf(”%d”,&maps[i][j]);
- if (maps[i][j] == 1){
- ++tots;
- adde(i,j + 210); //建立二分图
- adde(j + 210,i);
- }
- }
- }
- for (int i = 1;i <= n;i++){
- memset(vis,0,sizeof(vis));
- if (hungary(i)) ++ans;
- }
- if (tots<n || !check() || ans<n){//1.总共 1 的数量小于 n;2.至少有一行或一列为白色
- printf(”No\n”); //3.这个图不是完全匹配
- } else { //此时返回 No
- printf(”Yes\n”); //上述条件都不满足,则返回 Yes
- }
- _init();
- }
- return 0;
- }