POJ - 1422 Air Raid (最小路徑覆蓋 = 總點數 - 最大匹配)

題目:http://poj.org/problem?id=1422

題意:

n*n的方陣,存在若干條有向邊相連,選擇若干個點組成的路爲一條參觀路徑,所有的路徑都不存在相同點,選擇最少路徑,使得這些路徑包括所有節點,即最小路徑覆蓋問題

分析:

在有向無環圖中,假設沒有邊相連,則所有節點都是一條長度爲0的路勁,此時最小路徑覆蓋數爲總結點數

若此時多出一條邊1-2,則兩個節點1、2爲同一條路勁,此時最小路徑覆蓋數-1

若再多一條1-3,則最小路徑覆蓋數不變,因爲起點與上一次相同,即兩條邊不是匹配邊,只能選1-2或1-3

若多的第二條爲2-3,則與第一條路1-2爲同一條路勁,此時最小路徑覆蓋數-1

......

綜上,

最小路勁覆蓋數 == 總節點數 - 最大匹配數

代碼:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <map>

using namespace std;

#define MAX 200
#define MIN -1e5
#define INF 0x7f7f7f7f

int t, n, m;

int x[MAX], y[MAX]; // 行、列點集合
int Edge[MAX][MAX]; // 有向無環圖
int vis[MAX]; // path遞歸防止死循環的訪問數組

int path(int u) // 設置 或 更新最大匹配的邊
{
	for(int v = 1; v<=n; v++) // 便利所有y(列)集合 節點
	{
		if(vis[v] == 0 && Edge[u][v] == 1) // 遞歸中未訪問 && 該列節點v與行節點u有變相連(有方向 u->v)
		{
			vis[v] = 1;
			if(y[v] == -1 || path(y[v])) // 該列節點v沒有被行節點u匹配過 || 已經被行節點y【v】匹配過,嘗試是否能夠更新該匹配
			{
				x[u] = v;
				y[v] = u;
				return 1;
			}
		}
	}
	return 0;
}

void solve()
{
	int ans = 0;
	memset(x, -1, sizeof(x));
	memset(y, -1, sizeof(y));
	for(int u = 1; u<=n; u++) // 遍歷所有x行節點
	{
		if(x[u] == -1) // 未被匹配過
		{
			memset(vis, 0, sizeof(vis));
			ans += path(u); // 匹配成功, 最大匹配數+1
		}
	}
	printf("%d\n", n - ans); // 最小路徑覆蓋 == 總節點數 - 最大匹配數
}

int main()
{
	int i, j;
	//freopen("a.txt", "r", stdin);

	scanf("%d", &t);
	while(t--)
	{
		scanf("%d%d", &n, &m);
		int u, v;
		memset(Edge, 0, sizeof(Edge));
		for(i = 0; i<m; i++)
		{
			scanf("%d%d", &u, &v);
			Edge[u][v] = 1;
		}
		solve();
	}
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章