二分图的最大匹配

poj 3401 

题意:给定一个n*n的矩阵,其中分布着m个东西吧,每一次行操作或者列操作都可以消除整行或整列,求最小的操作次数

分析:以行和列建立左右顶集, 因此为一个二分图,对于i, j位置有一个东西这建立一条这样的边, 表示行i或列j至少要进行一次操作,也就是选出最少的边,使得每一个顶点至少被一条边覆盖, 这个就是最小点覆盖,二分图中这个等于最大匹配


#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        

const int N = 500 + 5;

int n, m;
int match[N], vis[N];
std::vector 
        
        
          edges[N]; bool dfs(int u) { for (int i = 0; i < (int)edges[u].size(); i ++) { int v = edges[u][i]; if (!vis[v]) { vis[v] = true; if (match[v] == -1 || dfs(match[v])) { match[v] = u; return true; } } } return false; } int hungary() { int ret = 0; std::fill(match, match + n, -1); for (int i = 0; i < n; i ++) { std::fill(vis, vis + n, false); if (dfs(i)) { ret ++; } } return ret; } int main() { while (scanf("%d%d", &n, &m) == 2) { std::fill(edges, edges + n, std::vector 
         
           ()); for (int i = 0; i < m; i ++) { int u, v; scanf("%d%d", &u, &v); u --, v --; edges[u].push_back(v); } printf("%d\n", hungary()); } return 0; } 
          
        
       
       
      
      
     
     
    
    

Regionals 2014 :: Asia - Taichung I题

题意:平面上有N个点,一次行操作可以消除角度相同的所有点,一次圆操作可以消除圆上的点,问最小的操作次数

分析:做法同上, 建图时可以用不同的角度和不同的距离建图, 这样就是二分图


#include 
    
    
     
     

const int N = 20000 + 5;

int n;
int vis[N], match[N];
int d[N], x[N], y[N];
std::vector 
     
     
      
       circle;
std::vector 
      
      
       
        line;
std::vector 
       
       
        
         edges[N];

bool dfs(int u) {
    for (int i = 0; i < (int)edges[u].size(); i ++) {
        int v = edges[u][i];
        if (!vis[v]) {
            vis[v] = true;
            if (match[v] == -1 || dfs(match[v])) {
                match[v] = u;
                return true;
            }
        }
    }
    return false;
} 

int work() {
    std::sort(line.begin(), line.end());
    std::sort(circle.begin(), circle.end());
    line.erase(std::unique(line.begin(), line.end()), line.end());
    circle.erase(std::unique(circle.begin(), circle.end()), circle.end());
    int row = (int)line.size();
    int col = (int)circle.size();
    std::fill(edges, edges + row, std::vector 
        
        
          ()); for (int i = 0; i < n; i ++) { int a = std::lower_bound(line.begin(), line.end(), 1.0 * y[i] / x[i]) - line.begin(); int b = std::lower_bound(circle.begin(), circle.end(), d[i]) - circle.begin(); edges[a].push_back(b); } int ret = 0; memset(match, -1, sizeof(match[00]) * col); for (int i = 0; i < row; i ++) { memset(vis, false, sizeof(vis[0]) * col); if (dfs(i)) { ret ++; } } return ret; } int main() { int t; scanf("%d", &t); while (t --) { scanf("%d", &n); circle.clear(); line.clear(); for (int i = 0; i < n; i ++) { scanf("%d%d%d", &d[i], &x[i], &y[i]); circle.push_back(d[i]); line.push_back(1.0 * y[i] / x[i]); } printf("%d\n", work()); } return 0; } 
        
       
       
      
      
     
     
    
    


// 匈牙利算法的复杂度:O(E * sqrt(V));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值