(intermediate) 带下界的最小流 UVA 1440 - Inspection

You are in charge of a team that inspects a new ski resort. A ski resort is situated on several mountains and consists of a number of slopes. Slopes are connected with each other, forking and joining. A map of the ski resort is represented as an acyclic directed graph. Nodes of the graph represent different points in ski resort and edges of the graph represent slopes between the points, with the direction of edges going downwards.

\epsfbox{p4597.eps}

Your team has to inspect each slope of the ski resort. Ski lifts on this resort are not open yet, but you have a helicopter. In one flight the helicopter can drop one person into any point of the resort. From the drop off point the person can ski down the slopes, inspecting each slope as they ski. It is fine to inspect the same slope multiple times, but you have to minimize the usage of the helicopter. So, you have to figure out how to inspect all the slopes with the fewest number of helicopter flights.

Input 

Input consists of several datasets. The first line of each dataset contains a single integer number n ( 2$ \le$n$ \le$100) -- the number of points in the ski resort. The following n lines of the input file describe each point of the ski resort numbered from 1 to n. Each line starts with a single integer number mi ( 0$ \le$mi <n for i from 1 to n) and is followed by mi integer numbers aij separated by spaces. All aij are distinct for each i and each aij ( 1$ \le$aij$ \le$n, aij$ \ne$i) represents a slope going downwards from point i to point aij. Each point in the resort has at least one slope connected to it.

Output 

On the first line of each dataset write a single integer number k -- the minimal number of helicopter flights that are needed to inspect all slopes. Then write k lines that describe inspection routes for each helicopter flight. Each route shall start with single integer number from 1 to n -- the number of the drop off point for the helicopter flight, followed by the numbers of points that will be visited during inspection in the corresponding order as the slopes are inspected going downwards. Numbers on a line shall be separated by spaces. You can write routes in any order.

Sample Input 

8
1 3
1 7
2 4 5
1 8
1 8
0
2 6 5
0

Sample Output 

4
1 3 4 8
1 3 5 8
2 7 6
7 5



题意:给定一个有向无环图,然后你每次能从其中的任意一个点出发,沿着边走下去,问要多少次才能经过所有的边。


思路:这是一个有下界的最小流,因为每个边的下界容量为1,上界可设为inf,不难发现每次都从入度为0的点出发更优,所以我们加一个超级源点s连向所有入度为0的点,不难发现走到不能走才停下来会更好。所以所有初度为0的点连向一个超级汇点t。 然后我们开始建带下界容量的图,怎么建呢?我们另外还要加一个源点s'和汇点t',对于原来的图(不带任何汇点和源点)中的有向边u->v,我们由s'连一条容量为下界的边到v,然后由u连一条容量为下界的边到t',u->v的容量改为上界-下界,其他边不用改变(包括反向边),最后加一条t->s容量为inf的有向边,然后我们为了知道最小要多少次,我们可以二分这个值,那么我们继续加一个源点s''连向s,容量就是二分的值。当跑一次最大流得到的值等于所有边的下界之和,表示这时候是可行的。我们试着缩小二分的值,如果不行,我们就放大它。最后我们一定能得到一个最小的可行的容量。那么这个时候的残余网络里面告诉我们那些边是有流量的,注意这是流量为0的边,其实流量应该是下界的值。那么我们可以根据这个残余网络求出方案。这时候只需要从s开始dfs,经过的边就让流量减一,知道到达t,就输出方案,退出来。然后继续从s开始dfs,知道找不到为止。。。。


代码:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<stack>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1000+20;
const int inf=1e8;
struct Edge
{
    int u,v;
    int cap,flow;
    Edge(int u,int v,int cap,int flow)
    :u(u),v(v),cap(cap),flow(flow) {}
};

vector<Edge> edges;
vector<int> G[maxn];

vector<Edge> e_tp;

void add(int u,int v,int cap)
{
    edges.push_back(Edge(u,v,cap,0));
    edges.push_back(Edge(v,u,0,0));
    int m=edges.size();
    G[u].push_back(m-2);
    G[v].push_back(m-1);
}

struct ISAP
{
    int d[maxn],p[maxn],num[maxn],cur[maxn];
    int n,s,t;
    void init(int n) { this->n=n; }

    int Augment()
    {
        int x=t,a=inf;
        while(x!=s)
        {
            Edge&e=edges[p[x]];
            a=min(a,e.cap-e.flow);
            x=e.u;
        }
        x=t;
        while(x!=s)
        {
            edges[p[x]].flow+=a;
            edges[p[x]^1].flow-=a;
            x=edges[p[x]].u;
        }
        return a;
    }

    void bfs()
    {
        for(int i=0;i<n;++i) d[i]=inf;
        queue<int> q;
        d[t]=0;
        q.push(t);
        while(q.size())
        {
            int x=q.front();q.pop();
            for(int i=0;i<G[x].size();++i)
            {
                Edge&e=edges[G[x][i]];
                if(e.cap>0||d[e.v]<=d[x]+1) continue;
                d[e.v]=d[x]+1;
                q.push(e.v);
            }
        }
    }

