hdu3551 Hard Problem 【一般图匹配】

解决图论问题:通过删边实现指定度数匹配
本文探讨了一种方法,通过在无向图中删除边来调整节点度数,以达到指定的目标度数。利用带花树的概念简化问题,并通过构建中间边间接连接节点群组,实现高效度数匹配。该方法适用于解决图论中的复杂配对问题。

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3551

题意:给定一个无向图,(可能重边)问能否通过删边,得到每个点的度数(d)为给定的度数(D)。

分析:我们先不管边上的两点是哪个,只考虑这两点的度数,现在删掉某条边,这边上的两点的度数都不为零那么就可以删,

这样我们可以把这两个“度”匹配,如果所有的匹配可,那么所有多余的都可以删掉了。我们这样将所有的点拆成它度数个点,

这样我们就可以用带花树开花取求。建边的时候不需要把两边上的所有点建边,这样会有很多边重复,我们可以创建一个中间边,

使得这两坨点间接相邻。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define Mn 810
#define Mm 200005
#define mod 1000000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#define CPY(a,b) memcpy ((a), (b), sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
struct edge {
    int v,next;
}e[Mm];
int tot,head[Mn];
void addedge(int u,int v) {
    e[tot].v=v;
    e[tot].next=head[u];
    head[u]=tot++;
}
int cnt,pre[Mn];
int findpre(int x) {
    return x==pre[x]?pre[x]:pre[x]=findpre(pre[pre[x]]);
}
void ol(int a,int b) {
    a=findpre(a);
    b=findpre(b);
    if(a!=b) pre[a]=b;
}
int lk[Mn],vis[Mn],mark[Mn],match[Mn],ne[Mn];
int lca(int x,int y) {
    static int t=0;t++;
    while(1) {
        if(x!=-1) {
            x=findpre(x);
            if(vis[x]==t) return x;
            vis[x]=t;
            if(match[x]!=-1) x=ne[match[x]];
            else x=-1;
        }
        swap(x,y);
    }

}
queue<int> q;
void group(int a,int p) {
    while(a!=p) {
        int b=match[a],c=ne[b];
        if(findpre(c)!=p) ne[c]=b;
        if(mark[b]==2) mark[b]=1,q.push(b);
        if(mark[c]==2) mark[c]=1,q.push(c);
        ol(a,b),ol(b,c);
        a=c;
    }
}
void aug(int s) {
    for(int i=0;i<=cnt;i++) {
        ne[i]=-1;
        pre[i]=i;
        mark[i]=0;
        vis[i]=-1;
    }
    mark[s]=1;
    while(!q.empty()) q.pop();
    q.push(s);
    while((!q.empty())&&match[s]==-1) {
        int u=q.front();
        q.pop();
        for(int i=head[u];~i;i=e[i].next) {
            int v=e[i].v;
            if(match[u]==v||findpre(u)==findpre(v)||mark[v]==2) continue;
            if(mark[v]==1) {
                int r=lca(u,v);
                if(findpre(u)!=r) ne[u]=v;
                if(findpre(v)!=r) ne[v]=u;
                group(u,r);
                group(v,r);
            } else if(match[v]==-1) {
                ne[v]=u;
                for(int x=v;~x;) {
                    int y=ne[x];
                    int mv=match[y];
                    match[x]=y,match[y]=x;
                    x=mv;
                }
                break;
            } else {
                ne[v]=u;
                q.push(match[v]);
                mark[match[v]]=1;
                mark[v]=2;
            }
        }
    }
}
int degree[Mn];
int d[Mn];
int U[Mn],V[Mn];
int id[Mn],num[Mn];
void init() {
    tot=0;cnt=1;
    CLR(head,-1);
    CLR(id,0);
    CLR(degree,0);
}
void build(int n,int m) {
    for(int i=1;i<=m;i++) {
        int u=U[i],v=V[i];
        if(!id[u]) {
            id[u]=cnt;
            num[u]=cnt+d[u]-1;
            cnt+=d[u];
        }
        if(!id[v]) {
            id[v]=cnt;
            num[v]=cnt+d[v]-1;
            cnt+=d[v];
        }
        if(!id[n+i+1]) {
            id[n+i+1]=cnt;
            num[n+i+1]=cnt+1;
            cnt+=2;
        }
        int t=id[n+i+1];
        addedge(t,t+1);
        addedge(t+1,t);
        for(int j=id[u];j<=num[u];j++)
            addedge(t,j),addedge(j,t);
        for(int j=id[v];j<=num[v];j++)
            addedge(t+1,j),addedge(j,t+1);
    }
}
int main() {
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++) {
        int n,m;init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++) {
            scanf("%d%d",&U[i],&V[i]);
            degree[U[i]]++;
            degree[V[i]]++;
        }
        int flag=1;
        for(int i=1;i<=n;i++) {
            scanf("%d",&d[i]);
            if(degree[i]<d[i]) flag=0;
            else d[i]=degree[i]-d[i];
        }
        printf("Case %d: ",cas);
        if(!flag) {
            printf("NO\n");
            continue;
        }
        build(n,m);
        int ans=0;
        cnt--;
        //cout<<cnt<<endl;
        for(int i=1;i<=cnt;i++) {
            match[i]=-1;
        }
        for(int i=1;i<=cnt;i++) {
            if(match[i]==-1) aug(i);
        }
        for(int i=1;i<=cnt;i++) {
            if(match[i]!=-1) ans++;
        }
        if(ans==cnt) {
            printf("YES\n");
        } else {
            printf("NO\n");
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值