Luogu 4251 [SCOI2015]小凸玩矩阵

BZOJ 4443

二分答案 + 二分图匹配

外层二分一个最小值,然后检验是否能选出$n - k + 1$个不小于当前二分出的$mid$的数。对于每一个$a_{i, j} \geq mid$,从$i$向$j + n$连一条边,然后跑二分图最大匹配即可。

菜的很,二分图匹配都写不对……

注意数组要开到双倍$n$。

时间复杂度$O(nmlogn)$。

Code:

#include <cstdio>
#include <cstring>
using namespace std;

const int N = 505;
const int M = 2e5 + 5;
const int inf = 1 << 30;

int n, m, k, a[N][N], tot, head[N], mat[N];
bool vis[N];

struct Edge {
    int to, nxt;
} e[M];

inline void add(int from, int to) {
    e[++tot].to = to;
    e[tot].nxt = head[from];
    head[from] = tot;
}

inline void read(int &X) {
    X = 0; char ch = 0; int op = 1;
    for(; ch > '9'|| ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

bool dfs(int x) {
    for(int i = head[x]; i; i = e[i].nxt) {
        int y = e[i].to;
        if(vis[y]) continue;
        vis[y] = 1;
        if(!mat[y] || dfs(mat[y])) {
            mat[y] = x;
            return 1;
        }
    }
    return 0;
}

inline bool chk(int mid) {
    memset(head, 0, sizeof(head)); tot = 0;
    memset(mat, 0, sizeof(mat));
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            if(a[i][j] <= mid) add(i, j + n);

    int res = 0;
    for(int i = 1; i <= n; i++) {
        memset(vis, 0, sizeof(vis));
        if(dfs(i)) ++res;
    }

    return res >= n - k + 1;
}

int main() {
    read(n), read(m), read(k);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            read(a[i][j]);

    int ln = 0, rn = inf, mid, res;
    for(; ln <= rn; ) {
        mid = ((ln + rn) >> 1);
        if(chk(mid)) rn = mid - 1, res = mid;
        else ln = mid + 1;
    }

    printf("%d\n", res);
    return 0;
}
View Code

 

posted @ 2018-12-02 08:54  CzxingcHen  阅读(133)  评论(0编辑  收藏  举报