最小路径覆盖 网络流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
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 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);
}




你可能感兴趣的:(网络流,二分图)