bzoj4443

二分+二分图匹配

晚上脑子不太好使。。。

行列模型,填充数量性质,种种迹象告诉我们这是二分图,但是我觉得好像不太科学就弃了网络流。。。

二分第k大值,转化为求第n-k+1小值,二分求匹配判定即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 810, inf = 1000000010;
namespace IO
{
    const int Maxlen = N;
    char buf[Maxlen], *C = buf;
    int Len;
    inline void read_in()
    {
        Len = fread(C, 1, Maxlen, stdin);
        buf[Len] = ‘\0‘;
    }
    inline void fread(int &x)
    {
        x = 0;
        int f = 1;
        while (*C < ‘0‘ || ‘9‘ < *C) { if(*C == ‘-‘) f = -1; ++C; }
        while (‘0‘ <= *C && *C <= ‘9‘) x = (x << 1) + (x << 3) + *C - ‘0‘, ++C;
        x *= f;
    }
    inline void read(int &x)
    {
        x = 0;
        int f = 1; char c = getchar();
        while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) f = -1; c = getchar(); }
        while(c >= ‘0‘ && c <= ‘9‘) { x = (x << 1) + (x << 3) + c - ‘0‘; c = getchar(); }
        x *= f;
    }
    inline void read(long long &x)
    {
        x = 0;
        long long f = 1; char c = getchar();
        while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) f = -1; c = getchar(); }
        while(c >= ‘0‘ && c <= ‘9‘) { x = (x << 1ll) + (x << 3ll) + c - ‘0‘; c = getchar(); }
        x *= f;
    }
} using namespace IO;
struct edge {
    int nxt, to, f;
} e[N * N * 10];
int n, m, cnt = 1, source, sink, k;
int head[N], d[N], a[N][N], iter[N];
inline void link(int u, int v, int f)
{
    e[++cnt].nxt = head[u];
    head[u] = cnt;
    e[cnt].to = v;
    e[cnt].f = f;
}
inline void insert(int u, int v, int f)
{
    link(u, v, f);
    link(v, u, 0);
}
inline bool bfs()
{
    queue<int> q;
    memset(d, -1, sizeof(d));
    d[source] = 0;
    q.push(source);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        for(int i = head[u]; i; i = e[i].nxt) if(e[i].f && d[e[i].to] == -1)
        {
            d[e[i].to] = d[u] + 1;
            q.push(e[i].to);
        }
    }
    return d[sink] != -1;
}
inline int dfs(int u, int delta)
{
    if(u == sink) return delta;
    int ret = 0;
    for(int &i = iter[u]; i && delta; i = e[i].nxt) if(e[i].f && d[e[i].to] == d[u] + 1)
    {
        int flow = dfs(e[i].to, min(e[i].f, delta));
        e[i].f -= flow;
        e[i ^ 1].f += flow;
        delta -= flow;
        ret += flow;
    }
    return ret;
}
inline int dinic()
{
    int ret = 0;
    while(bfs())
    {
        for(int i = source; i <= sink; ++i) iter[i] = head[i];
        ret += dfs(source, inf);
    }
    return ret;
}
inline bool check(int mid)
{
    memset(head, 0, sizeof(head));
    cnt = 1;
    for(int i = 1; i <= n; ++i)
    {
        insert(source, i, 1);
        for(int j = 1; j <= m; ++j) if(a[i][j] <= mid)
            insert(i, j + n, 1);
    }
    for(int i = 1; i <= m; ++i) insert(i + n, i + m + n, 1), insert(i + m + n, sink, 1);
    int ret = dinic();
    return ret >= n - k + 1;
}
int main()
{
    read(n);
    read(m);
    read(k);
    sink = 2 * m + n + 1;
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j) read(a[i][j]);
    int l = 0, r = inf + 10, ans = 0;
    while(r - l > 1)
    {
        int mid = (l + r) >> 1;
        if(check(mid)) r = ans = mid;
        else l = mid;
    }
    printf("%d\n", ans);
    return 0;
}

