BZOJ1059 [ZJOI2007]矩阵游戏 二分图匹配 匈牙利算法

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ1059


题意概括

  有一个n*n(n<=200)的01矩阵,问你是否可以通过交换整行和整列使得左上角到右下角的对角线上的数字都是1。


题解

  我们发现,题目模型可以转换。

  其实题目就是叫我们求是否存在一些1,这些1所在的行和列互不相同。

  我给一个小小的证明:

  假设我们选出了一个n个点的坐标。

  如果这n个点所在的行、列互不相同,那么,我们一定可以通过交换来完成任务。

  比如:依次把每一行的1通过列交换交换到相应位置。

  如果这n个点所在的行有重复,那么,无论如何,位于同一行的两个点是不可能弄到两个不同的行上的。所以一定不行。

  列有重复也同理。

  那么就成了一个行和列的匹配问题。

  转化成二分图匹配,匈牙利算法跑一跑就过去了。


代码

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <vector>
using namespace std;
const int N=200+5;
vector <int> e[N];
int T,n,match[N*2];
bool vis[N*2];
bool dfs(int x){
    for (int i=0,y;i<e[x].size();i++)
        if (!vis[y=e[x][i]]){
            vis[y]=1;
            if (match[y]==-1||dfs(match[y])){
                match[y]=x;
                return 1;
            }
        }
    return 0;
}
int main(){
    scanf("%d",&T);
    while (T--){
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
            e[i].clear();
        for (int i=1;i<=n;i++)
            for (int j=1,x;j<=n;j++){
                scanf("%d",&x);
                if (x)
                    e[i].push_back(j+n);
            }
        memset(match,-1,sizeof match);
        int cnt=0;
        for (int i=1;i<=n;i++){
            memset(vis,0,sizeof vis);
            if (dfs(i))
                cnt++;
        }
        puts(cnt==n?"Yes":"No");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ1059.html