【网络流】最小路径覆盖问题

题目描述
给定有向图G=(V,E)。设P 是G 的一个简单路(顶点不相交)的集合。如果V 中每个
顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖。P 中路径可以从V 的任何一个顶
点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。
设计一个有效算法求一个有向无环图G 的最小路径覆盖。
提示:设V={1,2,……,n},构造网络G1=(V1,E1)如下:

每条边的容量均为1。求网络G1的(X0,Y0)最大流。
对于给定的给定有向无环图G,编程找出G的一个最小路径覆盖。
输入
由文件input.txt提供输入数据。文件第1 行有2个正整数n和m。n是给定有向无环图
G 的顶点数,m是G 的边数。接下来的m行,每行有2 个正整数i和j,表示一条有向边(i,j)。
输出
从第1 行开始,每行输出一条路径。文件的最后一行是最少路径数。
样例输入
11 12
1 2
1 3
1 4
2 5
3 6
4 7
5 8
6 9
7 10
8 11
9 11
10 11
样例输出
1 4 7 10 11
2 5 8
3 6 9
3
这个问题说白了就是一个二分图匹配问题为什么呢?
转换成网络流就是从源点连一条容量为1的边到每一个点,然后拆点将每一个两点之间有边的连一条有向边到另一个点的拆了点的那个点,然后把每个拆了点的点连到汇点(容量1)。因为如果有两个点都同时指向同一个点那么就只有一个边能过去,所以这时有流量的边就可以看作把两个端点合成了一个,所以网络流的最大流量实际上就是能合成的对数,所以所有点的数量-最大流=答案

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 10000;
const int MAXM = 500000;
const int INF = 999999999;
int tmp1[MAXN], tmp3[MAXN];
bool tmp2[MAXN];
int nn, ss, number;
struct node
{
    int v, c, f;
    node *next, *back;
};
struct ISAP
{
    node Edge[MAXM * 2 + 10];
    node *adj[MAXN+10];
    node *ecnt;
    int num[MAXN+10], d[MAXN+10];
    int s, t, Color[MAXN+10];
    bool del[MAXN+10];
    void addedge(int u,int v,int c)
    {
        ++ecnt;
        ecnt->v = v;
        ecnt->c = c;
        ecnt->f = 0;
        ecnt->next = adj[u];
        ecnt->back = ecnt+1;
        adj[u] = ecnt;

        ++ecnt;
        ecnt->v = u;
        ecnt->c = 0;
        ecnt->f = 0;
        ecnt->next = adj[v];
        ecnt->back = ecnt-1;
        adj[v] = ecnt;
    }
    void init(int _s, int _t)
    {
        ecnt = Edge;
        s = _s;
        t = _t;
        memset(adj, 0, sizeof adj);
        memset(num, 0, sizeof num);
        memset(d, 0, sizeof d);
    }
    int aug(int i,int maxt)
    {
        int ag = 0,mind = t,u;
        if(i == t)
            return maxt;
        for(node *p = adj[i]; p; p = p->next)
        {
            int v = p->v;
            if(p->c <= p->f)   continue;
            if(d[i] == d[v] + 1)
            {
                u = min(maxt - ag,p->c - p->f);
                u = aug(v,u);
                p->f += u;
                p->back->f -= u;
                ag += u;
                if(d[s] > t)
                    return ag;
                if(ag == maxt)
                    break;
            }
            mind = min(mind,d[v]);
        }

        if(ag == 0)
        {
            num[d[i]]--;
            if(!num[d[i]])
                d[s] = t+1;
            d[i] = mind + 1;
            num[d[i]]++;
        }

        return ag;
    }
    void Get_S(int u, int color)
    {
        Color[u] = color;
        for (node* p = adj[u]; p; p = p->next)
            if(Color[p->v]==0 && p->c > p->f)
                Get_S(p->v, color);
    }
    int work()
    {
        int ret = 0;
        while(d[s] <= t)
            ret += aug(s, INF);
        return ret;
    }
    bool Is_changed()
    {
        for(node *now = adj[s]; now; now = now->next)
            if(now->c != now->f)
                return false;
        return true;
    }
    void Space_Plan()
    {
        tmp1[0] = tmp3[0] = 0;
        for(int i=1; i<=nn; i++)
            if(Color[i] != 1)
                tmp1[++tmp1[0]] = i;
        for(int i=nn+1; i<=ss+nn; i++)
            if(Color[i] == 2)
                tmp3[++tmp3[0]] = i-nn;
        for(int i=1; i<=tmp1[0]; i++)
            printf(i == 1 ? "%d" : " %d", tmp1[i]);
        printf("\n");
        for(int i=1; i<=tmp3[0]; i++)
            printf(i == 1 ? "%d" : " %d", tmp3[i]);
        printf("\n");
    }
    int Next[MAXN+10];
    bool vis[MAXN+10];
    int _Search(){
        memset(vis, 0, sizeof vis);
        for(node *p=adj[s];p;p=p->next){
            if(p->f == 0) continue;
            for(node *p1=adj[p->v];p1;p1=p1->next){
                if(p1->v == s) continue;
                if(p1->f == 0) continue;
                Next[p->v] = p1->v-nn;
                vis[p1->v-nn] = true;
            }
        }
        for(int i=1;i<=nn;i++)
            if(!vis[i]){
                int now = i;
                while(now != 0){
                    printf(now == i ? "%d" : " %d", now);
                    now = Next[now];
                }
                printf("\n");
            }
        return 0;
    }
} isap;
void Read()
{
    int u,v;
    scanf("%d %d", &nn, &ss);
    isap.init(0,2*nn+1);
    for(int i=1; i<=nn; i++)
        isap.addedge(0,i,1), isap.addedge(nn+i, 2*nn+1, 1);
    for(int i=0; i<ss; i++)
    {
        scanf("%d %d", &u, &v);
        isap.addedge(u,v+nn,1);
    }
    int Ret = isap.work();
    isap._Search();
    printf("%d\n", nn-Ret);
}
int main()
{
    Read();

    return 0;
}

转载于:https://www.cnblogs.com/JeremyGJY/p/5921743.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值