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\n"); }else{ printf("NO\n"); } } return 0; }