HDU - 2063 过山车(最大匹配数)(模板)

1、男生女生一起坐过山车,每一排有两个座位,但是有个条件,就是每个女生必须找个男生做同伴一起(但是女生只愿意和某几个男生中的一个做同伴),求最多可以有多少对男女生组合坐上过山车。

2、二分图的最大匹配,女生作为X集合(左边),男生作为Y集合(右边)

3、

3.1匈牙利算法(邻接矩阵):

/*
顶点编号从0开始的
邻接矩阵(匈牙利算法)
二分图匹配(匈牙利算法的DFS实现)(邻接矩阵形式)
初始化:g[][]两边顶点的划分情况
建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配
g没有边相连则初始化为0
uN是匹配左边的顶点数,vN是匹配右边的顶点数
左边是X集,右边是Y集
调用:res=hungary();输出最大匹配数
优点:适用于稠密图,DFS找增广路,实现简洁易于理解
时间复杂度:O(VE)
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;

const int MAXN=512;
int uN,vN;//u,v 的数目,使用前面必须赋值
int g[MAXN][MAXN];//邻接矩阵,记得初始化
int linker[MAXN];//linker[v]=u,表示v(右边Y集合中的点)连接到u(左边X集合中的点)
bool used[MAXN];
bool dfs(int u){//判断以X集合中的节点u为起点的增广路径是否存在
    for(int v=0;v<vN;v++)//枚举右边Y集合中的点
        if(g[u][v]&&!used[v]){//搜索Y集合中所有与u相连的未访问点v
            used[v]=true;//访问节点v
            if(linker[v]==-1||dfs(linker[v])){//是否存在增广路径
                //若v是未盖点(linker[v]==-1表示没有与v相连的点,即v是未盖点),找到增广路径
                //或者存在从与v相连的匹配点linker[v]出发的增广路径
                linker[v]=u;//设定(u,v)为匹配边,v连接到u
                return true;//返回找到增广路径
            }
        }
        return false;
}
int hungary(){//返回最大匹配数(即最多的匹配边的条数)
    int res=0;//最大匹配数
    memset(linker,-1,sizeof(linker));//匹配边集初始化为空
    for(int u=0;u<uN;u++){//找X集合中的点的增广路
        memset(used,false,sizeof(used));//设Y集合中的所有节点的未访问标志
        if(dfs(u))res++;//找到增广路,匹配数(即匹配边的条数)+1
    }
    return res;
}

int main(){
    int i,ans;
    int K,M,N;//K边数,M是左边的顶点数,N是右边的顶点数
    int u,v;
    while(~scanf("%d",&K)){
        if(K==0)break;
        scanf("%d%d",&M,&N);
        uN=M;//匹配左边的顶点数
        vN=N;//匹配右边的顶点数
        memset(g,0,sizeof(g));//二分图的邻接矩阵初始化
        for(i=0;i<K;++i){
            scanf("%d%d",&u,&v);
            g[--u][--v]=1;//顶点编号从0开始的
        }
        ans=hungary();
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

3.2匈牙利算法(邻接表):

/*
//顶点编号从0开始的
匈牙利算法邻接表形式
使用前用init()进行初始化,给uN赋值
加边使用函数addedge(u,v)
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;

const int MAXN=512;//点数的最大值
const int MAXM=1024;//边数的最大值
struct Edge{
    int to,next;
}edge[MAXM];
int head[MAXN],tot;
void init(){
    tot=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
int linker[MAXN];
bool used[MAXN];
int uN;
bool dfs(int u){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        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));
    //点的编号0~uN-1
    for(int u=0;u<uN;u++){
        memset(used,false,sizeof(used));
        if(dfs(u))res++;
    }
    return res;
}
int main(){
    int i,ans;
    int K,M,N;
    int u,v;
    while(~scanf("%d",&K)){
        if(K==0)break;
        scanf("%d%d",&M,&N);
        uN=M;//匹配左边的顶点数
        init();//初始化
        for(i=0;i<K;++i){
            scanf("%d%d",&u,&v);
            addedge(--u,--v);//顶点编号从0开始的
        }
        ans=hungary();
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

3.3匈牙利算法(邻接表,用vector实现):

/*
//顶点编号从1开始的
用STL中的vector建立邻接表实现匈牙利算法
效率比较高
处理点比较多的效率很高。1500的点都没有问题
*/
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;

const int MAXN=512;//这个值要超过两边个数的较大者,因为有linker
int linker[MAXN];
bool used[MAXN];
vector<int>G[MAXN];
int uN;
bool dfs(int u)
{
    int sz=G[u].size();
    for(int i=0; i<sz; i++)
    {
        if(!used[G[u][i]])
        {
            used[G[u][i]]=true;
            if(linker[G[u][i]]==-1||dfs(linker[G[u][i]]))
            {
                linker[G[u][i]]=u;
                return true;
            }
        }
    }
    return false;
}

int hungary()
{
    int u;
    int res=0;
    memset(linker,-1,sizeof(linker));
    for(u=1; u<=uN; u++)
    {
        memset(used,false,sizeof(used));
        if(dfs(u)) res++;
    }
    return res;
}