    int maxflow(int s,int t)
    {
        this->s=s,this->t=t;
        memset(num,0,sizeof(num));
        memset(cur,0,sizeof(cur));
        bfs();
        for(int i=0;i<n;++i)
            if(d[i]!=inf)
                ++num[d[i]];
        int flow=0,x=s;
        while(d[s]<n)
        {
            if(x==t)
            {
                flow+=Augment();
                x=s;
            }
            int ok=0;
            for(int i=cur[x];i<G[x].size();++i)
            {
                Edge&e=edges[G[x][i]];
                if(e.cap>e.flow&&d[e.v]+1==d[x])
                {
                    ok=1;
                    cur[x]=i;
                    p[e.v]=G[x][i];
                    x=e.v;
                    break;
                }
            }
            if(!ok)
            {
                int m=n-1;
                for(int i=0;i<G[x].size();++i)
                {
                    Edge&e=edges[G[x][i]];
                    if(e.cap>e.flow) m=min(m,d[e.v]);
                }
                if(--num[d[x]]==0) break;
                ++num[d[x]=m+1];
                cur[x]=0;
                if(x!=s) x=edges[p[x]].u;
            }
        }
        return flow;
    }
}solver;

int n;
int in[maxn],out[maxn];
int maxedges;
void input()
{
    edges.clear();
    for(int i=0;i<maxn;++i) G[i].clear();
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    maxedges=0;
    for(int i=1;i<=n;++i)
    {
        int mi;scanf("%d",&mi);
        maxedges+=mi;
        while(mi--)
        {
            ++out[i];
            int x;scanf("%d",&x);
            ++in[x];
            add(i,x,inf-1);
        }
    }
    for(int i=1;i<=n;++i)
    {
        if(in[i]==0) add(0,i,inf);
        add(n+2,i,in[i]);
        if(out[i]==0) add(i,n+1,inf);
        add(i,n+3,out[i]);
    }
    add(n+1,n+4,inf);
}

vector<Edge> output;
int w[maxn][maxn];
int S[maxn],c;

int cnt=0;
bool dfs(int x)
{
    if(x==n+1)
    {
      //  printf("%d\n",c);
        printf("%d",S[0]);
        for(int i=1;i<c;++i) printf(" %d",S[i]);
        printf("\n");
        return true;
    }
    if(1<=x&&x<=n) S[c++]=x;
  //  printf("%d\n",x);
    int sz=G[x].size();
    for(int i=0;i<sz;++i)
    {
        Edge&e=output[G[x][i]];
        int y=e.v;
        if(w[x][y]==inf||w[x][y]<=0) continue;
        --w[x][y];
        if(dfs(y)) return true;
        ++w[x][y];
    }
    if(1<=x&&x<=n) --c;
    return false;
}


void solve()
{
    solver.init(n+5);
    int left=1,right=maxedges;
  //  cout<<maxedges<<endl;
    int mid=(left+right)>>1;
    e_tp=edges;
    G[n+4].push_back(edges.size());
    G[0].push_back(edges.size()+1);
    int ans=inf;
    while (left<=right)
    {
        edges.push_back(Edge(n+4,0,mid,0));
        edges.push_back(Edge(0,n+4,0,0));
        int flow=solver.maxflow(n+2,n+3);
      //  cout<<flow<<endl;
        if(flow==maxedges)
        {
            right=mid-1;
            if(ans>mid)
            {
                ans=mid;
                output=edges;
            }
        }
        else if(flow<maxedges)
            left=mid+1;
        mid=(left+right)>>1;
        edges=e_tp;
    }
    for(int i=0;i<=n+5;++i)
    for(int j=0;j<=n+5;++j)
    w[i][j]=inf;
    for(int i=0;i<output.size();i+=2)
    {
        Edge&e=output[i];
        if(e.cap==0||e.flow<0) continue;
        if(e.u>n+1) continue;
        if(e.v>n+1) continue;
        w[e.u][e.v]=e.flow+1;
        if(e.u==0) --w[e.u][e.v];
    }
    c=0;
    w[0][n+1]=w[n+1][0]=inf;
    printf("%d\n",ans);
    int sz=G[n+4].size();
    while(dfs(0)) c=0;
}

void GetInput()
{
    char path[1000];
    freopen("in.txt","w",stdout);
    for(int i=0;i<=66;++i)
    {
        sprintf(path,"C:\\Users\\Devil\\Desktop\\tests\\%02d",i);
        freopen(path,"r",stdin);
        while(gets(path))
        {
            printf("%s\n",path);
        }
        printf("\n");
    }
}

void GetAns()
{
    char path[1000];
    freopen("out.txt","w",stdout);
    for(int i=0;i<=66;++i)
    {
        sprintf(path,"C:\\Users\\Devil\\Desktop\\tests\\%02d.a",i);
        freopen(path,"r",stdin);
        while(gets(path))
        {
            printf("%s\n",path);
        }
        printf("\n");
    }
}
int main()
{
   // GetAns(); return 0;
   // GetInput(); return 0;
  // freopen("in.txt","r",stdin);
    while (scanf("%d",&n)==1)
    {
        input();
        solve();
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值