zoukankan      html  css  js  c++  java
  • [BZOJ1059] [ZJOI2007] 矩阵游戏 (二分图匹配)

    Description

      小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏。矩阵游戏在一个N
    *N黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:行交换操作:选择
    矩阵的任意两行,交换这两行(即交换对应格子的颜色)列交换操作:选择矩阵的任意行列,交换这两列(即交换
    对应格子的颜色)游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑
    色。对于某些关卡,小Q百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!!于是小Q决定写一个程
    序来判断这些关卡是否有解。

    Input

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

    Output

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

    Sample Input

    2
    2
    0 0
    0 1
    3
    0 0 1
    0 1 0
    1 0 0

    Sample Output

    No
    Yes

      【数据规模】
      对于100%的数据,N ≤ 200

    HINT

    Source

    Solution

      题意可以理解成“是否可以找出$n$个点使得两两不同行且两两不同列”

      把所有行看成左边$n$个点,所有列看成右边$n$个点,如果$(i, j)$是黑边就把左边第$i$个点与右边第$j$个点连边,匈牙利算法跑一边即可

      基于邻接表实现的匈牙利是$O(nm)$的

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 struct edge
     4 {
     5     int v, nxt;
     6 }e[40005];
     7 int n, fst[205], belong[205], etot;
     8 bool vis[205];
     9 
    10 void addedge(int u, int v)
    11 {
    12     e[++etot] = (edge){v, fst[u]}, fst[u] = etot;
    13 }
    14 
    15 bool Hungary(int i)
    16 {
    17     for(int j = fst[i]; j; j = e[j].nxt)
    18         if(!vis[e[j].v])
    19         {
    20             int v = e[j].v;
    21             vis[v] = true;
    22             if(!belong[v] || Hungary(belong[v]))
    23                 return belong[v] = i;
    24         }
    25     return false;
    26 }
    27 
    28 int main()
    29 {
    30     int t, ans, x;
    31     scanf("%d", &t);
    32     while(t--)
    33     {
    34         memset(fst, 0, sizeof(fst));
    35         memset(belong, 0, sizeof(belong));
    36         ans = etot = 0;
    37         scanf("%d", &n);
    38         for(int i = 1; i <= n; ++i)
    39             for(int j = 1; j <= n; ++j)
    40             {
    41                 scanf("%d", &x);
    42                 if(x) addedge(i, j);
    43             }
    44         for(int i = 1; i <= n; ++i)
    45         {
    46             memset(vis, 0, sizeof(vis));
    47             ans += Hungary(i);
    48         }
    49         puts(ans == n ? "Yes" : "No");
    50     }
    51     return 0;
    52 }
    View Code

      当然也可以用$Dinic$跑,据说$Dinic$跑二分图是$O(sqrt{n}m)$的,然而亲测是匈牙利的$4$倍时间......是我常数太大...么?

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 struct edge
     4 {
     5     int v, w, nxt;
     6 }e[81005];
     7 int n, fst[405], level[405], etot, sss, ttt, q[405];
     8 
     9 void addedge(int u, int v)
    10 {
    11     e[++etot] = (edge){v, 1, fst[u]}, fst[u] = etot;
    12     e[++etot] = (edge){u, 0, fst[v]}, fst[v] = etot;
    13 }
    14 
    15 bool BFS()
    16 {
    17     int front = 0, back, u;
    18     memset(level, 0, sizeof(level));
    19     level[sss] = 1, q[back = 1] = sss;
    20     while(front != back)
    21     {
    22         u = q[++front];
    23         for(int i = fst[u]; i; i = e[i].nxt)
    24             if(e[i].w && !level[e[i].v])
    25             {
    26                 level[e[i].v] = level[u] + 1;
    27                 q[++back] = e[i].v;
    28             }
    29     }
    30     return level[ttt];
    31 }
    32 
    33 int Dinic(int u, int lim)
    34 {
    35     int tmp = lim;
    36     if(u == ttt) return lim;
    37     for(int i = fst[u]; i; i = e[i].nxt)
    38         if(e[i].w && level[e[i].v] == level[u] + 1)
    39         {
    40             int flow = Dinic(e[i].v, min(e[i].w, tmp));
    41             e[i].w -= flow, e[i ^ 1].w += flow;
    42             if(!(tmp -= flow)) break;
    43         }
    44     if(tmp == lim) level[u] = 0;
    45     return lim - tmp;
    46 }
    47 
    48 int main()
    49 {
    50     int t, ans, x;
    51     scanf("%d", &t);
    52     while(t--)
    53     {
    54         memset(fst, 0, sizeof(fst));
    55         etot = 1, ans = 0;
    56         scanf("%d", &n);
    57         sss = n * 2 + 1, ttt = n * 2 + 2;
    58         for(int i = 1; i <= n; ++i)
    59             addedge(sss, i);
    60         for(int i = n + 1; i <= n * 2; ++i)
    61             addedge(i, ttt);
    62         for(int i = 1; i <= n; ++i)
    63             for(int j = 1; j <= n; ++j)
    64             {
    65                 scanf("%d", &x);
    66                 if(x) addedge(i, j + n);
    67             }
    68         while(BFS())
    69             ans += Dinic(sss, 1);
    70         puts(ans == n ? "Yes" : "No");
    71     }
    72     return 0;
    73 }
    View Code
  • 相关阅读:
    JS leetcode 买卖股票的最佳时机 题解分析,我离职了。
    JS leetcode x 的平方根 题解分析
    JS leetcode 有多少小于当前数字的数字 解题分析,你应该了解的桶排序
    JS leetcode 合并两个有序数组 解题分析
    JavaSE部分 (多线程下)
    JavaSE部分 (多线程上)
    JavaSE部分 (异常)
    JavaSE部分 集合下(Map)
    JavaSE部分 集合中(数据结构 list set Collections)
    JavaSE (接口 final 内部类)
  • 原文地址:https://www.cnblogs.com/CtrlCV/p/5615782.html
Copyright © 2011-2022 走看看