二分图的最大匹配

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));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值