有向無環圖(DAG)的最小路徑覆蓋

DAG的最小路徑覆蓋

 

定義:在一個有向圖中,找出最少的路徑,使得這些路徑通過了全部的點。ios

最小路徑覆蓋分爲最小不相交路徑覆蓋最小可相交路徑覆蓋算法

最小不相交路徑覆蓋:每一條路徑通過的頂點各不相同。如圖,其最小路徑覆蓋數爲3。即1->3>4,2,5。閉包

最小可相交路徑覆蓋:每一條路徑通過的頂點能夠相同。若是其最小路徑覆蓋數爲2。即1->3->4,2->3>5。spa

特別的,每一個點本身也能夠稱爲是路徑覆蓋,只不過路徑的長度是0。code

 

DAG的最小不相交路徑覆蓋

算法:把原圖的每一個點V拆成$V_x$和$V_y$兩個點,若是有一條有向邊A->B,那麼就加邊$A_x->B_y$。這樣就獲得了一個二分圖。那麼最小路徑覆蓋=原圖的結點數-新圖的最大匹配數。blog

證實:一開始每一個點都是獨立的爲一條路徑,總共有n條不相交路徑。咱們每次在二分圖裏找一條匹配邊就至關於把兩條路徑合成了一條路徑,也就至關於路徑數減小了1。因此找到了幾條匹配邊,路徑數就減小了多少。因此有最小路徑覆蓋=原圖的結點數-新圖的最大匹配數。get

由於路徑之間不能有公共點,因此加的邊之間也不能有公共點,這就是匹配的定義。string

習題POJ1422it

//
//  main.cpp
//  POJ1422最小不想交路徑覆蓋
//
//  Created by beMaster on 16/4/8.
//  Copyright © 2016年 beMaster. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
using namespace std;
const int N = 200 + 10;
vector<int> g[N];
int cy[N];
bool vis[N];
bool dfs(int u){
    for(int i=0; i<g[u].size(); ++i){
        int v = g[u][i];
        if(vis[v]) continue;
        vis[v] = true;
        if(cy[v]==-1 || dfs(cy[v])){
            cy[v] = u;
            return true;
        }
    }
    return false;
}
int solve(int n){
    int ret = 0;
    memset(cy, -1, sizeof(cy));
    for(int i=1;i<=n;++i){
        memset(vis, 0, sizeof(vis));
        ret += dfs(i);
    }
    return n - ret;
}
int main(int argc, const char * argv[]) {
    int t,n,m;
    int u,v;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
            g[i].clear();
        for(int i=0;i<m;++i){
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
        }
        
        int ans = solve(n);
        printf("%d\n",ans);
    }
    return 0;
}

 

DAG的最小可相交路徑覆蓋

算法:先用floyd求出原圖的傳遞閉包,即若是a到b有路徑,那麼就加邊a->b。而後就轉化成了最小不相交路徑覆蓋問題。io

證實:爲了連通兩個點,某條路徑可能通過其它路徑的中間點。好比1->3->4,2->4->5。可是若是兩個點a和b是連通的,只不過中間須要通過其它的點,那麼能夠在這兩個點之間加邊,那麼a就能夠直達b,沒必要通過中點的,那麼就轉化成了最小不相交路徑覆蓋。

題目POJ2594

//
//  main.cpp
//  POJ2594最小可相交路徑覆蓋
//
//  Created by beMaster on 16/4/8.
//  Copyright © 2016年 beMaster. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
using namespace std;
const int N = 500 + 10;
bool dis[N][N];
bool vis[N];
int cy[N];
void floyd(int n){
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            for(int k=1;k<=n;++k)
                if(dis[i][k] && dis[k][j])//傳遞可達性
                    dis[i][j] = true;
}
bool dfs(int u, int n){
    for(int i=1;i<=n;++i){
        if(!vis[i] && dis[u][i]){
            vis[i] = true;
            if(cy[i]==-1 || dfs(cy[i], n)){
                cy[i] = u;
                return true;
            }
        }
    }
    return false;
}
int solve(int n){
    int cnt = 0;
    memset(cy,-1,sizeof(cy));
    for(int i=1;i<=n;++i){
        memset(vis,0,sizeof(vis));
        cnt += dfs(i, n);
    }
    return n - cnt;
}
int main(int argc, const char * argv[]) {
    int n,m;
    int a,b;
    while(scanf("%d%d",&n,&m),n+m){
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                dis[i][j] = false;
        for(int i=1;i<=m;++i){
            scanf("%d%d",&a,&b);
            dis[a][b] = true;
        }
        floyd(n);
        int ans = solve(n);
        printf("%d\n",ans);
    }
    return 0;
}
 

 

 

參考

二分圖大講堂——完全搞定最大匹配數(最小覆蓋數)、最大獨立數、最小路徑覆蓋、帶權最優匹配

相關文章
相關標籤/搜索