int main()
{
    int i,ans;
    int K,M,N;
    int u,v;
    while(~scanf("%d",&K))
    {
        if(K==0)break;
        scanf("%d%d",&M,&N);
        uN=M;//匹配左边的顶点数
        for(i=0; i<MAXN; i++)//二分图的邻接表初始化
            G[i].clear();
        for(i=0; i<K; ++i)
        {
            scanf("%d%d",&u,&v);
            G[u].push_back(v);//顶点编号从1开始的
        }
        ans=hungary();
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

3.4Hopcroft-Karp算法(邻接矩阵):

/* *********************************************
//顶点编号从1开始的
二分图匹配(Hopcroft-Carp的算法)。
初始化:g[][]邻接矩阵
调用:res=MaxMatch();  Nx,Ny要初始化!!!
时间复杂大为 O(V^0.5 E)
适用于数据较大的二分匹配
需要queue头文件
********************************************** */
#include<iostream>
#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
const int MAXN=512;
const int INF=1<<28;
int g[MAXN][MAXN],Mx[MAXN],My[MAXN],Nx,Ny;
int dx[MAXN],dy[MAXN],dis;
bool vst[MAXN];
bool searchP()
{
    queue<int>Q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=1; i<=Nx; i++)
        if(Mx[i]==-1)
        {
            Q.push(i);
            dx[i]=0;
        }
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        if(dx[u]>dis)  break;
        for(int v=1; v<=Ny; v++)
            if(g[u][v]&&dy[v]==-1)
            {
                dy[v]=dx[u]+1;
                if(My[v]==-1)  dis=dy[v];
                else
                {
                    dx[My[v]]=dy[v]+1;
                    Q.push(My[v]);
                }
            }
    }
    return dis!=INF;
}
bool DFS(int u)
{
    for(int v=1; v<=Ny; v++)
        if(!vst[v]&&g[u][v]&&dy[v]==dx[u]+1)
        {
            vst[v]=1;
            if(My[v]!=-1&&dy[v]==dis) continue;
            if(My[v]==-1||DFS(My[v]))
            {
                My[v]=u;
                Mx[u]=v;
                return 1;
            }
        }
    return 0;
}
int MaxMatch()
{
    int res=0;
    memset(Mx,-1,sizeof(Mx));
    memset(My,-1,sizeof(My));
    while(searchP())
    {
        memset(vst,0,sizeof(vst));
        for(int i=1; i<=Nx; i++)
            if(Mx[i]==-1&&DFS(i))  res++;
    }
    return res;
}

int main()
{
    int i,ans;
    int K,M,N;
    int u,v;
    while(~scanf("%d",&K))
    {
        if(K==0)break;
        scanf("%d%d",&M,&N);
        Nx=M;//匹配左边的顶点数
        Ny=N;//匹配右边的顶点数
        memset(g,0,sizeof(g));//二分图的邻接矩阵初始化
        for(i=0; i<K; ++i)
        {
            scanf("%d%d",&u,&v);
            g[u][v]=1;//顶点编号从1开始的
        }
        ans=MaxMatch();
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

3.5Hopcroft-Karp算法(邻接表,用vector实现):

/*
//顶点编号从0开始的
二分图匹配(Hopcroft-Karp算法)
复杂度O(squt(n)*E)
邻接表存图,vector实现
vector先初始化,然后加入边
uN为左端的顶点数,使用前赋值(点编号0开始)
*/
#include<iostream>
#include<stdio.h>
#include<vector>
#include<queue>
#include<string.h>
using namespace std;

const int MAXN=512;
const int INF=0x3f3f3f3f;
vector<int>G[MAXN];
int uN;
int Mx[MAXN],My[MAXN];
int dx[MAXN],dy[MAXN];
int dis;
bool used[MAXN];
bool SearchP(){
    queue<int>Q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=0;i<uN;i++)
        if(Mx[i]==-1){
            Q.push(i);
            dx[i]=0;
        }
    while(!Q.empty()){
        int u=Q.front();
        Q.pop();
        if(dx[u]>dis)break;
        int sz=G[u].size();
        for(int i=0;i<sz;i++){
            int v=G[u][i];
            if(dy[v]==-1){
                dy[v]=dx[u]+1;
                if(My[v]==-1)dis=dy[v];
                else{
                    dx[My[v]]=dy[v]+1;
                    Q.push(My[v]);
                }
            }
        }
    }
    return dis!=INF;
}
bool DFS(int u){
    int sz=G[u].size();
    for(int i=0;i<sz;i++){
        int v=G[u][i];
        if(!used[v]&&dy[v]==dx[u]+1){
            used[v]=true;
            if(My[v]!=-1&&dy[v]==dis)continue;
            if(My[v]==-1||DFS(My[v])){
                My[v]=u;
                Mx[u]=v;
                return true;
            }
        }
    }
    return false;
}
int MaxMatch(){
    int res=0;
    memset(Mx,-1,sizeof(Mx));
    memset(My,-1,sizeof(My));
    while(SearchP()){
        memset(used,false,sizeof(used));
        for(int i=0;i<uN;i++)
            if(Mx[i]==-1&&DFS(i))
                res++;
    }
    return res;
}

int main(){
    int i,ans;
    int K,M,N;
    int u,v;
    while(~scanf("%d",&K)){
        if(K==0)break;
        scanf("%d%d",&M,&N);
        uN=M;//匹配左边的顶点数
        for(i=0;i<MAXN;++i)//二分图的邻接表初始化
            G[i].clear();
        for(i=0;i<K;++i){
            scanf("%d%d",&u,&v);
            G[--u].push_back(--v);//顶点编号从0开始的
        }
        ans=MaxMatch();
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2015-11-19 14:00  gongpixin  阅读(315)  评论(0编辑  收藏  举报