最小路徑覆蓋 網絡流24題

«問題描述:

給定有向圖G=(V,E)。設P 是G 的一個簡單路(頂點不相交)的集合。如果V 中每個頂點恰好在P 的一條路上,則稱P是G 的一個路徑覆蓋。P 中路徑可以從V 的任何一個頂點開始,長度也是任意的,特別地,可以爲0。G 的最小路徑覆蓋是G 的所含路徑條數最少的路徑覆蓋。設計一個有效算法求一個有向無環圖G 的最小路徑覆蓋。

關於最小不相交路徑覆蓋:

最小路徑覆蓋(path covering):是“路徑” 覆蓋“點”,即用盡量少的不相交簡單路徑覆蓋有向無環圖G的所有頂點,即每個頂點嚴格屬於一條路徑。路徑的長度可能爲0(單個點)。

最小路徑覆蓋數=G的點數-最小路徑覆蓋中的邊數。應該使得最小路徑覆蓋中的邊數儘量多,但是又不能讓兩條邊在同一個頂點相交。拆點:將每一個頂點i拆成兩個頂點Xi和Yi。然後根據原圖中邊的信息,從X部往Y部引邊。所有邊的方向都是由X部到Y部。因此,所轉化出的二分圖的最大匹配數則是原圖G中最小路徑覆蓋上的邊數。因此由最小路徑覆蓋數=原圖G的頂點數-二分圖的最大匹配數便可以得解。

關於最小可相交路徑覆蓋:

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

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

參考神牛博客點擊打開鏈接

對於存儲路徑可以存下路徑最末尾的點,然後向上輸出

#include<bits/stdc++.h>
using namespace std;
const int inf=1000000000;
const int maxn=20000,maxm=1e6+10;
struct Edge{
    int v,f,nxt;
};
int src,sink;
int g[maxn+10];
int nume;
Edge e[maxm*2+10];
void addedge(int u,int v,int c) {
    e[++nume].v=v;
    e[nume].f=c;
    e[nume].nxt=g[u];
    g[u]=nume;
    e[++nume].v=u;
    e[nume].f=0;
    e[nume].nxt=g[v];
    g[v]=nume;
}
void init() {
    memset(g,0,sizeof(g));
    nume=1;
}
queue<int> que;
bool vis[maxn+10];
int dist[maxn+10];
int n,m,k,dfn;
int h[maxn];
int a[maxn][30];
bool bfs() {
    memset(dist,0,sizeof(dist));
    while(!que.empty())que.pop();
    vis[src]=true;
    que.push(src);
    while(!que.empty()) {
        int u=que.front();
        que.pop();
        for(int i=g[u];i;i=e[i].nxt) {
            if(e[i].f && !vis[e[i].v]) {
                que.push(e[i].v);
                dist[e[i].v]=dist[u]+1;
                vis[e[i].v]=true;
            }
        }
    }
    return vis[sink];
}
int dfs(int u,int delta) {
    if(u==sink) {
        return delta;
    }
    else {
        int ret=0;
        for(int i=g[u];delta&&i;i=e[i].nxt) {
            if(e[i].f && dist[e[i].v]==dist[u]+1) {
                int dd=dfs(e[i].v,min(e[i].f,delta));
                e[i].f-=dd;
                e[i^1].f+=dd;
                delta-=dd;
                ret+=dd;
            }
        }
        return ret;
    }
}
int maxflow() {
    int ret=0;
    while(true) {
        memset(vis,0,sizeof(vis));
        bfs();
        if(!vis[sink]) return ret;
        ret+=dfs(src,inf);
    }
}
int to[maxn];
int in[maxn];
int main() {
    init();
    scanf("%d%d",&n,&m);
    src=0;
    sink=n+n+1;
    for(int i=1;i<=m;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        addedge(u,n+v,1);
    }
    for(int i=1;i<=n;i++) {
        addedge(src,i,1);
        addedge(n+i,sink,1);
    }
    int ans=n-maxflow();
    for(int u=1;u<=n;u++) {
        for(int i=g[u];i;i=e[i].nxt) {
            int v=e[i].v;
            if(e[i].f==0) {
                to[u]=v-n;
                in[v-n]++;
            }
        }
    }
    for(int i=1;i<=n;i++) {
        if(!in[i]) {
            int u=i;
            while(u>0) {
                printf("%d ",u);
                u=to[u];
            }
            puts("");
        }
    }
    printf("%d\n",ans);
}




發佈了61 篇原創文章 · 獲贊 0 · 訪問量 5368
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章