bzoj 2502 清理雪道 (有源汇上下界最小流)

Posted 日拱一卒 功不唐捐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 2502 清理雪道 (有源汇上下界最小流)相关的知识,希望对你有一定的参考价值。

2502: 清理雪道

Time Limit: 10 Sec  Memory Limit: 128 MB

Description

       滑雪场坐落在FJ省西北部的若干座山上。
从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。
你的团队负责每周定时清理雪道。你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。
由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务。
 

Input

 
输入文件的第一行包含一个整数n (2 <= n <= 100) – 代表滑雪场的地点的数量。接下来的n行,描述1~n号地点出发的斜坡,第i行的第一个数为mi (0 <= mi < n) ,后面共有mi个整数,由空格隔开,每个整数aij互不相同,代表从地点i下降到地点aij的斜坡。每个地点至少有一个斜坡与之相连。

Output

 
       输出文件的第一行是一个整数k – 直升飞机的最少飞行次数。
 

Sample Input

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

Sample Output

4
 
下界为1,上界为inf
法一、从超级源点向超级汇点跑一遍dinic,再由普通汇点向普通源点连一条下界为0,上界为inf的边,再由超级源点向超级汇点跑一遍dinic
答案为最后加的那条边的反向边的流量
#include<cstdio>
#include<queue>
#include<algorithm>
#define N 105
#define M 40001
using namespace std;
const int inf=2e9;
int n,a[N],ans;
int src,dec,S,T;
int to[M],next[M],front[N],tot=1,cap[M];
int lev[N],cur[N];
queue<int>q;
void add(int u,int v,int w)
{
    to[++tot]=v; next[tot]=front[u]; front[u]=tot; cap[tot]=w;
    to[++tot]=u; next[tot]=front[v]; front[v]=tot; cap[tot]=0;
}
bool bfs(int s,int t)
{
    for(int i=0;i<=n+3;i++) cur[i]=front[i],lev[i]=-1;
    while(!q.empty()) q.pop();
    lev[s]=0;
    q.push(s);
    int now;
    while(!q.empty())
    {
        now=q.front(); q.pop();
        for(int i=front[now];i;i=next[i])
         if(cap[i]>0&&lev[to[i]]==-1)
         {
             lev[to[i]]=lev[now]+1;
             if(to[i]==t) return true;
             q.push(to[i]);
         }
    } 
    return false;
}
int dfs(int now,int t,int flow)
{
    if(now==t) return flow;
    int rest=0,delta;
    for(int i=front[now];i;i=next[i])
     if(cap[i]>0&&lev[to[i]]>lev[now])
     {
          delta=dfs(to[i],t,min(flow-rest,cap[i]));
          if(delta)
          {
              cap[i]-=delta; cap[i^1]+=delta;
              rest+=delta; if(rest==flow) return rest;
         }
     }
    if(rest!=flow) lev[now]=-1;
    return rest;
}
int dinic(int s,int t)
{
    while(bfs(s,t)) dfs(s,t,inf); 
}
int main()
{
    scanf("%d",&n);
    dec=n+1; S=n+2;T=n+3;
    for(int i=1;i<=n;i++) add(i,dec,inf);
    for(int i=1;i<=n;i++) add(src,i,inf);
    int x,y;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        while(x--)
        {
            scanf("%d",&y); 
            add(i,y,inf);
            a[y]++; a[i]--;
        }
    }
    for(int i=1;i<=n;i++) 
     if(a[i]>0) add(S,i,a[i]);
     else if(a[i]<0) add(i,T,-a[i]);
    dinic(S,T);
    add(dec,src,inf);
    dinic(S,T);
    printf("%d",cap[tot]);
}

法二、先由普通汇点向普通源点连一条下界为0,上界为inf的边,再由超级源点向超级汇点跑一遍dinic,记那条边的流量为okflow

        删去那条边,删去超级源点,删去超级汇点,由普通汇点向普通源点跑一遍dinic,得出最大流为a,

        答案为okflow-a

#include<cstdio>
#include<queue>
#include<algorithm>
#define N 105
#define M 40001
using namespace std;
const int inf=2e9;
int n,a[N],ans;
int src,dec,S,T;
int to[M],next[M],front[N],tot=1,cap[M];
int lev[N],cur[N];
queue<int>q;
void add(int u,int v,int w)
{
    to[++tot]=v; next[tot]=front[u]; front[u]=tot; cap[tot]=w;
    to[++tot]=u; next[tot]=front[v]; front[v]=tot; cap[tot]=0;
}
bool bfs(int s,int t)
{
    for(int i=0;i<=n+3;i++) cur[i]=front[i],lev[i]=-1;
    while(!q.empty()) q.pop();
    lev[s]=0;
    q.push(s);
    int now;
    while(!q.empty())
    {
        now=q.front(); q.pop();
        for(int i=front[now];i;i=next[i])
         if(cap[i]>0&&lev[to[i]]==-1)
         {
             lev[to[i]]=lev[now]+1;
             if(to[i]==t) return true;
             q.push(to[i]);
         }
    } 
    return false;
}
int dfs(int now,int t,int flow)
{
    if(now==t) return flow;
    int rest=0,delta;
    for(int i=front[now];i;i=next[i])
     if(cap[i]>0&&lev[to[i]]>lev[now])
     {
          delta=dfs(to[i],t,min(flow-rest,cap[i]));
          if(delta)
          {
              cap[i]-=delta; cap[i^1]+=delta;
              rest+=delta; if(rest==flow) return rest;
         }
     }
    if(rest!=flow) lev[now]=-1;
    return rest;
}
int dinic(int s,int t)
{
    int tmp=0;
    while(bfs(s,t)) 
     tmp+=dfs(s,t,inf); 
    return tmp;
}
void del(int x)
{
    for(int i=front[x];i;i=next[i]) 
     cap[i]=cap[i^1]=0;
}
int main()
{
    scanf("%d",&n);
    dec=n+1; S=n+2;T=n+3;
    for(int i=1;i<=n;i++) add(i,dec,inf);
    for(int i=1;i<=n;i++) add(src,i,inf);
    int x,y;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        while(x--)
        {
            scanf("%d",&y); 
            add(i,y,inf);
            a[y]++; a[i]--;
        }
    }
    for(int i=1;i<=n;i++) 
     if(a[i]>0) add(S,i,a[i]);
     else if(a[i]<0) add(i,T,-a[i]);
    add(dec,src,inf);
    dinic(S,T);
    int okflow=cap[tot];
    del(T); del(S); cap[tot]=cap[tot-1]=0;
    printf("%d",okflow-dinic(dec,src));
}

问题:法二中,dinic得出的最大流 与 有普通汇点向普通源点 连边 的反向边的流量  为什么不相等?

printf("%d",okflow-dinic(dec,src));  ??

以上是关于bzoj 2502 清理雪道 (有源汇上下界最小流)的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ2502]清理雪道 有上下界网络流(最小流)

BZOJ 2502: 清理雪道 | 有上下界最小流

bzoj 2502: 清理雪道 最小流

P4843 清理雪道(上下界网络流)

bzoj千题计划158:bzoj2406: 矩阵(有源汇上下界可行流)

bzoj2502 清理雪道