HDU3551 一般图最大匹配

hdu 3551 hard problem
题意: 给出一个无向图,存在重边,没有自环。问能否删除一些边,使得每个顶点的度数为,
指定度数deg[i]?
解题思路: 我们应该删除哪些边呢? 预处理每个顶点的度数d[i], 若d[i] = deg[i], 那么
与这个点相连的边是不能删掉的。原因很显然。若i与j之间有边,并且d[i]>deg[i],
d[j]>deg[j]那么这条边是可以删除的。接下来如何建图呢? 将每个点i 拆成
d[i] - deg[i]个点。如果i与j之间的边e可以删除, 则边e与i、j拆出的每个点连一条边
ei, ej(重边连多次)。然后求该一般图最大匹配,若存在完美匹配,则YES。


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 800;
int n,head,tail,start,finish,match[MAXN],fa[MAXN],base[MAXN],Q[MAXN];//match初始为0 n个点的图 从1开始
int adj[MAXN][MAXN];   //邻接矩阵
bool mark[MAXN],in_blossom[MAXN],in_queue[MAXN];
inline void Contract (int x,int y){
		memset(mark,0,sizeof(mark));
		memset(in_blossom,0,sizeof(in_blossom));
#define pre fa[match[i]]
		int lca,i;
		for(i = x;i;i = pre){
				i = base[i];
				mark[i] = 1;
		}
		for(i = y;i; i = pre){
				i = base[i];
				if(mark[i]){
						lca = i;
						break;
				}
		}
		for (i = x; base[i] != lca; i = pre){
				if(base[pre] != lca) fa[pre] = match[i];
				in_blossom[base[i]] = 1;
				in_blossom[base[match[i]]] = 1;
		}
		for (i = y; base[i] != lca; i = pre){
				if (base[pre] != lca) fa[pre] = match[i];
				in_blossom[base[i]] = 1;
				in_blossom[base[match[i]]] = 1;
		}
#undef pre
		if (base[x] != lca) fa[x] = y;
		if (base[y] != lca) fa[y] = x;
		for (i = 1; i <= n; ++i){
				if (in_blossom[base[i]]){
						base[i] = lca;
						if (!in_queue[i]){
								Q[++tail] = i;
								in_queue[i] = 1;
						}
				}
		}
}
inline void Change(){
		int x,y,z;
		z = finish;
		while (z){
				y = fa[z];
				x = match[y];
				match[y] = z;
				match[z] = y;
				z = x;
		}
}
inline void FindAugmentPath(){
		memset(fa,0,sizeof(fa));
		memset(in_queue,0,sizeof(in_queue));
		for(int i = 1; i <= n; ++i)base[i] = i;
		head = 0; tail = 1;
		Q[1] = start;
		in_queue[start] = 1;
		while (head != tail){
				int x = Q[++head];
				for (int y = 1; y <= n; ++y){
						if (adj[x][y] && base[x] != base[y] && match[x] != y)
								if (start == y || match[y] && fa[match[y]])
										Contract(x,y);
								else if(!fa[y]){
										fa[y] = x;
										if(match[y]){
												Q[++tail] = match[y];
												in_queue[match[y]] = 1;
										}
										else {
												finish = y;
												Change();
												return;
										}
								}
				}
		}
}
inline void Edmonds(){
		memset(match,0,sizeof(match));
		for (start = 1; start <= n; ++start)
				if (match[start] == 0)
						FindAugmentPath();
}
inline void init(){
	memset(adj,0,sizeof(adj));
}
int deg[MAXN], D[MAXN], M;
pair<int ,int> edge[MAXN], id[MAXN];
int main(){
		int Case, u, v,V;
		scanf("%d",&Case);
		for(int it = 1;it <= Case; ++it){
				scanf("%d%d",&V,&M);
				memset(deg, 0, sizeof(deg));
				memset(adj, 0, sizeof(adj));
				for(int i = 0;i < M; ++i){
						scanf("%d%d",&u,&v);
						u --; v --;
						edge[i] = make_pair(u, v);
						deg[u] ++; deg[v] ++;
				}
				for(int i = 0;i < V; ++i){
						scanf("%d",&D[i]);
				}
				bool flag = true;
				int cnt = 1;
				for(int i = 0;i < MAXN; ++i) id[i] = make_pair(-1, -1);
				for(int i = 0;i < V; ++i)
						if(deg[i] < D[i]) { flag = false; break; }
				printf("Case %d: ",it);
				if(!flag) {
						puts("NO");
						continue;
				}
				for(int i = 0;i < M; ++i){
						u = edge[i].first;
						v = edge[i].second;
						if(id[u].first == -1){
								id[u] = make_pair(cnt, cnt + deg[u] - D[u] - 1);
								cnt += (deg[u] - D[u]);
						}
						if(id[v].first == -1){
								id[v] = make_pair(cnt, cnt + deg[v] - D[v] - 1);
								cnt += (deg[v] - D[v]);
						}
						if(id[V+i].first == -1){
								id[V+i] = make_pair(cnt, cnt + 1);
								cnt += 2;
						}
						int t = id[V+i].first;
						adj[t][t+1] = adj[t+1][t] = true;
						for(int j = id[u].first;j <= id[u].second; ++j)
								adj[t][j] = adj[j][t] = true;
						for(int j = id[v].first;j <= id[v].second; ++j)
								adj[t+1][j] = adj[j][t+1] = true;
				}
				int j, sum = 0;
				n  = cnt-1;
				flag = 1;
				Edmonds();
				for(int i = 1; i <= n; ++i){
						if(!match[i]){
								flag = 0;
								break;
						}
				}
				if(flag) puts("YES");
				else puts("NO");
		}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值