hdu 1151 最小覆蓋路徑演算法證明
阿新 • • 發佈:2019-02-03
又是二分圖。若還不知道匈牙利演算法,看我前面的文章
先把每個點拆成兩個,一個表示出,一個表示入,根據資料輸入,對應的出點和對應入點之間構造了一條邊。這樣就有了一個二分圖。
有向圖的最小路徑覆蓋 = 總的點數(為拆分前的) - 最大匹配數。
證明如下:
先假設所有點我們都派出一個傘兵。然後每增加一條匹配,就代表我們從一個點出走向一個點。並且根據最大匹配的定義,我們可以直接看作是將這兩個點合併到一個路徑集合中,這個時候我們就可以減少一個傘兵。 所以 路徑覆蓋 = 總的點數 - 最大匹配數 。
然而這樣我們並沒有證明 是 “最小”覆蓋。 接下來證明最小。 假設 不是最小,那麼也就意味著還可以減少傘兵,而減少傘兵就必須合併某兩個點(即匹配),而匹配已經最大,所以矛盾。
即 需要派遣的傘兵數 = 總點數 - 匹配數。 最少傘兵派遣 = 總點數 - 最大匹配數。
#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); } }