二分图(匈牙利算法)

重新复习了二分图匹配,好像不用那个找对象版本的二分图来理解了,而是可以真正理解了。
首先判断一个图是否是一个二分图,二分图应该分为两个集合,两个集合内的点没有相连,但是两个集合之间是相连的,所以我们就bfs给每个点染色,如果有冲突,那么就不是。
那么怎么找出二分图的最大匹配呢?
我们应该遍历每个点,然后通过这个点找出一条增广路径(很重要,不懂的话百度),这样每次就能多匹配一个,但是每次遍历的时候肯定不能遍历本次已经遍历过的点,所以要标记一下。

/***判断是否是二分图并找最大匹配***/
#include<iostream>
#include<string.h>
#include<queue>
#include<cstdio>
using namespace std;
const int maxn=405*205;
int n,m;
struct node
{
    int u,v,nex;
} e[maxn];
int first[maxn];
int tot;
int vis[maxn];
int used[maxn];
int match[maxn];
void init()
{
    tot=0;
    memset(first,-1,sizeof(first));
    memset(vis,0,sizeof(vis));
    memset(match,0,sizeof(match));
    memset(used,0,sizeof(used));
}
void edge(int u,int v)
{
    e[tot].v=v;
    e[tot].nex=first[u];
    first[u]=tot++;
}
int bfs()
{
    queue<int>q;
    vis[1]=1;//染色
    used[1]=1;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=first[u]; ~i; i=e[i].nex)
        {
            int v=e[i].v;
            if(used[v]==0)
            {
                vis[v]=1-vis[u];
                used[v]=1;
                q.push(v);
            }
            else
            {
                if(vis[u]==vis[v])
                    return 1;
            }
        }
    }
    return 0;
}
int Dfs(int u)
{
    for(int i=first[u]; ~i; i=e[i].nex)
    {
        int v=e[i].v;
        if(!vis[v])//易错
        {
            vis[v]=1;
            if(match[v]==0||Dfs(match[v]))
            {
                match[v]=u;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=0; i<m; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            edge(u,v);
            edge(v,u);
        }
        if(bfs())
        {
            printf("No\n");
            continue;
        }
        int ans=0;
        for(int i=1; i<=n; i++)
        {
            memset(vis,0,sizeof(vis));
            ans+=Dfs(i);
        }
        printf("%d\n",ans/2);
    }
    return 0;
}

来自renfei的板子
补充定义和定理:

最大匹配数:最大匹配的匹配边的数目

最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择

最大独立数:选取最多的点,使任意所选两点均不相连

最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。

定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)

定理2:最大匹配数 = 最大独立数

定理3:最小路径覆盖数 = 顶点数 - 最大匹配数

下面给出匈牙利算法的 DFS 和 BFS 版本的代码

// 顶点、边的编号均从 0 开始
// 邻接表储存

struct Edge
{
    int from;
    int to;
    int weight;

    Edge(int f, int t, int w):from(f), to(t), weight(w) {}
};

vector<int> G[__maxNodes]; /* G[i] 存储顶点 i 出发的边的编号 */
vector<Edge> edges;
typedef vector<int>::iterator iterator_t;
int num_nodes;
int num_left;
int num_right;
int num_edges;
int matching[__maxNodes]; /* 存储求解结果 */
int check[__maxNodes];

bool dfs(int u)
{
    for (iterator_t i = G[u].begin(); i != G[u].end(); ++i) { // 对 u 的每个邻接点
        int v = edges[*i].to;
        if (!check[v]) {     // 要求不在交替路中
            check[v] = true; // 放入交替路
            if (matching[v] == -1 || dfs(matching[v])) {
                // 如果是未盖点,说明交替路为增广路,则交换路径,并返回成功
                matching[v] = u;
                matching[u] = v;
                return true;
            }
        }
    }
    return false; // 不存在增广路,返回失败
}

int hungarian()
{
    int ans = 0;
    memset(matching, -1, sizeof(matching));
    for (int u=0; u < num_left; ++u) {
        if (matching[u] == -1) {
            memset(check, 0, sizeof(check));
            if (dfs(u))
                ++ans;
        }
    }
    return ans;
}
queue<int> Q;
int prev[__maxNodes];
int Hungarian()
{
    int ans = 0;
    memset(matching, -1, sizeof(matching));
    memset(check, -1, sizeof(check));
    for (int i=0; i<num_left; ++i) {
        if (matching[i] == -1) {
            while (!Q.empty()) Q.pop();
            Q.push(i);
            prev[i] = -1; // 设 i 为路径起点
            bool flag = false; // 尚未找到增广路
            while (!Q.empty() && !flag) {
                int u = Q.front();
                for (iterator_t ix = G[u].begin(); ix != G[u].end() && !flag; ++ix) {
                    int v = edges[*ix].to;
                    if (check[v] != i) {
                        check[v] = i;
                        Q.push(matching[v]);
                        if (matching[v] >= 0) { // 此点为匹配点
                            prev[matching[v]] = u;
                        } else { // 找到未匹配点,交替路变为增广路
                            flag = true;
                            int d=u, e=v;
                            while (d != -1) {
                                int t = matching[d];
                                matching[d] = e;
                                matching[e] = d;
                                d = prev[d];
                                e = t;
                            }
                        }
                    }
                }
                Q.pop();
            }
            if (matching[i] != -1) ++ans;
        }
    }
    return ans;
}

