二分图再次总结

二分图首次总结

在caioj上刷了二分图的题,比较有收获

而且关键是网上找不到题解。

我开始几道做不出,然后题解搜不到

其实这反而更有效果

很好的限制了我喜欢抄题解的习惯

只有自己去做,思维能力才能得到锻炼

一定要克制自己抄题解!!!!!!!!

最大匹配1(二分图)(元问题byscy):公牛母牛配

裸题。

注意边加的时候是有向边

然后注意边数可能很多,不要以为是树,惯性思维

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 1e4 + 10;
const int MAXM = 1e5 + 10;
struct Edge{ int to, next; } e[MAXM << 1];
int head[MAXN], tot;
int vis[MAXN], link[MAXN], n, m, k;

void AddEdge(int from, int to)
{
    e[tot] = Edge{to, head[from]};
    head[from] = tot++;
}

bool dfs(int u)
{
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(vis[v]) continue;
        vis[v] = 1;
        if(!link[v] || dfs(link[v]))
        {
            link[v] = u;
            return true;
        }
    }
    return false;
}

int main()
{
    memset(head, -1, sizeof(head)); tot = 0;
    scanf("%d%d%d", &n, &m, &k);
    _for(i, 1, k)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        AddEdge(u, v);
    }
    
    int ans = 0;
    _for(i, 1, n)
    {
        memset(vis, 0, sizeof(vis));
        if(dfs(i)) ans++;
    }
    printf("%d\n", ans);
    
    return 0;
}

 

最大二分匹配2:上课(转化模型)

可以抽象出一个模型

一个A点可以拓展出很多B点,只要选择一个B点就可以覆盖A点

一个B点只能覆盖一个A点

求最多覆盖多少A点

用二分图最大匹配就好了

#include<bits/stdc++.h>
#define ID(x, y) (x - 1) * 12 + y
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 300 + 10;
const int MAXM = 100;
int n, a[MAXN][MAXN];
int link[MAXN], vis[MAXN];

bool find(int u)
{
    _for(v, 1, 84)
    {
        if(!a[u][v] || vis[v]) continue;
        vis[v] = 1;
        if(!link[v] || find(link[v]))
        {
            link[v] = u;
            return true;
        }
    }
    return false;
}

int main()
{
    scanf("%d", &n);
    _for(i, 1, n)
    {
        int t, p, q;
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d%d", &p, &q); 
            a[i][ID(p, q)] = 1;
        }
    }
                    
    int ans = 0;
    _for(i, 1, n)
    {
        memset(vis, 0, sizeof(vis));
        if(find(i)) ans++;
    }
    printf("%d\n", ans);
    
    return 0;
}

 

最大二分匹配3:地鼠(转化模型)

套用上一题的模型。能达到就连边。

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 100 + 10;
int n, m, s, v;
int a[MAXN][MAXN], vis[MAXN], link[MAXN];
double x[MAXN], y[MAXN];

int find(int u)
{
    _for(v, 1, m)
    {
        if(!a[u][v] || vis[v]) continue;
        vis[v] = 1;
        if(!link[v] || find(link[v]))
        {
            link[v] = u;
            return true;
        }
    }
    return false;
}

bool judge(int i, double xx, double yy)
{
    return sqrt(pow(x[i] - xx, 2) + pow(y[i] - yy, 2)) <= (double)s * v;
}

int main()
{
    while(~scanf("%d%d%d%d", &n, &m, &s, &v))
    {
        memset(a, 0, sizeof(a));
        memset(link, 0, sizeof(link));
        
        _for(i, 1, n) scanf("%lf%lf", &x[i], &y[i]);
        _for(i, 1, m)
        {
            double xx, yy;
            scanf("%lf%lf", &xx, &yy);
            _for(j, 1, n) a[j][i] = judge(j, xx, yy);
        }
        
        int ans = 0;
        _for(i, 1, n)
        {
            memset(vis, 0, sizeof(vis));
            if(find(i)) ans++;
        }
        printf("%d\n", n - ans);
    }

    return 0;
}

 

