• 【模板】匹配问题


    二分图匹配

    匈牙利算法

    • 时间复杂度(O(nm))
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <queue>
    using namespace std;
     
    int mp[2100][2100]; // 图的存储矩阵
    int n, m;
    int ans;
    bool vis[2100]; // 当前搜索过程中是否被访问过
    int link[2100]; // y集合中的点在x集合中的匹配点 -1表示未匹配
     
    bool find_(int x) {
        for (int i=1; i<=n; ++i) {
            if (mp[x][i] && !vis[i]) { // 有边相连
                vis[i] = 1; // 标记该点
                if (link[i] == -1 || find_(link[i])) { //该点未匹配 或者匹配的点能找到增光路
                    link[i] = x; // 删掉偶数条边 加进奇数条边
                    return true; // 找到增光路
                }
            }
        }
        return false;
    }
     
    void match() {
        //初始化
        ans = 0;
        memset(link, -1, sizeof(link));
     
        for (int i=1; i<=n; ++i) {
            memset(vis, 0, sizeof(vis)); // 从集合的每个点依次搜索
            if (find_(i)) // 如果能搜索到 匹配数加1
                ans++;
        }
        return;
    }
     
    int main() {
        while(cin >> n >> m) {
            memset(mp, 0, sizeof(mp));
     
            for (int i=0; i<m; ++i) {
                int x, y;
                cin >> x >> y;
                mp[x][y] = 1;
                mp[y][x] = 1;
            }
     
            //判断是不是二分图 过
     
            match();
            cout << ans/2 << endl;
        }
        return 0;
    }
    

    HK算法

    • 时间复杂度为(O(msqrt n))

    • 点的序号要从0开始!

    • 需要把nx,ny都赋值为n(点数)

    const int MAXN = 1010;
    const int MAXM = 1010*1010;
    
    struct Edge {
        int v;
        int next;
    } edge[MAXM];
    
    struct node {
        double x, y;
        double v;
    } a[MAXN], b[MAXN];
    
    int nx, ny;
    int cnt;
    int t;
    int dis;
    
    
    int first[MAXN];
    int xlink[MAXN], ylink[MAXN];
    /*xlink[i]表示左集合顶点所匹配的右集合顶点序号,ylink[i]表示右集合i顶点匹配到的左集合顶点序号。*/
    int dx[MAXN], dy[MAXN];
    /*dx[i]表示左集合i顶点的距离编号,dy[i]表示右集合i顶点的距离编号*/
    int vis[MAXN]; //寻找增广路的标记数组
    
    void init() {
        cnt = 0;
        memset(first, -1, sizeof(first));
        memset(xlink, -1, sizeof(xlink));
        memset(ylink, -1, sizeof(ylink));
    }
    
    void read_graph(int u, int v) {
        edge[cnt].v = v;
        edge[cnt].next = first[u], first[u] = cnt++;
    }
    
    int bfs() {
        queue<int> q;
        dis = INF;
        memset(dx, -1, sizeof(dx));
        memset(dy, -1, sizeof(dy));
        for(int i = 0; i < nx; i++) {
            if(xlink[i] == -1) {
                q.push(i);
                dx[i] = 0;
            }
        }
        while(!q.empty()) {
            int u = q.front();
            q.pop();
            if(dx[u] > dis) break;
            for(int e = first[u]; e != -1; e = edge[e].next) {
                int v = edge[e].v;
                if(dy[v] == -1) {
                    dy[v] = dx[u] + 1;
                    if(ylink[v] == -1) dis = dy[v];
                    else {
                        dx[ylink[v]] = dy[v]+1;
                        q.push(ylink[v]);
                    }
                }
            }
        }
        return dis != INF;
    }
    
    int find(int u) {
        for(int e = first[u]; e != -1; e = edge[e].next) {
            int v = edge[e].v;
            if(!vis[v] && dy[v] == dx[u]+1) {
                vis[v] = 1;
                if(ylink[v] != -1 && dy[v] == dis) continue;
                if(ylink[v] == -1 || find(ylink[v])) {
                    xlink[u] = v, ylink[v] = u;
                    return 1;
                }
            }
        }
        return 0;
    }
    
    int MaxMatch() {
        int ans = 0;
        while(bfs()) {
            memset(vis, 0, sizeof(vis));
            for(int i = 0; i < nx; i++) if(xlink[i] == -1) {
                    ans += find(i);
                }
        }
        return ans;
    }
    

    调用:

    init();
    for(int i = 0; i < m; i++) {
        if(l[edgee[i][0]] && edgee[i][1] != s && !l[edgee[i][1]])    read_graph(edgee[i][0],edgee[i][1]);
        if(l[edgee[i][1]] && edgee[i][0] != s && !l[edgee[i][0]])    read_graph(edgee[i][1],edgee[i][0]);
    }
    nx = n;
    ny = n;
    int ans = MaxMatch();
    

    KM算法 BFS优化

    最优匹配——定理:设(M)是一个带权完全二分图(G)的一个完备匹配,给每个顶点一个可行顶标(第(i)(x)顶点的可行顶标用 (x_i)表示,第(j)个y顶点的可行顶标用(y_j)表示),如果对所有的边((i,j)) in (G),都有(x_i+y_j≥w_{i,j})成立((w_{i,j})表示边的权),且对所有的边((i,j)) in (M),都有 成立,则(M)是图(G)(x_i+y_j≥w_{i,j})一个最优匹配。

    • 时间复杂度(O(n^3))

    • 最小权值匹配,边权取反,答案取反

    • (x_i+y_j>=w_{i,j}),最小顶标和等价于最大权值匹配

      (x_i+y_j<=w_{i,j}),最大顶标和等价于最小权值匹配

    typedef long long ll;
    const int N = 405;
    const ll INF = LONG_LONG_MAX;
    struct KM
    {
        int link_x[N], link_y[N], n, nx, ny;
        bool visx[N], visy[N];
        int que[N << 1], top, fail, pre[N];
        ll mp[N][N], hx[N], hy[N], slk[N];
        inline int check(int i)
        {
            visx[i] =true;
            if(link_x[i])
            {
                que[fail++] = link_x[i];
                return visy[link_x[i]] = true;
            }
            while(i)
            {
                link_x[i] = pre[i];
                swap(i, link_y[pre[i]]);
            }
            return 0;
        }
        void bfs(int S)
        {
            for(int i=1; i<=n; i++)
            {
                slk[i] = INF;
                visx[i] = visy[i] = false;
            }
            top = 0; fail = 1;
            que[0] = S;
            visy[S] = true;
            while(true)
            {
                ll d;
                while(top < fail)
                {
                    for(int i = 1, j = que[top++]; i <= n; i++)
                    {
                        if(!visx[i] && slk[i] >= (d = hx[i] + hy[j] - mp[i][j]))
                        {
                            pre[i] = j;
                            if(d) slk[i] = d;
                            else if(!check(i)) return;
                        }
                    }
                }
                d = INF;
                for(int i=1; i<=n; i++)
                {
                    if(!visx[i] && d > slk[i]) d = slk[i];
                }
                for(int i=1; i<=n; i++)
                {
                    if(visx[i]) hx[i] += d;
                    else slk[i] -= d;
                    if(visy[i]) hy[i] -= d;
                }
                for(int i=1; i<=n; i++)
                {
                    if(!visx[i] && !slk[i] && !check(i)) return;
                }
            }
        }
        void init(int cntx, int cnty)
        {
            nx = cntx; ny = cnty; n = max(nx, ny);
            top = fail = 0;
            for (int i = 1; i <= n; i++) {
                link_x[i] = link_y[i] = pre[i] = 0;
                hx[i] = hy[i] = 0;
                for (int j = 1; j <= n; j++) mp[i][j] = 0;
            }
        }
        void solve() {
            for(int i=1; i<=n; i++) for(int j=1; j<=n; j++)
                if(hx[i] < mp[i][j]) hx[i] = mp[i][j];
            ll ans = 0;
            for (int i = 1; i <= n; i++) bfs(i);
            for (int i = 1; i <= nx; i++) ans += mp[i][link_x[i]];
            printf("%lld
    ", ans); // 输出最大权值
            
            /*
            for (int i = 1; i <= nx; i++) if(!mp[i][link_x[i]])
                link_x[i] = 0; // 如果不存在该边则置零
            for (int i = 1; i <= nx; i++) {
                if(i != 1) printf(" ");
                printf("%d", link_x[i]); // 输出每一组匹配
            }
            printf("
    ");
             */
        }
    }km;
    

    调用

    int n; scanf("%d", &n);
    km.init(n, n);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            ll w; scanf("%lld", &w);
            km.mp[i][j] = w;
        }
    }
    km.solve();
    

    一般图匹配(带花树)

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=1050;
    bool g[maxn][maxn],inque[maxn],inpath[maxn];
    bool inhua[maxn];
    int st,ed,newbase,ans,n;
    int base[maxn],pre[maxn],match[maxn];
    int head,tail,que[maxn]; 
    int x[maxn],y[maxn],f[maxn],mp[maxn][maxn],ne,np;
    
    void Push(int u)
    {
        que[tail]=u;
        tail++;
        inque[u]=1;
    }
    int Pop()
    {
        int res=que[head];
        head++;
        return res;
    }
    
    int lca(int u,int v)//寻找公共花祖先 
    {
        memset(inpath,0,sizeof(inpath));
        while(1)
        {
            u=base[u];
            inpath[u]=1;
            if(u==st) break;
            u=pre[match[u]];    
        }    
        while(1)
        {
            v=base[v];
            if(inpath[v]) break;
            v=pre[match[v]];
        }
        return v;
    } 
    void reset(int u)//缩环 
    {
        int v;
        while(base[u]!=newbase)
        {
            v=match[u];
            inhua[base[u]]=inhua[base[v]]=1;
            u=pre[v];
            if(base[u]!=newbase) pre[u]=v;
        }
    } 
    void contract(int u,int v)//
    {
        newbase=lca(u,v);
        memset(inhua,0,sizeof(inhua));
        reset(u);
        reset(v);
        if(base[u]!=newbase) pre[u]=v;
        if(base[v]!=newbase) pre[v]=u;
        for(int i=1;i<=n;i++)
        {
            if(inhua[base[i]]){
                base[i]=newbase;
                if(!inque[i])
                    Push(i);
            }
        }
    }
    void findaug()
    {
        memset(inque,0,sizeof(inque));
        memset(pre,0,sizeof(pre));
        for(int i=1;i<=n;i++)//并查集 
            base[i]=i;
        head=tail=1;
        Push(st);
        ed=0;
        while(head<tail)
        {
            int u=Pop();
            for(int v=1;v<=n;v++)
            {
                if(g[u][v]&&(base[u]!=base[v])&&match[u]!=v)
                {
                    if(v==st||(match[v]>0)&&pre[match[v]]>0)//成环 
                        contract(u,v);
                    else if(pre[v]==0)
                    {
                        pre[v]=u;
                        if(match[v]>0)
                            Push(match[v]);
                        else//找到增广路 
                        {
                            ed=v;
                            return ;    
                        }    
                    }
                }
            }
        }
    }
    void aug()
    {
        int u,v,w;
        u=ed;
        while(u>0)
        {
            v=pre[u];
            w=match[v];
            match[v]=u;
            match[u]=v;
            u=w;
        }
    }
    void edmonds()//匹配 
    {
        memset(match,0,sizeof(match));
        for(int u=1;u<=n;u++)
        {
            if(match[u]==0)
            {
                st=u;
                findaug();//以st开始寻找增广路 
                if(ed>0) aug();//找到增广路  重新染色,反向 
            }
        }
    }
    //以上是带花树求最大匹配算法  不用看 
     
    void create()//建图 
    {
        n=0;
        memset(g,0,sizeof(g));
        for(int i=1;i<=np;i++)
            for(int j=1;j<=f[i];j++)
                mp[i][j]=++n;//拆点,给每个度的点编号 
        for(int i=0;i<ne;i++)
        {//此时n+1代表x,n+2代表y 
            for(int j=1;j<=f[x[i]];j++)
                g[mp[x[i]][j]][n+1]=g[n+1][mp[x[i]][j]]=1;//每个度的点与对应的x,y相连 
            for(int j=1;j<=f[y[i]];j++)
                g[mp[y[i]][j]][n+2]=g[n+2][mp[y[i]][j]]=1;
            g[n+1][n+2]=g[n+2][n+1]=1;//x与y相连 
            n+=2;
        }    
    }
    void print()
    {
        ans=0;
        for(int i=1;i<=n;i++)
            if(match[i]!=0)
            {
                ans++;
    //            if(match[i]>i)
    //            cout<<"_____"<<i<<' '<<match[i]<<endl;
            }
        //cout<<"******"<<ans<<' '<<n<<endl;
        if(ans==n)    printf("YES
    ");
        else    printf("NO
    ");
    }
    int main()
    {
        int t,k=0;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&np,&ne);
            for(int i=0;i<ne;i++)
                scanf("%d%d",&x[i],&y[i]);
            for(int i=1;i<=np;i++)
                scanf("%d",&f[i]);
            printf("Case %d: ",++k);
            create();
            edmonds();
            print();    
        }     
        return 0;
    }
    
  • 相关阅读:
    struts2国际化
    Struts2运行机制
    HelloStruts2
    iOS流布局UICollectionView系列七——三维中的球型布局
    iOS- UITextView与键盘回收与键盘遮挡输入框
    web 网页截取图片
    iOS 平台如何使用 TestFlight 进行 Beta 测试
    Ubuntu shortcuts
    ubuntu study
    ios 即时通讯 xmpp
  • 原文地址:https://www.cnblogs.com/Waldeinsamkeit/p/13450798.html
Copyright © 2020-2023  润新知