zoukankan      html  css  js  c++  java
  • HDU 3551 Hard Problem

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

    题意:给出一个图,还有一个子图的度数,求有没有办法通过删边使得原图的度数变成那个子图的度数?

    思路:我们考虑把每个点拆成du[i]-d[i]个点,代表要删去的度数,然后对于每条边,我们建立两个点eu,ev,eu与ev连边,如果这条边连接了i,j两个点,那么所有的i的点向eu连边,所有的j向ev连边,如果有完美匹配(就是所有点都有匹配)那么有解。

    至于为什么:如果eu和ev是匹配边,代表这条边不删,因为这条边的两侧,也就是连接的两个点都有其他的匹配了,那么这条边就不用删。

    如果这条边不是匹配边,那么说明i和eu匹配了,j和ev匹配了,这条边代表删掉了,而i和j的度数也-1了

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    struct edge{
        int u,v;
    }e[2005];
    int n,m,p[2005],q[20005];
    int match[1005],newbase;
    int inqueue[1005],inpath[1005],G[1005][1005],inblossom[1005],father[1005];
    int du[1005],d[1005],c[500005],base[1005],start,finish,head,tail;
    int read(){
        int t=0,f=1;char ch=getchar();
        while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
        while ('0'<=ch&&ch<='9'){t=t*10+ch-'0';ch=getchar();}
        return t*f;
    }
    int lca(int u,int v){
        memset(inpath,0,sizeof inpath);
        while (1){
            u=base[u];
            inpath[u]=1;
            if (!match[u]) break;
            u=father[match[u]];
        }
        while (1){
            v=base[v];
            if (inpath[v]) break;
            v=father[match[v]];
        }
        return v;
    }
    void reset(int u){
        while (u!=newbase){
            int v=match[u];
            inblossom[base[v]]=inblossom[base[u]]=1;
            u=father[v];
            if (base[u]!=newbase) father[u]=v;
        }
    }
    void blossomcontract(int u,int v){
        newbase=lca(u,v);
        memset(inblossom,0,sizeof inblossom);
        reset(u);
        reset(v);
        if (base[u]!=newbase) father[u]=v;
        if (base[v]!=newbase) father[v]=u;
        for (int i=1;i<=n;i++) 
         if (inblossom[base[i]]){
                base[i]=newbase;
                if (!inqueue[i]) c[++tail]=i,inqueue[i]=1;
         }
    }
    void findaugmentingpath(){
        memset(inqueue,0,sizeof inqueue);
        memset(father,0,sizeof father);
        for (int i=1;i<=n;i++) base[i]=i;
        head=1;tail=1;c[1]=start;inqueue[start]=1;
        finish=0;
        while (head<=tail){
            int u=c[head++];
            for (int v=1;v<=n;v++)
             if (G[u][v]&&base[u]!=base[v]&&match[v]!=u){
                    if (v==start||(match[v]>0)&&(father[match[v]]>0)){
                        blossomcontract(u,v);
                    }else
                    if (father[v]==0){
                        father[v]=u;
                        if (match[v]){
                            c[++tail]=match[v];inqueue[match[v]]=1;
                        }else{
                            finish=v;
                            return;
                        }
                    }
             }
        }
    }
    void augmentpath(){
        int u,v,w;
        u=finish;
        while (u>0){
            v=father[u];
            w=match[v];
            match[u]=v;
            match[v]=u;
            u=w;
        }
    }
    bool solve(){
        int res=0;
        memset(match,0,sizeof match);
        for (int i=1;i<=n;i++)
         if (!match[i]){
            start=i;
            findaugmentingpath();
            if (finish) augmentpath(),res++;
         }
        for (int i=1;i<=n;i++)
         if (!match[i]) return 0;
        return 1; 
    }
    bool build(){
        memset(G,0,sizeof G);
        int cnt=0;
        for (int i=1;i<=n;i++)
         if (du[i]>d[i]) return 0;
        memset(p,0,sizeof p); 
        for (int i=1;i<=m;i++){
            if (!p[e[i].u]){
                p[e[i].u]=++cnt;
                q[e[i].u]=cnt+d[e[i].u]-du[e[i].u]-1;
                cnt=cnt+d[e[i].u]-du[e[i].u]-1;
            }
            if (!p[e[i].v]){
                p[e[i].v]=++cnt;
                q[e[i].v]=cnt+d[e[i].v]-du[e[i].v]-1;
                cnt=cnt+d[e[i].v]-du[e[i].v]-1;
            }
            int k=cnt+2;cnt+=2;
            G[k][k-1]=G[k-1][k]=1;
            for (int j=p[e[i].u];j<=q[e[i].u];j++) G[j][k-1]=G[k-1][j]=1;
            for (int j=p[e[i].v];j<=q[e[i].v];j++) G[j][k]=G[k][j]=1;
        }
        n=cnt;
        return 1;
    }
    int main(){
        int Tcase=0;
        int T=read();
        while (T--){
            n=read();m=read();
            printf("Case %d: ",++Tcase);
            memset(d,0,sizeof d);
            for (int i=1;i<=m;i++){
                e[i].u=read();e[i].v=read();
                d[e[i].u]++;d[e[i].v]++;
            }
            for (int i=1;i<=n;i++) du[i]=read();
            if (build()&&solve()){
                printf("YES
    ");
            }else{
                printf("NO
    ");
            }
        }
        return 0;
    }
  • 相关阅读:
    Linux内核中的信号机制--一个简单的例子【转】
    国际C语言混乱代码大赛代码赏析(一)【转】
    宏内核与微内核【转】
    Linux内核USB驱动【转】
    USB驱动开发大全【转】
    Linux驱动程序学习【转】
    GPIO口及中断API函数【转】
    Linux的fasync驱动异步通知详解【转】
    request_irq() | 注册中断服务函数【转】
    混杂设备动态次设备号分析【转】
  • 原文地址:https://www.cnblogs.com/qzqzgfy/p/5683813.html
Copyright © 2011-2022 走看看