牛客练习赛30: E. 国政议事(二分匹配)

版权声明:本文为博主原创文章,你们可以随便转载 https://blog.csdn.net/Jaihk662/article/details/83662683

链接:https://ac.nowcoder.com/acm/contest/216/E
来源:牛客网
 

题目描述

对于任何一个高速发展的发展中国家而言,一个高效的领导小组是不可或缺的。

现在我们知道k国的领导小组有n个人,准备举行一次会议,他们一共需要处理m个重要事项,第i个重要事项在ai手中,并且该重要事项需要交给bi来具体实施。

人都到齐后,他们会进行一个“交换意见”的环节,即每个人都会把自己手中一个自己认为关键的事项i的相关材料转发给该事项的具体实施者bi(如果该人手中没有重要事项,则不进行操作),随后,每个人都从自己收到的重要事项中选择一个自己认为关键的去实施,每实施一个事项,可以获得1点效率。

很显然,领导小组希望在这次会议中的效率更高,请你帮助他们决定在效率最高的情况下,哪些事项是必须执行的。

输入描述:

第一行两个正整数n(n<=500),m(m<=20000);

接下来m行,第i+1行两个正整数ai和bi表示重要事项i在ai手中,并且需要交给bi具体实施,可能存在ai=bi的情况

输出描述:

第一行一个正整数ans,num表示该会议的最高效率和必须执行的事项个数;

接下来num行,每行有一个正整数,表示在最高效率的情况下,必须执行的事项的标号,按照字典序从小到大输出。

输入

3 3
1 2
1 3
2 3

输出

2 2
1
3

一个人只能转手一次材料 → 二分图左边的每个点只能选一次

一个人只能实施一次计划 → 二分图右边的每个点只能选一次

一份计划经过转手+实施效率+1 → 选中一条边+1效率

所以这道题就是个二分匹配

将每个人拆成两个点,一个点表示转手(左),一个点表示实施(右), 对于第i个计划ai, bi,左边第ai个点向右边第bi个点连边

求出最大匹配就是第一问的答案,很简单

而第二问相当于是有多少条边你删掉它之后,最大匹配会-1,输出这些边

考虑直接暴力删除每一条边,每次求一遍最大匹配,复杂度O(nm²), 会超时

仔细分析一下可以发现不需要暴力每条边,只要先求一次二分匹配,然后暴力最大匹配的那些边即可,复杂度O(n²m)

除此之外二分匹配可以通过预处理增广路优化成O(nmsqrt(n)),加上其实跑不满,完全可过

#pragma comment(linker, "/STACK:102400000,102400000")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string>
#include<math.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
#define LL long long
#define mod 1000000007
vector<int> G[505], F[505], B;
int ban, n, m, dis, lx[505], ly[505], dx[505], dy[505], vis[505], q[20005];
int HKSech();
int Sech(int u);
int main(void)
{
	int u, v, i, Ans, p, ans, cnt;
	scanf("%d%d", &n, &m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d", &u, &v);
		G[u].push_back(v);
		F[u].push_back(i);
	}
	m = n;
	cnt = Ans = ban = 0;
	memset(lx, -1, sizeof(lx));
	memset(ly, -1, sizeof(ly));
	while(HKSech())
	{
		memset(vis, 0, sizeof(vis));
		for(i=1;i<=n;i++)
		{
			if(lx[i]==-1 && Sech(i))
				Ans++;
		}
	}
	for(i=1;i<=n;i++)
	{
		if(lx[i]!=-1)
			B.push_back(lx[i]);
	}
	for(p=0;p<B.size();p++)
	{
		ans = 0, ban = B[p];
		memset(lx, -1, sizeof(lx));
		memset(ly, -1, sizeof(ly));
		while(HKSech())
		{
			memset(vis, 0, sizeof(vis));
			for(i=1;i<=n;i++)
			{
				if(lx[i]==-1 && Sech(i))
					ans++;
			}
		}
		if(ans!=Ans)
			q[++cnt] = ban;
	}
	printf("%d %d\n", Ans, cnt);
	sort(q+1, q+cnt+1);
	for(i=1;i<=cnt;i++)
		printf("%d\n", q[i]);
	return 0;
}

int HKSech()
{
	int i, x, v;
	dis = 100000000;
	queue<int> q;
	memset(dx, -1, sizeof(dx));
	memset(dy, -1, sizeof(dy));
	for(i=1;i<=n;i++)
	{
		if(lx[i]==-1)
		{
			q.push(i);
			dx[i] = 0;
		}
	}
	while(q.empty()==0)
	{
		x = q.front();
		q.pop();
		if(dx[x]>dis)
			break;
		for(i=0;i<G[x].size();i++)
		{
			v = G[x][i];
			if(F[x][i]==ban)
				continue;
			if(dy[v]==-1)
			{
				dy[v] = dx[x]+1;
				if(ly[v]==-1)
					dis = dy[v];
				else
				{
					dx[ly[v]] = dy[v]+1;
					q.push(ly[v]);
				}
			}
		}
	}
	if(dis==100000000)
		return 0;
	return 1;
}
int Sech(int x)
{
	int i, v;
	for(i=0;i<G[x].size();i++)
	{
		v = G[x][i];
		if(F[x][i]==ban)
			continue;
		if(vis[v]==0 && dy[v]==dx[x]+1)
		{
			vis[v] = 1;
			if(ly[v]!=-1 && dy[v]==dis)
				continue;
			if(ly[v]==-1 || Sech(ly[v]))
			{
				ly[v] = x;
				lx[x] = F[x][i];
				return 1;
			}
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/83662683