2020牛客暑期多校训练营(第一场)I.1 or 2

2020牛客暑期多校训练营(第一场)I.1 or 2

题目链接:https://ac.nowcoder.com/acm/contest/5666/I

题意:给你一个n个点m条边的无向图,通过删除一些边使每个点的度数满足di,若存在则输出Yes,否则输出No。

通过拆点来跑一般图的最大匹配,将度数为2的点进行拆点,拆完的点连向对应的x,y点。如果达到完美匹配,这条边的另一头必定匹配着另一个点的一个度,表示拆点原点相连,这样一条边的匹配是合乎要求的。

思路来源:https://www.cnblogs.com/xiongtao/p/11189452.html

#include<bits/stdc++.h>
using namespace std;
#define ll long long
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();
            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++){
        for(int j=1;j<=f[x[i]];j++)
            g[mp[x[i]][j]][n+1]=g[n+1][mp[x[i]][j]]=1;
        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;
        n+=2;
    }
}
void print(){
    ans=0;
    for(int i=1;i<=n;i++)
        if(match[i]!=0)
            ans++;
    if(ans==n) printf("Yes\n");
    else printf("No\n");
}
int main(){
    int t,k=0;
    while(~scanf("%d%d",&np,&ne)){
    	for(int i=1;i<=np;i++)
            scanf("%d",&f[i]);
        for(int i=0;i<ne;i++)
            scanf("%d%d",&x[i],&y[i]);
        create();
        edmonds();
        print();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值