链接: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;
}
解决图论问题:通过删边实现指定度数匹配

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

被折叠的 条评论
为什么被折叠?



