• 二分图再次总结


    二分图首次总结

    在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
    ", 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
    ", 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 - 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
    ", 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
    ", ans);
        
        return 0;
    }

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

    不会……

  • 相关阅读:
    electrica writeup
    C++ Tips and Tricks
    net-force.nl/steganography writeup
    3*n/2
    C++ 关键字浅谈
    MindMaster激活教程
    gitignre
    python pil 安装
    ubuntu 阿里云安全配置
    阿里云 centos 环境配置与 django 部署
  • 原文地址:https://www.cnblogs.com/sugewud/p/9930370.html
Copyright © 2020-2023  润新知