一般图最大匹配

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

下面的模板是来自这个博客的 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;
}

 



posted @ 2013-03-08 22:57  枕边梦  阅读(2453)  评论(0编辑  收藏  举报