1. 程式人生 > >1340 最小路徑覆蓋 最大匹配

1340 最小路徑覆蓋 最大匹配

題目

定義: 一個不含圈的有向圖G中,G的一個路徑覆蓋是一個其結點不相交的路徑集合P,圖中的每一個結點僅包含於P中的某一條路徑。路徑可以從任意結點開始和結束,且長度也為任意值,包括0。請你求任意一個不含圈的有向圖G的最小路徑覆蓋數。

提示:最小路徑覆蓋數=G的定點數-最小路徑覆蓋中的邊數
最小路徑覆蓋數=原圖G的頂點數-二分圖的最大匹配數

題解

把原圖的每個點拆成兩個點,然後從有向邊的起點向終點連線,其中起點全部放在二分圖的同一邊。
這時可以把n個點看成獨立的一個路徑,然後選擇一個匹配就是把兩個路徑連起來。可以看出同一個起點不能連出兩條匹配,因為同一條路徑方向不能改變,同理同一個終點也是這樣,符合匹配的定義。
最後答案=頂點數-最大匹配數

時間複雜度 O ( n 3 ) O(n^3)

程式碼

#include <cstdio>
#include <cstring>

using namespace std;

int t,n,m,cnt;
int ls[150],ne[20005],y[20005],link[150];
bool cover[150];

bool dfs(int k){
	for (int i=ls[k];i;i=ne[i])
	if (!cover[y[i]]){
		int t=link[y[i]];
		link[y[i]]=k;
		cover[y[i]]=1;
		if (t==0||dfs(t)) return 1;
		link[y[i]]=t;
	}
	return 0;
}

int main(){
	scanf("%d",&t);
	for (;t;t--){
		scanf("%d",&n);
		scanf("%d",&m);
		cnt=0;
		memset(link,0,sizeof(int)*(n+3));
		memset(ls,0,sizeof(int)*(n+3));
		for (int i=1;i<=m;i++){
			int a,b;
			scanf("%d%d",&a,&b);
			ne[++cnt]=ls[a];ls[a]=cnt;y[cnt]=b;
		}
		int ans=n;
		for (int i=1;i<=n;i++){
			memset(cover,0,sizeof(bool)*(n+3));
			if (dfs(i)) ans--;
		}
		printf("%d\n",ans);
	}
}

心之所至,是為天上錦繡;心之所安,是為人間繁華