1. 程式人生 > >hdu 1151 最小覆蓋路徑演算法證明

hdu 1151 最小覆蓋路徑演算法證明

又是二分圖。若還不知道匈牙利演算法,看我前面的文章

先把每個點拆成兩個,一個表示出,一個表示入,根據資料輸入,對應的出點和對應入點之間構造了一條邊。這樣就有了一個二分圖。

有向圖的最小路徑覆蓋 = 總的點數(為拆分前的) - 最大匹配數。

證明如下:

先假設所有點我們都派出一個傘兵。然後每增加一條匹配,就代表我們從一個點出走向一個點。並且根據最大匹配的定義,我們可以直接看作是將這兩個點合併到一個路徑集合中,這個時候我們就可以減少一個傘兵。 所以 路徑覆蓋 = 總的點數 - 最大匹配數 。

然而這樣我們並沒有證明 是 “最小”覆蓋。 接下來證明最小。 假設 不是最小,那麼也就意味著還可以減少傘兵,而減少傘兵就必須合併某兩個點(即匹配),而匹配已經最大,所以矛盾。

即 需要派遣的傘兵數 = 總點數 - 匹配數。    最少傘兵派遣 = 總點數 - 最大匹配數。

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <string.h>
#define base 10
using namespace std;
bool vis[130],map[130][130];
int match[130],n,m;
bool find(int p)
{
	int i,j;
	for(i = 1; i <= m; i++)
	{
		if(!vis[i] && map[p][i])
		{
			vis[i] = 1;
			if( !match[i] || find(match[i])) 
			{
				match[i] = p;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
//	freopen("t.txt","r",stdin);
	int k,i,j,count,t,a,b;	
	scanf("%d",&t);
	while(t--)
	{
		memset(map,0,sizeof(map));
		memset(match,0,sizeof(match));
		count = 0;
		scanf("%d%d",&m,&n);//m 點, n 邊 
		for(i = 0; i < n; i++)
		{
			scanf("%d%d",&a,&b);
			map[a][b] = 1;
		}
		for(i = 1; i <= m; i++)
		{
			memset(vis,0,sizeof(vis));
			if(find(i)) count++;
		}
		printf("%d\n",m-count);
	}
}