hdu2444

自己的匈牙利dfs板子+判断是否是二分图

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 201
using namespace std;
int Map[MAXN][MAXN];
int color[MAXN];//染色
int N, M;
bool judge(int u)//判断是否是二分图
{
    for(int i = 1; i <= N; i++)
    {
        if(Map[u][i] == 0 && Map[i][u] == 0) continue;  ///注意判断条件,因为这是建的有向图了,因为在一般的二分图匹配的过程中是有向图和
        ///无向图都一样的,因为用的是match都做标记了,但是在这里dfs判断的时候就必须不一样了
        if(color[u] == color[i]) return false;
        if(!color[i])
        {
            color[i] = 3 - color[u];
            if(!judge(i))
                return false;
        }
    }
    return true;
}
int match[MAXN];
bool used[MAXN];
int DFS(int u)
{
    for(int i = 1; i <= N; i++)
    {
        if(Map[u][i] == 0) continue;
        if(!used[i])
        {
            used[i] = true;
            if(match[i] == -1 || DFS(match[i]))
            {
                match[i] = u;
                return 1;
            }
        }
    }
    return 0;
}
void solve()
{
    memset(Map, 0, sizeof(Map));
    int a, b;
    for(int i = 1; i <= M; i++)
    {
        scanf("%d%d", &a, &b);
        Map[a][b] = 1;
    }
    memset(color, 0, sizeof(color));
    color[1] = 1;
    if(!judge(1))
    {
        printf("No\n");
        return ;
    }
    int ans = 0;
    memset(match, -1, sizeof(match));
    for(int i = 1; i <= N; i++)
    {
        memset(used, false, sizeof(used));
        ans += DFS(i);
    }
    printf("%d\n", ans);
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        solve();
    }
    return 0;
}

二分图最大匹配+输出路径

