2020牛客暑期多校训练营(第一场)I 1or 2题解
题意:
给定一个无向图,问能否从中选出一些边构成新图,使得每个点的度数为di,1<=di<=2,可以输出Yes,否则输出No。n<=50,m<=100 100组数据
当时第一反应是网络流,发现不可行之后尝试分析性质,发现最终的图一定是由一些环和一些链构成,尝试搜索+剪枝无果,后来想把这道题转化为01方程是否有解的判断,发现不会……
我们不妨先把问题简化,如果所有点di都为1,那么这道题就是问这张图上的最大匹配是多少,直接套一般图上的完美匹配板子即可。
那么我们可以尝试将di=2的点拆开,让他们各自找一个点匹配。那么就会有这样一个问题,由一个点拆开的两个点可能正好和另外两个由一个点拆开的点相匹配。所以我们不能将原边照搬,应当加以限制。
我们可以将一条边 e 拆成两个点e e',e和这条边的端点x ( x' ) 连边,e' 和y ( y' ) ,e e'之间连边。这时,如果e与e'匹配,说明我们不选择这条边,否则说明我们选择了这条边,(似乎有点网络流最小割的意思)
之后我们在这张图上跑一般图上的完美匹配,看匹配数是否为点数除2即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<queue> 8 #define N 306 9 #define M 4005 10 using namespace std; 11 int n,m,D[N]; 12 int zz,a[N]; 13 struct ro{ 14 int to,next; 15 }road[M]; 16 void build(int x,int y) 17 { 18 zz++; 19 road[zz].to=y; 20 road[zz].next=a[x]; 21 a[x]=zz; 22 } 23 int fa[N],bj[N],pre[N],to[N],cnt2; 24 queue<int> q1; 25 int find(int x) 26 { 27 if(fa[x]==x)return x; 28 return fa[x]=find(fa[x]); 29 } 30 int cnt,dfn[N]; 31 int get_lca(int x,int y) 32 { 33 cnt++; 34 x=find(x),y=find(y); 35 while(dfn[x]!=cnt) 36 { 37 dfn[x]=cnt; 38 x=find(pre[to[x]]); 39 if(y) swap(x,y); 40 } 41 return x; 42 } 43 void work2(int x,int y,int z) 44 { 45 while(find(x)!=z) 46 { 47 pre[x]=y,y=to[x]; 48 if(bj[y]==2) bj[y]=1,q1.push(y); 49 if(find(x)==x) fa[x]=z; 50 if(find(y)==y) fa[y]=z; 51 x=pre[y]; 52 } 53 } 54 bool bfs(int S) 55 { 56 for(int i=1;i<=n*2+m*2;i++) 57 { 58 fa[i]=i,bj[i]=pre[i]=0; 59 } 60 while(!q1.empty())q1.pop(); 61 q1.push(S);bj[S]=1; 62 while(!q1.empty()) 63 { 64 int x=q1.front();q1.pop(); 65 for(int i=a[x];i;i=road[i].next) 66 { 67 int y=road[i].to; 68 if(find(y)==find(x)||bj[y]==2)continue; 69 if(!bj[y]) 70 { 71 bj[y]=2,pre[y]=x; 72 if(!to[y]) 73 { 74 for(int now=y,la;now!=0;now=la) 75 { 76 la=to[pre[now]],to[now]=pre[now],to[pre[now]]=now; 77 } 78 return 1; 79 } 80 bj[to[y]]=1;q1.push(to[y]); 81 } 82 else 83 { 84 int lca=get_lca(x,y); 85 work2(x,y,lca); 86 work2(y,x,lca); 87 } 88 } 89 } 90 return 0; 91 } 92 bool work() 93 { 94 memset(dfn,0,sizeof(dfn)); 95 cnt=0; 96 memset(to,0,sizeof(to)); 97 for(int i=1;i<=n*2+m*2;i++) 98 { 99 if(!to[i]) bfs(i); 100 } 101 int ans=0; 102 for(int i=1;i<=n*2+m*2;i++) if(to[i]) ans++; 103 return ans==cnt2; 104 } 105 int deg[N]; 106 int main() 107 { 108 while(~scanf("%d%d",&n,&m)) 109 { 110 zz=0; 111 memset(a,0,sizeof(a)); 112 cnt2=n; 113 for(int i=1;i<=n;i++) 114 { 115 scanf("%d",&D[i]); 116 if(D[i]==2)cnt2++; 117 } 118 memset(deg,0,sizeof(deg)); 119 cnt2+=m*2; 120 for(int i=1;i<=m;i++) 121 { 122 int x,y; 123 scanf("%d%d",&x,&y); 124 deg[x]++,deg[y]++; 125 if(D[x]==2) 126 { 127 build(x,2*n+i); 128 build(x+n,2*n+i); 129 build(2*n+i,x); 130 build(2*n+i,x+n); 131 } 132 else 133 { 134 build(x,2*n+i); 135 build(2*n+i,x); 136 } 137 if(D[y]==2) 138 { 139 build(2*n+m+i,y); 140 build(2*n+m+i,y+n); 141 build(y,2*n+m+i); 142 build(y+n,2*n+m+i); 143 } 144 else 145 { 146 build(y,2*n+m+i); 147 build(2*n+m+i,y); 148 } 149 build(2*n+i,2*n+m+i); 150 build(2*n+m+i,2*n+i); 151 } 152 bool op=1; 153 for(int i=1;i<=n;i++) 154 { 155 if(deg[i]<D[i]) 156 { 157 op=0; 158 } 159 } 160 if(!op) 161 { 162 printf("No\n"); 163 continue; 164 } 165 if(work())printf("Yes\n"); 166 else printf("No\n"); 167 } 168 return 0; 169 } 170 /* 171 2 2 172 1 2 1 173 1 2 2 174 3 175 0 2 176 3 3 177 1 4 178 */