HDU 3551 Hard Problem 一般图的最大匹配(带花树)

来源:互联网 发布:python 爬虫 伯乐在线 编辑:程序博客网 时间:2024/06/05 20:31

题目大意:给你一个图,让你取一个包含所有点和其中一些边的子图,使子图中每一点的度为给定的值。


建图很神。

把每条边(u,v)拆成两个点,x , y,并在 x,y间连一条边;

如果题目要求u点的度数为f[u],那么就把u拆成f[u]个点,每个点与x相连,

同样的,把v点拆成f[v]个点,每个点与y相连,

然后用带花树求最大匹配,如果达到完美匹配,就存在,否则不存在。


至于原理,

在最终的匹配方案中,如果达到完美匹配,

则每个点的每个度都匹且唯一匹配着一条边的一头,

如果想达到完美匹配,这条边的另一头必定匹配着另一个点的一个度,

这样一条边的匹配是合乎要求的。

而又由于完美匹配,每个度都被匹配上了,因此就满足题意了。

至于被删掉的边,

在最终的匹配方案中 ,他被拆成的两个点间进行匹配,

表示没与任意一个度相关连。


#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <cstdlib>#include <queue>#include <stack>using namespace std;#define REP(i,n)   for(int i=0;i<(n);++i)#define FOR(i,l,r) for(int i=(l);i<=(r);++i)#define DSC(i,r,l) for(int i=(r);i>=(l);--i)#define N 1010#define SET(a,b) memset(a,b,sizeof(a))deque<int> Q;//g[i][j]存放关系图:i,j是否有边,match[i]存放i所匹配的点//建图开始初始化g//最终匹配方案为match//复杂度O(n^3)//点是从1到n的bool g[N][N],inque[N],inblossom[N];int match[N],pre[N],base[N];//找公共祖先int findancestor(int u,int v){    bool inpath[N]= {false};    while(1)    {        u=base[u];        inpath[u]=true;        if(match[u]==-1)break;        u=pre[match[u]];    }    while(1)    {        v=base[v];        if(inpath[v])return v;        v=pre[match[v]];    }}//压缩花void reset(int u,int anc){    while(u!=anc)    {        int v=match[u];        inblossom[base[u]]=1;        inblossom[base[v]]=1;        v=pre[v];        if(base[v]!=anc)pre[v]=match[u];        u=v;    }}void contract(int u,int v,int n){    int anc=findancestor(u,v);    SET(inblossom,0);    reset(u,anc);    reset(v,anc);    if(base[u]!=anc)pre[u]=v;    if(base[v]!=anc)pre[v]=u;    for(int i=1; i<=n; i++)        if(inblossom[base[i]])        {            base[i]=anc;            if(!inque[i])            {                Q.push_back(i);                inque[i]=1;            }        }}bool dfs(int S,int n){    for(int i=0; i<=n; i++)pre[i]=-1,inque[i]=0,base[i]=i;    Q.clear();    Q.push_back(S);    inque[S]=1;    while(!Q.empty())    {        int u=Q.front();        Q.pop_front();        for(int v=1; v<=n; v++)        {            if(g[u][v]&&base[v]!=base[u]&&match[u]!=v)            {                if(v==S||(match[v]!=-1&&pre[match[v]]!=-1))contract(u,v,n);                else if(pre[v]==-1)                {                    pre[v]=u;                    if(match[v]!=-1)Q.push_back(match[v]),inque[match[v]]=1;                    else                    {                        u=v;                        while(u!=-1)                        {                            v=pre[u];                            int w=match[v];                            match[u]=v;                            match[v]=u;                            u=w;                        }                        return true;                    }                }            }        }    }    return false;}int solve(int n){    SET(match,-1);    int ans=0;    for(int i=1; i<=n; i++)        if(match[i]==-1&&dfs(i,n))            ans++;    return ans;}//往上是带花树模板,不用看int f[60];int x[210],y[210];int map[55][210];bool build(int n,int m){    int num=0;    FOR(i,1,n)        FOR(j,1,f[i])            map[i][j]=++num;//给i点的第j个度一个标号,方便接下来的建图    REP(i,m)    {        FOR(j,1,f[ x[i] ]) g[ map[ x[i] ][j] ][num+1]=g[num+1][ map[ x[i] ][j] ]=1;        FOR(j,1,f[ y[i] ]) g[ map[ y[i] ][j] ][num+2]=g[num+2][ map[ y[i] ][j] ]=1;        g[num+1][num+2]=g[num+2][num+1]=1;        num+=2;    }//把边被拆成的两个点分别与两头的每个度相连    if(solve(num)*2==num) return 1;    return 0;}int main(){     //freopen("in","r",stdin);    int cas,cas1=1,n,m;    cin>>cas;    while(cin>>n>>m)    {        memset(g,0,sizeof(g));        REP(i,m)    scanf("%d%d",&x[i],&y[i]);        FOR(i,1,n) scanf("%d",&f[i]);        printf("Case %d: ",cas1++);        if(build(n,m)) puts("YES");        else puts("NO");    }    return 0;}