hdu1289

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, k;
int ans;
int mapp[110][110];
int line[555][555];
int used[555];
int gril[555];
int x[10010], y[10010];
int dfs(int x)
{
    //printf("x=%d\n", x);
    for (int i = 1; i <= n; i++)
    {
        if (line[x][i] && used[i] == 0)
        {
            //printf("%d %d\n", x, i);
            used[i] = 1;
            if (gril[i] == 0 || dfs(gril[i]))
            {
                gril[i] = x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    while (~scanf("%d", &n))
    {
        memset(mapp, 0, sizeof(mapp));
        memset(line, 0, sizeof(line));
        memset(x, 0, sizeof(x));
        memset(y, 0, sizeof(y));
        memset(gril, 0, sizeof(gril));
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                scanf("%d", &mapp[i][j]);
                if (mapp[i][j])
                {
                    line[i][j] = 1;
                    //line[j][i]=1;
                }
            }
        }
        ans = 0;
        for (int i = 1; i <= n; i++)
        {
            memset(used, 0, sizeof(used));
            if (dfs(i))
                ans++;
        }
        //printf("ans=%d\n", ans);
        if (ans < n)
            printf("-1\n");
        else
        {
            int tot = 0;
            int j;
            for (int i = 1; i <= n; i++)
            {
                for (j = i; j <= n; j++)
                {
                    if(gril[j]==i)
                        break;
                }
                if(j!=i)
                {
                    x[tot]=i;
                    y[tot++]=j;
                    swap(gril[i],gril[j]);
                }
            }
            printf("%d\n", tot);
            for (int i = 0; i < tot; i++)
            {
                printf("C %d %d\n", x[i], y[i]);
            }
        }
    }
    return 0;
}

poj3020

最小路径覆盖

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=605;
char str[maxn][maxn];
int gril[maxn*maxn];
int used[maxn*maxn];
int m[maxn][maxn];
int n,nn;
int top;
int ans;
struct node
{
    int v;
    int nex;
} e[maxn*maxn];
int first[maxn*maxn];
void edge(int u,int v)
{
    e[top].v=v;
    e[top].nex=first[u];
    first[u]=top++;
}
void init(int x,int y)
{
    //printf("fsdjklf\n");

    if(x-1>=0&&m[x-1][y])
    {

        edge(m[x][y],m[x-1][y]);
        //edge(m[x-1][y],m[x][y]);
    }
    if(x+1<n&&m[x+1][y])
    {

        edge(m[x][y],m[x+1][y]);
        //edge(m[x+1][y],m[x][y]);
    }
    if(y-1>=0&&m[x][y-1])
    {

        edge(m[x][y],m[x][y-1]);
        //edge(m[x][y-1],m[x][y]);
    }
    if(y+1<nn&&m[x][y+1])
    {
        edge(m[x][y],m[x][y+1]);
        //edge(m[x][y+1],m[x][y]);
    }

}
int dfs(int x)
{
    for(int i=first[x]; i!=-1; i=e[i].nex)
    {

        int p=e[i].v;
        if(used[p]==0)
        {
            used[p]=1;
            if(gril[p]==0||dfs(gril[p]))
            {
                gril[p]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int t;
    int tt=1;
    scanf("%d",&t);
    while(t--)
    {
        memset(gril,0,sizeof(gril));
        memset(str,0,sizeof(str));
        memset(m,0,sizeof(m));
        memset(first,-1,sizeof(first));
        int tot=1;
        top=0;
        scanf("%d%d",&n,&nn);
        for(int i=0; i<n; i++)
        {

            scanf("%s",str[i]);
            for(int j=0; j<nn; j++)
            {
                if(str[i][j]=='*')
                {
                    m[i][j]=tot++;
                }
            }
        }
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<nn; j++)
            {
                if(m[i][j])
                    init(i,j);
            }
        }
        //printf("fjsdlf\n");
        ans=0;
        for(int i=1; i<=tot; i++)
        {
            //printf("%d\n",i);
            memset(used,0,sizeof(used));
            if(dfs(i))
            {
                ans++;
            }
        }
        printf("%d\n",tot-ans/2-1);
    }
    return 0;
}

hdu1151

#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1505;
int ans;
int tot;
int n,m;
struct node
{
    int v;
    int nex;
} e[maxn*4];
int first[4*maxn];
void edge(int u,int v)
{
    e[tot].v=v;
    e[tot].nex=first[u];
    first[u]=tot++;
}
int used[maxn];
int g[maxn];
int dfs(int x)
{
    for(int i=first[x]; i!=-1; i=e[i].nex)
    {
        int p=e[i].v;
        if(used[p]==0)
        {
            used[p]=1;
            if(g[p]==-1||dfs(g[p]))
            {
                //printf("%d\n",x);
                g[p]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        ans=0;
        tot=0;
        memset(first,-1,sizeof(first));
        memset(g,-1,sizeof(g));
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            edge(u,v);
            //edge(v,u);
        }
        for(int i=1; i<=n; i++)
        {
            memset(used,0,sizeof(used));
            if(dfs(i))
                ans++;
        }
        printf("%d\n",n-ans);
    }
    return 0;
}

poj2594

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
#include<string.h>
using namespace std;
#define LL long long
const int maxn=505;
int g[maxn];
int used[maxn];
int line[maxn][maxn];
//int first[maxn];
int tot;
int n,m;
int ans;
//struct node
//{
//    int v;
//    int nex;
//} e[maxn];
//void edge(int u,int v)
//{
//    e[tot].v=v;
//    e[tot].nex=first[u];
//    first[u]=tot++;
//}
int dfs(int x)
{
    for(int i=1;i<=n;i++)
    {
        if(line[x][i]&&used[i]==0)
        {
            used[i]=1;
            if(g[i]==-1||dfs(g[i]))
            {
                g[i]=x;
                return 1;
            }
        }
    }
    return 0;

}

void floy()
{
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(line[i][k]&&line[k][j])
                {
                    line[i][j]=1;
                    //edge(i,j);
                }
            }
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0)
            return 0;
        if(m==0)
        {
            printf("%d\n",n);
            continue;
        }
        ans=0;
        memset(line,0,sizeof(line));
        memset(g,-1,sizeof(g));
        //memset(first,-1,sizeof(first));
        tot=0;
        for(int i=0; i<m; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            line[u][v]=1;
            //edge(u,v);
        }
        floy();
        for(int i=1; i<=n; i++)
        {
            memset(used,0,sizeof(used));
            if(dfs(i))
                ans++;
        }
        printf("%d\n",n-ans);
    }
    return 0;
}

hdu1054

最小点覆盖

#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1505;
int ans;
int tot;
struct node
{
    int v;
    int nex;
} e[maxn*4];
int first[4*maxn];
void edge(int u,int v)
{
    e[tot].v=v;
    e[tot].nex=first[u];
    first[u]=tot++;
}
int used[maxn];
int g[maxn];
int dfs(int x)
{
    for(int i=first[x]; i!=-1; i=e[i].nex)
    {
        int p=e[i].v;
        if(used[p]==0)
        {
            used[p]=1;
            if(g[p]==-1||dfs(g[p]))
            {
                //printf("%d\n",x);
                g[p]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        ans=0;
        tot=0;
        memset(first,-1,sizeof(first));
        memset(g,-1,sizeof(g));
        for(int i=0; i<n; i++)
        {
            int u,p;
            scanf("%d:(%d)",&u,&p);
            //printf("%d %d\n",u,p);
            for(int j=0; j<p; j++)
            {
                int v;
                scanf("%d",&v);
                edge(u,v);
                edge(v,u);
            }
        }
        for(int i=0; i<n; i++)
        {
            memset(used,0,sizeof(used));
            if(dfs(i))
                ans++;
        }
        printf("%d\n",ans/2);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值