时间: 2024-12-28 18:55:55

bzoj4443的相关文章

【BZOJ4443】[Scoi2015]小凸玩矩阵 二分+二分图最大匹配

[BZOJ4443][Scoi2015]小凸玩矩阵 Description 小凸和小方是好朋友,小方给小凸一个N*M(N<=M)的矩阵A,要求小秃从其中选出N个数,其中任意两个数字不能在同一行或同一列,现小凸想知道选出来的N个数中第K大的数字的最小值是多少. Input 第一行给出三个整数N,M,K 接下来N行,每行M个数字,用来描述这个矩阵 Output 如题 Sample Input 3 4 2 1 5 6 6 8 3 4 3 6 8 6 3 Sample Output 3 HINT 1<

【bzoj4443】[Scoi2015]小凸玩矩阵

第K大也就是第n-K+1小,所以就可以的二分答案了 (江哥讲过一道类似题) 二分答案找出比当前答案小的数的位置的坐标,判断一下是否可以选出满足不在同一行同一列的n-K+1个数,然后就可以跑匈牙利了,对于一个坐标(x,y)如果满足a[x][y]≤a[x][y]当前答案,就把第x行向第y列连边,然后跑匈牙利判断最大匹配是否大于n-K+1 #include<algorithm> #include<iostream> #include<cstdlib> #include<

bzoj4443: [Scoi2015]小凸玩矩阵

太久没搞网络流,又被坑了一发死循环..(这次是对cur[]初始化没对.以后直接for S到T不就好了嘛!) 先看数据量.诶,才250,肯定n三方.搜索不行,dp不行,贪心不行,二分图网络流?恩,有可能,先放一边去. 然而正解就是二分+二分图匹配. 二分答案,二分图匹配看是否存在大等n-k+1个匹配. 基本上每当题目中有限制不能同行不能同列的时候都是二分图左边为行,右边为列来匹配的.(我怎么就是不长记性呢,又被坑了 1 #include<bits/stdc++.h> 2 using namesp

【bzoj4443】[Scoi2015]小凸玩矩阵 二分+二分图匹配

题目描述 小凸和小方是好朋友,小方给小凸一个N*M(N<=M)的矩阵A,要求小秃从其中选出N个数,其中任意两个数字不能在同一行或同一列,现小凸想知道选出来的N个数中第K大的数字的最小值是多少. 输入 第一行给出三个整数N,M,K 接下来N行,每行M个数字,用来描述这个矩阵 输出 如题 样例输入 3 4 2 1 5 6 6 8 3 4 3 6 8 6 3 样例输出 3 题解 二分+二分图最大匹配 最(第k)大值最小,很容易想到二分答案. 二分一个mid,若满足条件,一定满足:可以选出n-k+1个不

bzoj4443 小凸玩矩阵

二分+最大check 1 #include<algorithm> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cstdio> 6 #include<string> 7 #include<cmath> 8 #include<ctime> 9 #include<queue> 10 #include<

BZOJ4443:[SCO2015]小凸玩矩阵

题目大意:给一个N*M的矩阵,选出N个数,使得每行没列只选一个数,求第K大的数的最小值是多少? 二分答案,第k大的数<=x,则有N-k+1个数<=k,用二分图判定. #include<bits/stdc++.h> using namespace std; int n,m,k; int a[255][255]; void init(){ scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n;++i) f

待 题表

题表 达哥终极杂题表Bzoj2839 hdu6021 Codeforces 804DBzoj2248 hdu5575 Codeforces 786CBzoj2013 bzoj2676 Codeforces 803CBzoj2386 bzoj3782 Codeforces 813DBzoj2699 cogs1667 Codeforces 814DBzoj4798 bzoj2064 Codeforces 814EBzoj4639 bzoj3505 Codeforces 815ABzoj4417 bz