题目描述
Bobo has a graph with n vertices and m edges where the i-th edge is between the vertices ai and bi. Find out whether is possible for him to choose some of the edges such that the i-th vertex is incident with exactly di edges.
题意:
问能否保留一些边,使得第i个点连接di边(也就是第i个点的度为d[i])
理论上二分图匹配是不可以的。应该用带花树才可以,但因为数据问题,都可以做?
如果建图呢?
左侧,将第i个点分成d[i]份,当然编号不相同
右侧根据题目输入的u,v连边,然后将左右两边对应的点相连
就比如数据:
4 4 1 2 1 0 1 2 1 4 2 3 3 4
题目要求的答案
最大匹配结果是:
为什么这样是对的呢?
如果存在完美匹配。也就是说每个点的每一度都有且仅有一条边与相应的端点相连。
左侧为拆点得到的点。右侧为拆边得到的边
左右侧之间的边说明相应该边对答案有贡献,应该保留的边,贡献为1
光右侧之间的边说明对答案并没有贡献,可以删去的
代码:
但是不知道代码哪里错了,。。。唉求大佬修改
#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 510; const int M = 3e5+10; struct node{ int to,nxt; }g[M]; int head[N],cnt; int vis[N],match[N],f[N],pre[N],Id,id[N]; //vis[i]: 0(未染色) 1(黑色) 2(白色) //match[i]: i的匹配点 //f[i]: i在带花树中的祖先 //pre[i]: i的非匹配边的另一点 //id: 找LCA用 int n,m,ans,u,v; queue<int> q; void Init() { Id=ans=cnt=0; for(int i=1;i<=n;i++) head[i]=-1,id[i]=match[i]=0; } void add(int u,int v){ g[cnt]=node{v,head[u]},head[u]=cnt++; } int getf(int x){ return f[x]==x?x:f[x]=getf(f[x]); } //查询x和y在带花树中的LCA int LCA(int x,int y) { //沿着增广路向上找lca for(++Id;;swap(x,y))//x,y交替向上 if(x) { x=getf(x);//有可能环中有环(花中有花),所以用并查集找祖先,只处理祖先节点 if(id[x]==Id) return x; //x,y在同一环中,一定会找到已被编号的点,该点即为LCA。 else id[x]=Id,x=pre[match[x]];//给点编号,并沿着非匹配边向上找 } } //缩点(开花 void blossom(int x,int y,int l)//,将x、y到LCA(l)路径中的点,缩为一点int l) { while(getf(x)!=l) { //增广路取反 pre[x]=y; y=match[x]; //如果x、y的奇环中有白点,将其染为黑点,放入队列,让其去找不是环中的匹配点 if(vis[y]==2) vis[y]=1,q.push(y); //只改变是根的点 if(getf(x)==x) f[x]=l; if(getf(y)==y) f[y]=l; //增广路取反 x=pre[y]; } } bool aug(int s) { //每次都以s为起点bfs,建带花树 for(int i=1;i<=n;i++) vis[i]=pre[i]=0,f[i]=i; while(!q.empty()) q.pop(); q.push(s),vis[s]=1; while(!q.empty()) { u=q.front();q.pop(); for(int i=head[u];~i;i=g[i].nxt) { v=g[i].to; //如果已经在同一个环(花)中或者是白点(意为这已经有匹配点),只接跳过 //这种情况不会增加匹配数 if(getf(u)==getf(v)||vis[v]==2) continue; //如果没有被染色 if(!vis[v]) { //先染为白色,将前继点指向u vis[v]=2; pre[v]=u; //如果没有被匹配过,直接匹配成功 if(!match[v]) { //增广路取反 for(int x=v,last;x;x=last) last=match[pre[x]],match[x]=pre[x],match[pre[x]]=x; return 1; } //如果被匹配过,则把匹配v的点染为黑色,放入队列中 vis[match[v]]=1; q.push(match[v]); } //v是黑色,形成奇环,则缩点(开花)。 else { int lca=LCA(u,v); blossom(u,v,lca); blossom(v,u,lca); } } } return 0; } vector <int> de[N]; int main(void) { while(~scanf("%d%d",&n,&m)) { Init(); int sum=0; for(int i=1;i<=n;i++) { int qqq; cin>>qqq; de[i].clear(); while(qqq--)de[i].push_back(++sum); } n=sum+2*m;//所有的点数 for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); //用sum+1来代替u add(sum+1,sum+2); add(sum+2,sum+1); for(auto x:de[u]) { add(x,sum+1); add(sum+1,x); } for(auto x:de[v]) { add(x,sum+2); add(sum+2,x); } sum+=2; } for(int i=1;i<=n;i++) if(!match[i]&&aug(i)) ans++; if(ans*2==n) printf("Yes\n"); else printf("No\n"); } return 0; }