• 一般图最大匹配


    与二分图不同,一般图的最大匹配用的是带花树开花算法。当然,本质都是寻找增广路。

    下面的模板是来自这个博客的 http://fanhq666.blog.163.com/blog/static/8194342620120304463580/

    ural1099(模板)
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int NMax=230;
    int Next[NMax];
    int spouse[NMax];
    int belong[NMax];
    int findb(int a){
        return belong[a]==a?a:belong[a]=findb(belong[a]);
    }
    void together(int a,int b){
        a=findb(a),b=findb(b);
        if (a!=b)belong[a]=b;
    }
    vector<int> E[NMax];
    int N;
    int Q[NMax],bot;
    int mark[NMax];
    int visited[NMax];
    int findLCA(int x,int y){
        static int t=0;
        t++;
        while (1){
            if (x!=-1){
                x=findb(x);
                if (visited[x]==t)return x;
                visited[x]=t;
                if (spouse[x]!=-1)x=Next[spouse[x]];
                else x=-1;
            }
            swap(x,y);
        }
    }
    void goup(int a,int p){
        while (a!=p){
            int b=spouse[a],c=Next[b];
            if (findb(c)!=p)Next[c]=b;
            if (mark[b]==2)mark[Q[bot++]=b]=1;
            if (mark[c]==2)mark[Q[bot++]=c]=1;
            together(a,b);
            together(b,c);
            a=c;
        }
    }
    void findaugment(int s){
        for (int i=0;i<N;i++)Next[i]=-1,belong[i]=i,mark[i]=0,visited[i]=-1;
        Q[0]=s;bot=1;mark[s]=1;
        for (int head=0;spouse[s]==-1 && head<bot;head++){
            int x=Q[head];
            for (int i=0;i<(int)E[x].size();i++){
                int y=E[x][i];
                if (spouse[x]!=y && findb(x)!=findb(y) && mark[y]!=2){
                    if (mark[y]==1){
                        int p=findLCA(x,y);
                        if (findb(x)!=p)Next[x]=y;
                        if (findb(y)!=p)Next[y]=x;
                        goup(x,p);
                        goup(y,p);
                    }else if (spouse[y]==-1){
                        Next[y]=x;
                        for (int j=y;j!=-1;){
                            int k=Next[j];
                            int l=spouse[k];
                            spouse[j]=k;spouse[k]=j;
                            j=l;
                        }
                        break;
                    }else{
                        Next[y]=x;
                        mark[Q[bot++]=spouse[y]]=1;
                        mark[y]=2;
                    }
                }
            }
        }
    }
    int Map[NMax][NMax];
    int main()
    {
        scanf("%d",&N);
        int x,y;
        for (int i=0;i<N;i++)for (int j=0;j<N;j++)Map[i][j]=0;
        while (scanf("%d%d",&x,&y)!=EOF){
            x--;y--;
            if (x!=y && !Map[x][y]){
                Map[x][y]=Map[y][x]=1;
                E[x].push_back(y);
                E[y].push_back(x);
            }
        }
        for (int i=0;i<N;i++)spouse[i]=-1;
        for (int i=0;i<N;i++)if (spouse[i]==-1)
            findaugment(i);
        int ret=0;
        for (int i=0;i<N;i++)if (spouse[i]!=-1)ret++;
        printf("%d\n",ret);
        for (int i=0;i<N;i++)
            if (spouse[i]!=-1 && spouse[i]>i)
                printf("%d %d\n",i+1,spouse[i]+1);
        return 0;
    }

    做了几道类模板的题目

    zoj3316

    题意:在一个棋盘上给定n的棋子的位子,轮流从棋盘上移除棋子,后者移动的棋子的与前者移动的棋子的哈密顿距离不能超过L。问后手能否赢。

    分析:将哈密顿距离<= L 的棋子连边,很直观的发现,若存在完美匹配,则后手赢。

    zoj3316
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <stdio.h>
    #include <stdlib.h>
    #include <iostream>
    #include <string.h>
    #include <math.h>
    
    using namespace std;
    
    const int N = 400;
    
    int Next[N];
    int spouse[N];
    int belong[N];
    
    int findb(int a){
        return belong[a] == a ? a : belong[a] = findb(belong[a]);
    }
    
    void together(int a,int b){
        a=findb(a),b=findb(b);
        if (a!=b)
            belong[a]=b;
    }
    
    vector<int> E[N];
    int Q[N],bot;
    int mark[N];
    int visited[N];
    int n;
    
    int findLCA(int x,int y){
        static int t=0;
        t++;
        while (1){
            if (x!=-1){
                x=findb(x);
                if (visited[x]==t)return x;
                visited[x]=t;
                if (spouse[x]!=-1)x=Next[spouse[x]];
                else x=-1;
            }
            swap(x,y);
        }
    }
    
    void goup(int a,int p){
        while (a!=p){
            int b=spouse[a],c=Next[b];
            if (findb(c)!=p)Next[c]=b;
            if (mark[b]==2)mark[Q[bot++]=b]=1;
            if (mark[c]==2)mark[Q[bot++]=c]=1;
            together(a,b);
            together(b,c);
            a=c;
        }
    }
    
    void findaugment(int s){
        for (int i=0;i<n;i++)
            Next[i]=-1,belong[i]=i,mark[i]=0,visited[i]=-1;
        Q[0]=s;bot=1;mark[s]=1;
    
        for (int head=0;spouse[s]==-1 && head<bot;head++){
            int x=Q[head];
            for (int i=0;i<(int)E[x].size();i++){
                int y=E[x][i];
                if (spouse[x]!=y && findb(x)!=findb(y) && mark[y]!=2){
                    if (mark[y]==1){
                        int p=findLCA(x,y);
                        if (findb(x)!=p)Next[x]=y;
                        if (findb(y)!=p)Next[y]=x;
                        goup(x,p);
                        goup(y,p);
                    }else if (spouse[y]==-1){
                        Next[y]=x;
                        for (int j=y;j!=-1;){
                            int k=Next[j];
                            int l=spouse[k];
                            spouse[j]=k;spouse[k]=j;
                            j=l;
                        }
                        break;
                    }else{
                        Next[y]=x;
                        mark[Q[bot++]=spouse[y]]=1;
                        mark[y]=2;
                    }
                }
            }
        }
    }
    
    pair<int, int> p[N];
    
    int main()
    {
        int L;
        while(scanf("%d",&n) == 1)
        {
            for(int i = 0; i < n; ++i)
                scanf("%d %d",&p[i].first,&p[i].second);
            scanf("%d",&L);
            for(int i = 0; i < n; ++i)
                for(int j = i + 1; j < n; ++j)
                {
                    int dis = abs(p[i].first - p[j].first) + abs(p[i].second - p[j].second);
                    if(dis <= L)
                    {
                        E[i].push_back(j);
                        E[j].push_back(i);
                    }
                }
    
            for (int i=0;i<n;i++)spouse[i]=-1;
            for (int i=0;i<n;i++)
                if(spouse[i] == -1)
                    findaugment(i);
            int ans = 0;
            for(int i = 0; i < n; ++i)
                if(spouse[i] != -1)
                    ++ans;
            if(ans == n)
                puts("YES");
            else puts("NO");
    
            for(int i = 0; i < n; ++i)
                E[i].clear();
        }
        return 0;
    }

    hdu3446

    题意:在一个R行C列的棋盘上,俩个人轮流移动一个棋子,每次可以向相邻的20个格子移动,走过的每个格子自能走一次。另外,某些各自一开始就固定了不能走。

    无法移动者输。问:先手能否赢。

    分析:首先,忽略K点,将其他能相互移动的格子连边,求一次最大匹配,再将K点加入图中,若存在增广路,则先手赢,否则后手赢。

    hdu3446
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <stdio.h>
    #include <stdlib.h>
    #include <iostream>
    
    using namespace std;
    
    const int N = 230;
    
    int Next[N];
    int spouse[N];
    int belong[N];
    
    int findb(int a){
        return belong[a] == a ? a : belong[a] = findb(belong[a]);
    }
    
    void together(int a,int b){
        a=findb(a),b=findb(b);
        if (a!=b)
            belong[a]=b;
    }
    
    vector<int> E[N];
    int Q[N],bot;
    int mark[N];
    int visited[N];
    int n;
    
    int findLCA(int x,int y){
        static int t=0;
        t++;
        while (1){
            if (x!=-1){
                x=findb(x);
                if (visited[x]==t)return x;
                visited[x]=t;
                if (spouse[x]!=-1)x=Next[spouse[x]];
                else x=-1;
            }
            swap(x,y);
        }
    }
    
    void goup(int a,int p){
        while (a!=p){
            int b=spouse[a],c=Next[b];
            if (findb(c)!=p)Next[c]=b;
            if (mark[b]==2)mark[Q[bot++]=b]=1;
            if (mark[c]==2)mark[Q[bot++]=c]=1;
            together(a,b);
            together(b,c);
            a=c;
        }
    }
    
    void findaugment(int s){
        for (int i=0;i<n;i++)
            Next[i]=-1,belong[i]=i,mark[i]=0,visited[i]=-1;
    
        Q[0]=s;bot=1;mark[s]=1;
    
        for (int head=0;spouse[s]==-1 && head<bot;head++){
            int x=Q[head];
            for (int i=0;i<(int)E[x].size();i++){
                int y=E[x][i];
                if (spouse[x]!=y && findb(x)!=findb(y) && mark[y]!=2){
                    if (mark[y]==1){
                        int p=findLCA(x,y);
                        if (findb(x)!=p)Next[x]=y;
                        if (findb(y)!=p)Next[y]=x;
                        goup(x,p);
                        goup(y,p);
                    }else if (spouse[y]==-1){
                        Next[y]=x;
                        for (int j=y;j!=-1;){
                            int k=Next[j];
                            int l=spouse[k];
                            spouse[j]=k;spouse[k]=j;
                            j=l;
                        }
                        break;
                    }else{
                        Next[y]=x;
                        mark[Q[bot++]=spouse[y]]=1;
                        mark[y]=2;
                    }
                }
            }
        }
    }
    int has[N][N];
    int dir[21][2] = {{-2,-2},{-2,-1},{-2,1},{-2,2},{-1,-2},{-1,-1},{-1,0},{-1,1},{-1,2},{0,-1},{0,1},{1,-2},{1,-1},{1,0},{1,1},{1,2},{2,-2},{2,-1},{2,1},{2,2}};
    char str[N][N];
    bool Map[N][N];
    
    int main()
    {
        int T, Ki, Kj, cas = 0;
        scanf("%d",&T);
        while(T--)
        {
            int r, c;
            scanf("%d %d",&r,&c);
            n = 0;
            for(int i = 0; i < r; ++i)
            {
                scanf("%s",str[i]);
                for(int j = 0; j < c; ++j)
                {
                    if(str[i][j] == 'K')
                        Ki = i, Kj = j;
                    if(str[i][j] != '#')
                        has[i][j] = n++;
                }
            }
            memset(Map, 0, sizeof(Map));
            //cout << n << endl;
            for(int i = 0; i < r; ++i)
                for(int j = 0; j < c; ++j)
                {
                    if(str[i][j] != '.')
                        continue;
                    for(int k = 0; k < 20; ++k)
                    {
                        int ni = i + dir[k][0];
                        int nj = j + dir[k][1];
                        if(ni < 0 || ni >= r || nj < 0 || nj >= c || str[ni][nj] != '.')
                            continue;
                        int a = has[i][j], b = has[ni][nj];
                        if(Map[a][b] == 0)
                        {
                            
                            E[a].push_back(b);
                            E[b].push_back(a);
                            Map[a][b] = Map[b][a] = 1;
                        }
                    }
                }
    
            for (int i=0;i<n;i++)spouse[i]=-1;
            for (int i=0;i<n;i++)if (spouse[i]==-1)
                findaugment(i);
            for(int k = 0; k < 20; ++k)
            {
                int i = Ki + dir[k][0];
                int j = Kj + dir[k][1];
                if(i < 0 || i >= r || j < 0 || j >= c || str[i][j] != '.')
                    continue;
                E[has[Ki][Kj]].push_back(has[i][j]);
                E[has[i][j]].push_back(has[Ki][Kj]);
            }
    
            findaugment(has[Ki][Kj]);
            printf("Case #%d: ", ++cas);
            if(spouse[has[Ki][Kj]] != -1)
                puts("daizhenyang win");
            else puts("daizhenyang lose");
            for(int i = 0; i < n; ++i)
                E[i].clear();
        }
        return 0;
    }

    hdu3551 Hard Problem

    题意:给定一个无向图,通过删边,问能否通过删边,得到一个得定的点的度数序列? 图中存在重边,不存在自环。

    思路:参考自http://blog.sina.com.cn/s/blog_6af663940100n7pr.html

     “我们应该删除哪些边呢? 预处理每个顶点的度数d[i], 若d[i] = deg[i], 那么 与这个点相连的边是不能删掉的。原因很显然。若i与j之间有边,并且d[i]>deg[i], d[j]>deg[j]那么这条边是可以删除的。接下来如何建图呢? 将每个点i 拆成 d[i] - deg[i]个点。如果i与j之间的边e可以删除, 则边e与i、j拆出的每个点连一条边 ei, ej(重边连多次)。然后求该一般图最大匹配,若存在完美匹配,则YES。”
    一开始将一条可删边e的俩个端点各自拆出来的点,俩俩连了一条边,结果直接TLE了。 其实也是,连了很多多余的边。对于可删边e 的俩个端点i 和 j , 将i和j拆出的所有点连边,将j和i拆除的所有点连边即可。。 200+ms

    hdu3551
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <stdio.h>
    #include <stdlib.h>
    #include <iostream>
    #include <string.h>
    #include <math.h>
    
    using namespace std;
    
    const int N = 800;
    
    int Next[N];
    int spouse[N];
    int belong[N];
    
    int findb(int a){
        return belong[a] == a ? a : belong[a] = findb(belong[a]);
    }
    
    void together(int a,int b){
        a=findb(a),b=findb(b);
        if (a!=b)
            belong[a]=b;
    }
    
    vector<int> E[N];
    int Q[N],bot;
    int mark[N];
    int visited[N];
    int n;
    
    int findLCA(int x,int y){
        static int t=0;
        t++;
        while (1){
            if (x!=-1){
                x=findb(x);
                if (visited[x]==t)return x;
                visited[x]=t;
                if (spouse[x]!=-1)x=Next[spouse[x]];
                else x=-1;
            }
            swap(x,y);
        }
    }
    
    void goup(int a,int p){
        while (a!=p){
            int b=spouse[a],c=Next[b];
            if (findb(c)!=p)Next[c]=b;
            if (mark[b]==2)mark[Q[bot++]=b]=1;
            if (mark[c]==2)mark[Q[bot++]=c]=1;
            together(a,b);
            together(b,c);
            a=c;
        }
    }
    
    void findaugment(int s){
        for (int i=0;i<n;i++)
            Next[i]=-1,belong[i]=i,mark[i]=0,visited[i]=-1;
        Q[0]=s;bot=1;mark[s]=1;
    
        for (int head=0;spouse[s]==-1 && head<bot;head++){
            int x=Q[head];
            for (int i=0;i<(int)E[x].size();i++){
                int y=E[x][i];
                if (spouse[x]!=y && findb(x)!=findb(y) && mark[y]!=2){
                    if (mark[y]==1){
                        int p=findLCA(x,y);
                        if (findb(x)!=p)Next[x]=y;
                        if (findb(y)!=p)Next[y]=x;
                        goup(x,p);
                        goup(y,p);
                    }else if (spouse[y]==-1){
                        Next[y]=x;
                        for (int j=y;j!=-1;){
                            int k=Next[j];
                            int l=spouse[k];
                            spouse[j]=k;spouse[k]=j;
                            j=l;
                        }
                        break;
                    }else{
                        Next[y]=x;
                        mark[Q[bot++]=spouse[y]]=1;
                        mark[y]=2;
                    }
                }
            }
        }
    }
    
    pair<int, int> id[N];
    int D[N];
    int xx[N], yy[N];
    int d[N];
    
    
    void build(int n1, int m)
    {
        for(int i = 0; i <= N; ++i)
            id[i].first = -1;
        n = 0;
        for(int i = 0; i < m; ++i)
        {
            int u = xx[i], v = yy[i];
            if(id[u].first == -1)
            {
                id[u] = make_pair(n, n + d[u] - D[u] - 1);
                n += (d[u] - D[u]);
            }
            if(id[v].first == -1)
            {
                id[v] = make_pair(n, n + d[v] - D[v] - 1);
                n += (d[v] - D[v]);
            }
            if(id[n1 + i + 1].first == -1)
            {
                id[n1 + i + 1] = make_pair(n, n + 1);
                n += 2;
            }
            int t = id[n1 + i + 1].first;
            E[t].push_back(t + 1);
            E[t + 1].push_back(t);
            for(int j = id[u].first; j <= id[u].second; ++j)
                E[t].push_back(j),E[j].push_back(t);
            for(int j = id[v].first; j <= id[v].second; ++j)
                E[t + 1].push_back(j),E[j].push_back(t + 1);
        }
    
    }
    
    int main()
    {
        int T, cas = 0;
        int n1, m;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d %d",&n1,&m);
            memset(d,0,sizeof(d));
            for(int i = 0; i < m; ++i)
            {
                scanf("%d %d",&xx[i],&yy[i]);
                ++d[xx[i]];
                ++d[yy[i]];
            }
            for(int i = 1; i <= n1; ++i)
                scanf("%d",&D[i]);;
            bool flag = true;
            for(int i = 1; i <= n1; ++i)
            {
                if(d[i] < D[i])
                {
                    flag = false;
                    break;
                }
            }
            printf("Case %d: ",++cas);
            if(!flag) {
                puts("NO");
                continue;
            }
            build(n1, m);
    
            for (int i=0;i<n;i++)spouse[i]=-1;
            for (int i=0;i<n;i++)
                if(spouse[i] == -1)
                    findaugment(i);
            int ans = 0;
            for(int i = 0; i < n; ++i)
                if(spouse[i] != -1)
                    ++ans;
            if(ans == n)
                puts("YES");
            else puts("NO");
    
            for(int i = 0; i < n; ++i)
                E[i].clear();
        }
        return 0;
    }


  • 相关阅读:
    可遇不可求的Question之DateTime.Ticks的单位篇(囧rz)
    可遇不可求的Question之SQLLite创建持久视图篇
    可遇不可求的Question之FusionCharts图表显示异常的解决办法
    可遇不可求的Question之安装的.NET Framework版本以及Service Pack
    可遇不可求的Question之不支持一个STA 线程上针对多个句柄的WaitAll
    可遇不可求的Question之Regex.Split解析乱码字符串异常篇
    Protocol Buffers proto语言语法说明
    [转]网页轻松绘制流程图:Diagramly
    笔记:代码整洁之道
    类之间的关系
  • 原文地址:https://www.cnblogs.com/nanke/p/2950913.html
Copyright © 2020-2023  润新知