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));  ??
时间: 2024-08-05 07:08:27

bzoj 2502 清理雪道 (有源汇上下界最小流)的相关文章

hdu3157有源汇上下界最小流

题意:有源汇上下界最小流裸题,主要就是输入要用字符串的问题 #include<bits/stdc++.h> #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair<int,int> #define C 0.5772156649 #define pi acos(-1.0) #define ll long long #define mod 10000000

[hdu] 3157 Crazy Circuits || 有源汇上下界最小流

原题 有两个正负极n个节点和m个元件,每个元件告诉端点是接在哪个节点上的,并且每个元件有工作的最小电流限制,问使所有元件工作的满足条件的最小电流是多少. 有源汇上下界最小流. 考虑dinic的推流思想,所以在跑完可行流后,减去t到s的最大流就是最小流. 实现方法: 建图时先不加入t到s的inf边,跑最大流,再加入inf边跑最大流.若此刻是可行流,那么加入的t到s的inf边的反向边的权值就是最小流. ll ans=0; ans+=dinic(); add(t,s,inf); ans+=dinic(

sgu176 有源汇上下界最小流

题意:有一堆点和边,1起点,n终点,某些边有可能必须满流,要求满足条件的最小流 解法:按原图建边,满流的即上下界都是容量,但是这样按有源汇上下界可行流求出来的可能不是最小流,那么我们需要开始建边的时候不要建从t到s的边,先跑一边从ss到tt的最大流,然后把该边加上再跑一次从ss到tt的最大流,那么从t到s的反向边流过的流量就是原图的最小流,为什么是这样呢,这是因为当我们第一遍跑最大流的时候,此时没有t到s的这条边,那么图中的流量会尽量按其他的边流,当我们第二次跑最大流的时候,流出来的都是第一次中

BZOJ_2502_清理雪道_有源汇上下界最小流

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

HDU 3157 Crazy Circuits(有源汇上下界最小流)

HDU 3157 Crazy Circuits 题目链接 题意:一个电路板,上面有N个接线柱(标号1~N),还有两个电源接线柱 + -,给出一些线路,每个线路有一个下限值求一个可以让所有部件正常工作的总电流 没有则输出impossible 思路: 有源汇有上下界求最小流,建模方法为: 按无源汇先建图,跑超级源汇ss->tt一次,然后加入t->s,容量INF的边,在跑一次ss->tt,如果是满流,就有解,解为t->s边的当前流量 顺带写个最大流的,最大流就先把t->s加入直接跑

zoj 3229 Shoot the Bullet(有源汇上下界最大流)

Shoot the Bullethttp://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3442 Time Limit: 2 Seconds      Memory Limit: 32768 KB      Special Judge Gensokyo is a world which exists quietly beside ours, separated by a mystical border. It is a utopia

有源汇上下界可行流(POJ2396)

题意:给出一个n*m的矩阵的每行和及每列和,还有一些格子的限制,求一组合法方案. 源点向行,汇点向列,连一条上下界均为和的边. 对于某格的限制,从它所在行向所在列连其上下界的边. 求有源汇上下界可行流即可. 具体做法可以从汇点向源点连容量为正无穷的边,转成无源汇上下界可行流. 然后可以新建超级源汇,对于一条下界为l,上界为r的边(x,y),从超级源点向y,x向超级汇点连容量为l的边,x向y连容量为r-l的边. 如果那些容量为l的边没满流,则无解. #include <cstdio> #incl

算法复习——有源汇上下界可行流(bzoj2396)

题目: Description We are supposed to make a budget proposal for this multi-site competition. The budget proposal is a matrix where the rows represent different kinds of expenses and the columns represent different sites. We had a meeting about this, so

POJ2396&amp;ZOJ1994--Budget【有源汇上下界可行流】

链接:http://poj.org/problem?id=2396 题意:给一个n*m的矩阵,给出每行的总和以及每列的总和,再给出某些位置的最小或最大限制,问是否存在可能的矩阵,如果存在输出一种矩阵信息. 思路:这是一个有源汇的上下界可行流,对于这种题,从汇点连一条弧到源点,容量为INF,这不会影响流量平衡条件,并且此时原图转换为了无源汇的上下界可行流,剩下的做法和无源汇一样. 建图:原图源点src连向每个行顶点,容量为每行的和,每个列顶点连向汇点,容量为每个列顶点的和,行顶点和列顶点间也各有一