zoukankan      html  css  js  c++  java
  • 二分图匹配入门题

    板子(匈牙利算法,邻接矩阵)

    const int MAXN=2e3+5;
    int uN, vN;
    int g[MAXN][MAXN];
    int linker[MAXN];
    bool used[MAXN];
    
    bool dfs(int u)
    {
        for(int v=0; v<vN; v++)
            if(g[u][v] && !used[v])
            {
                used[v]=true;
                if(linker[v]==-1 || dfs(linker[v]))
                {
                    linker[v]=u;
                    return true;
                }
            }
        return false;
    }
    
    int hungary()
    {
        int res=0;
        memset(linker, -1, sizeof(linker));
        for(int u=0; u<uN; u++)
        {
            memset(used, 0, sizeof(used));
            if(dfs(u)) res++;
        }
        return res;
    }
    View Code

    HDU 1045 (经典建模)

    题意:给出一张图,给出空地'.'和隔板‘x’, 求放置最多满足条件的blockhouse,条件:垂直和水平方向上没有如果隔板隔开的话,只能放置一个house,有隔板话,隔板的之后(相对位置)的不用考虑。

    题解:分别对每一行和每一列进行缩点(重新标号),两个相交的话就连边(其实就是把2个条件链接(行,列)到了一起)

    #include <bits/stdc++.h>
    using namespace std; 
    #define _for(i,a,b) for(int i=(a); i< (b); i++)
    #define _rep(i,a,b) for(int i=(a); i<=(b); i++)
    
    const int MAXN=2e2+5;
    int uN, vN;
    int g[MAXN][MAXN];
    int linker[MAXN];
    bool used[MAXN];
    
    bool dfs(int u)
    {
        for(int v=0; v<vN; v++)
            if(g[u][v] && !used[v])
            {
                used[v]=true;
                if(linker[v]==-1 || dfs(linker[v]))
                {
                    linker[v]=u;
                    return true;
                }
            }
        return false;
    }
    
    int hungary()
    {
        int res=0;
        memset(linker, -1, sizeof(linker));
        for(int u=0; u<uN; u++)
        {
            memset(used, 0, sizeof(used));
            if(dfs(u)) res++;
        }
        return res;
    }
    
    char Map[MAXN][MAXN];
    int Mrow[MAXN][MAXN], Mcol[MAXN][MAXN];
    
    int main()
    {
        ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
        //freopen("in.txt", "r", stdin);    
        int n;
        while(cin>>n, n)
        {
            _for(i, 0, n) _for(j, 0, n) cin>>Map[i][j];
            uN=vN=0;
            int cu=0, cv=0;
            memset(Mrow, -1, sizeof(Mrow));
            memset(Mcol, -1, sizeof(Mcol));
            _for(i, 0, n) _for(j, 0, n)
            {
                if(Mrow[i][j]==-1 && Map[i][j]=='.')
                {
                    for(int k=j; k<n&&Map[i][k]=='.'; k++)
                        Mrow[i][k]=cu;
                    uN=max(uN, ++cu);
                }
                if(Mcol[i][j]==-1 && Map[i][j]=='.')
                {
                    for(int k=i; k<n&&Map[k][j]=='.'; k++)
                        Mcol[k][j]=cv;
                    vN=max(vN, ++cv);
                }    
            }
            memset(g, 0, sizeof(g));
            _for(i, 0, n) _for(j, 0, n)
                if(Map[i][j]=='.') g[Mrow[i][j]][Mcol[i][j]]=1;
            cout<<hungary()<<endl;
        }
        return 0;
    }
    View Code

    HDU 2444

    题意:给你一张图,你需要先判断这个图是不是二分图,然后在求其的最大匹配。

    题解:用bfs对图进行染色,如果发现有相邻且同色的点那么就不是二分图。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int MAXN=200+5;
    vector<int> G[MAXN];
    int uN, linker[MAXN];
    bool used[MAXN];
    
    bool dfs(int u)
    {
        for(int i=0; i<G[u].size(); i++)
        {
            int v=G[u][i];
            if(!used[v])
            {
                used[v]=true;
                if(linker[v]==-1 || dfs(linker[v]))
                {
                    linker[v]=u;
                    return true;
                }
            }
        }
        return false;
    }
    
    int hungary()
    {
        int res=0;
        memset(linker, -1, sizeof(linker));
        for(int u=1; u<=uN; u++)
        {
            memset(used, false, sizeof(used));
            if(dfs(u)) res++;
        }
        return res;
    }
    
    int vis[MAXN];
    bool check()
    {
        queue<int> q;
        memset(vis, false, sizeof(vis));
        q.push(1); vis[1]=true;
        while(!q.empty())
        {
            int u=q.front(); q.pop();
            for(int i=0; i<G[u].size(); i++)
            {
                int v=G[u][i];
                if(!vis[v])
                {
                    if(vis[u]==1) vis[v]=2; else vis[v]=1;  //这里的染色别弄错了
                    //if(vis[u]) vis[v]=2; else vis[v]=1;  WA
                    q.push(v);
                }
                else if(vis[u]==vis[v])    return false;
            }    
        }
        return true; 
    }
    
    int main()
    {
        //freopen("in.txt", "r", stdin);
        int n, m;
        while(cin>>n>>m)
        {
            uN=n;
            for(int i=0; i<=n; i++) G[i].clear(); 
            while(m--)
            {
                int u, v; 
                cin>>u>>v;
                G[u].push_back(v);
                G[v].push_back(u);
            }
            if(check()) cout<<hungary()/2<<endl;
            else cout<<"No"<<endl;
        }
        return 0;
    }
    View Code

    HDU 1281

    题意:

      小希和Gardon在玩一个游戏:对一个N*M的棋盘,在格子里放尽量多的一些国际象棋里面的“车”,并且使得他们不能互相攻击,这当然很简单,但是Gardon限制了只有某些格子才可以放,小希还是很轻松的解决了这个问题(见下图)注意不能放车的地方不影响车的攻击。 
      所以现在Gardon想让小希来解决一个更难的问题,在保证尽量多的“车”的前提下,棋盘里有些格子是可以避开的,也就是说,不在这些格子上放车,也可以保证尽量多的“车”被放下。但是某些格子若不放子,就无法保证放尽量多的“车”,这样的格子被称做重要点。 Gardon想让小希算出有多少个这样的重要点,你能解决这个问题么。求多少个这样的点,和最大可以放下的车。

    题解:把行看成x部,列看成y部,可以放车的点的坐标,即x,y可以构成一个匹配(和 HDU 1045一样的道理),每一行每一列只有一个车,相当于行和列在做匹配,在尝试接触g【x】【y】看求出的最大匹配是否发生变化。若发生变化,即是重要点。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int MAXN=100+5;
    int uN, vN;
    int g[MAXN][MAXN];
    int linker[MAXN];
    bool used[MAXN];
    
    bool dfs(int u)
    {
        for(int v=0; v<vN; v++)
            if(g[u][v] && !used[v])
            {
                used[v]=true;
                if(linker[v]==-1 || dfs(linker[v]))
                {
                    linker[v]=u;
                    return true;
                }
            }
        return false;
    }
    
    int hungary()
    {
        int res=0;
        memset(linker, -1, sizeof(linker));
        for(int u=0; u<uN; u++)
        {
            memset(used, 0, sizeof(used));
            if(dfs(u)) res++;
        }
        return res;
    }
    
    int main()
    {
        int n, m, k, kase=0;
        while(cin>>n>>m>>k)
        {
            uN=n, vN=m;
            memset(g, 0, sizeof(g));
            while(k--)
            {
                int u, v;
                cin>>u>>v;
                g[u-1][v-1]=1;
            }
            int ans=hungary();
            int res=0;
            for(int i=0; i<n; i++)
                for(int j=0; j<m; j++)
                {
                    if(g[i][j]) {
                        g[i][j]=0;
                        if(ans>hungary()) res++;
                        g[i][j]=1;
                    }
                }
             printf("Board %d have %d important blanks for %d chessmen.
    ",++kase, res, ans);
        }
        return 0;
    }
    View Code

    HDU 2819 (匹配路径)

    题意:可交换任意两行或任意两列,最终是主对角线上全为1,输出交换过程(如果可以的话),否则-1

    题解:如果可行的话,一定可以只交换列或只交换行得到,因为不管怎么交换,原先在同一行的始终在同一行,原先在同一列的始终在同一列。用行列构图(和上面的一样),如果a[i][j]=1,则加边,求最大匹配就行。

       输出交换的路径的时候,注意这个交换中的行列是指在交换后的新图的中的位置(判断交换后swap())。

       注意:解除流绑定后,就要不要用printf了,用printf就不要接触流绑定,会WA

    #include <bits/stdc++.h>
    using namespace std;
    
    const int MAXN=200+5;
    int uN, vN;
    int g[MAXN][MAXN];
    int linker[MAXN];
    bool used[MAXN];
    
    bool dfs(int u)
    {
        for(int v=0; v<vN; v++)
            if(g[u][v] && !used[v])
            {
                used[v]=true;
                if(linker[v]==-1 || dfs(linker[v]))
                {
                    linker[v]=u;
                    return true;
                }
            }
        return false;
    }
    
    int hungary()
    {
        int res=0;
        memset(linker, -1, sizeof(linker));
        for(int u=0; u<uN; u++)
        {
            memset(used, 0, sizeof(used));
            if(dfs(u)) res++;
        }
        return res;
    }
    
    int A[MAXN*MAXN], B[MAXN*MAXN];
    
    int main()
    {
        //freopen("in.txt", "r", stdin);
        //ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
        int n;
        while(cin>>n)
        {
            uN=vN=n;
            for(int i=0; i<n; i++)
                for(int j=0; j<n; j++)
                    cin>>g[i][j];
            if(hungary()<n){
                cout<<"-1"<<endl;
                continue;
            }
            int cnt=0;
            for(int j, i=0; i<uN; i++)  //记录匹配过程中的匹配路径 
            {
                for(j=0; j<vN&&linker[j]!=i; j++) ;
                if(i!=j){               //交换i,j。这个交换中的行列是指在新图的中的位置 
                    A[cnt]=i, B[cnt++]=j;
                    swap(linker[i], linker[j]);
                }
            }
            cout<<cnt<<endl;
            for(int i=0; i<cnt; i++)
                printf("C %d %d
    ", A[i]+1, B[i]+1);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Linux系统类别
    ST-GCN基于skeleton的动作识别
    (gcc/g++)/clang/cl编译器
    羽毛球经典教材范例
    opencv批量读取图片
    十六、mysql 变量
    十五、mysql 存储过程
    十四、mysql 视图
    十三、mysql TCL语言
    十二、mysql 标识列
  • 原文地址:https://www.cnblogs.com/Yokel062/p/11330059.html
Copyright © 2011-2022 走看看