最小覆盖1(二分图)(元问题byscy)

最小覆盖=最大匹配

代码和最大匹配的一模一样,不贴了

 

最小覆盖2(模型转换:地雷)

很经典的一道题

其实要理解透彻,抽象出一个模型

我的理解是,对于一个地雷,可以被这一行和这一列的炮击毁。

所以把行看作X集合,列看作Y集合,地雷看作边

这样建图后,一个点,也就是一行或者一列,就可以消灭这一行或这一列的地雷,

也就是说一个点可以消灭与其相连的边

那么显然最小覆盖了

要理解本质

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 500 + 10;
const int MAXM = 1e4 + 10;
struct Edge{ int to, next; } e[MAXM];
int head[MAXN], tot;
int vis[MAXN], link[MAXN], n, m, k;

void AddEdge(int from, int to)
{
    e[tot] = Edge{to, head[from]};
    head[from] = tot++;
}

bool dfs(int u)
{
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(vis[v]) continue;
        vis[v] = 1;
        if(!link[v] || dfs(link[v]))
        {
            link[v] = u;
            return true;
        }
    }
    return false;
}

int main()
{
    memset(head, -1, sizeof(head)); tot = 0;
    scanf("%d%d", &n, &m);
    _for(i, 1, m)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        AddEdge(u, v);
    }
    
    int ans = 0;
    _for(i, 1, n)
    {
        memset(vis, 0, sizeof(vis));
        if(dfs(i)) ans++;
    }
    printf("%d\n", ans);
    
    return 0;
}

 

最小覆盖2(模型转换:草场淹水)

这题想了巨久。1.5h吧

其实这题可以用来检验你有没有理解上一题的本质

在这道题

每个点可以被横着的木板和竖着的木板消灭

那么类比上一题,就把横着的木板和竖着的木板作为X集合和Y集合

这个点就作为边,就ok了

注意要预处理出每一个木板的编号

不过我一开始觉得要超时。因为匈牙利算法是n三方的

然而这个编号可以到2500(大概这个数量级,实际上1000多左右,因为点和点可以合并)

但是交上去是0ms

可能是数据水,或者匈牙利算法实际上远远达不到n三方。

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 50;
const int MAXM = 2500;
struct Edge{ int to, next; } e[MAXM * MAXM];
int head[MAXM], tot;

int vis[MAXM], link[MAXM], n, m, id;
int a1[MAXN][MAXN], a2[MAXN][MAXN];
char s[MAXN][MAXN];

void AddEdge(int from, int to)
{
    e[tot] = Edge{to, head[from]};
    head[from] = tot++;
}

bool dfs(int u)
{
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(vis[v]) continue;
        vis[v] = 1;
        if(!link[v] || dfs(link[v]))
        {
            link[v] = u;
            return true;
        }
    }
    return false;
}

int main()
{
    memset(head, -1, sizeof(head)); tot = 0;
    scanf("%d%d", &n, &m);
    _for(i, 1, n) scanf("%s", s[i] + 1);
    
    id = 0;
    _for(j, 1, m)
        _for(i, 1, n)
            if(s[i][j] == '*')
            {
                int t = i; id++;
                while(s[t][j] == '*') a2[t++][j] = id;
                i = t - 1;
            }

    id = 0;
    _for(i, 1, n)
        _for(j, 1, m)
            if(s[i][j] == '*')
            {
                int t = j; id++;
                while(s[i][t] == '*') a1[i][t++] = id;
                j = t - 1;
            }
    
    _for(i, 1, n)
        _for(j, 1, m)
            if(s[i][j] == '*')
                AddEdge(a1[i][j], a2[i][j]);
    
    
    int ans = 0;
    _for(i, 1, id)
    {
        memset(vis, 0, sizeof(vis));
        if(dfs(i)) ans++;
    }
    printf("%d\n", ans);
    
    return 0;
}

 

最后剩下一道题,一般图的最大独立子集。

不会……

posted @ 2018-11-08 17:21  Sugewud  阅读(227)  评论(0编辑  收藏  举报