二分图匹配入门题

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

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.\n",++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\n", A[i]+1, B[i]+1);
    }
    return 0;
}
View Code

 

posted @ 2019-08-13 04:10  N_Yokel  阅读(293)  评论(0编辑  收藏  举报