1. 程式人生 > >牛客練習賽30: E. 國政議事(二分匹配)

牛客練習賽30: 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;
}