bzoj2502 清理雪道

2502: 清理雪道

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1279  Solved: 690
[Submit][Status][Discuss]

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

Source

2011福建集训

分析:题目就是要求把所有边都走一次的最小流.

   很容易想到无源汇的上下界最小流. 添加一个源点S和一个汇点T,S向每个点连容量为inf的边,每个点向T连容量为inf的边.这样就转化成了有源汇的上下界最小流. 

   套用模板:1.每条边的起点u向终点v连边,容量为inf(因为上界是inf,下界是1,实际上是inf - 1,和inf没有区别).

   2.S向每个点连容量为inf的边,每个点向T连容量为inf的边.

   3.超级源点SS向每个点连容量为du[i]的边(如果du[i] > 0),每个点向超级汇点TT连容量为-du[i]的边(如果du[i] < 0).

   4.先求出SS到TT的最大流,再连边T --> S,容量为inf,再跑一次从SS到TT的最大流. TT到SS这条边跑过的流量即为答案.

   一个问题:为什么不拆点建图呢?(把一个点拆成入点和出点两个点).

   因为人可以随时停下来,并且滑的过程可能会经过许多点,而不是只经过两个点,不好建图.

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 30010,inf = 0x7fffffff;
int n,S,T,SS,TT,head[maxn],to[maxn],nextt[maxn],w[maxn],tot = 2;
int du[maxn],d[maxn];

void add(int x,int y,int z)
{
    w[tot] = z;
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;

    w[tot] = 0;
    to[tot] = x;
    nextt[tot] = head[y];
    head[y] = tot++;
}

bool bfs()
{
    memset(d,-1,sizeof(d));
    queue <int> q;
    q.push(SS);
    d[SS] = 0;
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        if (u == TT)
            return true;
        for (int i = head[u];i;i = nextt[i])
        {
            int v = to[i];
            if (w[i] && d[v] == -1)
            {
                d[v] = d[u] + 1;
                q.push(v);
            }
        }
    }
    return false;
}

int dfs(int u,int f)
{
    if (u == TT)
        return f;
    int res = 0;
    for (int i = head[u];i;i = nextt[i])
    {
        int v = to[i];
        if (w[i] && d[v] == d[u] + 1)
        {
            int temp = dfs(v,min(f - res,w[i]));
            w[i] -= temp;
            w[i ^ 1] += temp;
            res += temp;
            if (res == f)
                return res;
        }
    }
    if (!res)
        d[u] = -1;
    return res;
}

void dinic()
{
    while (bfs())
        dfs(SS,inf);
}

int main()
{
    scanf("%d",&n);
    S = n + 1;
    T = S + 1;
    SS = T + 1;
    TT = SS + 1;
    for (int i = 1; i <= n; i++)
    {
        int num;
        scanf("%d",&num);
        for (int j = 1; j <= num; j++)
        {
            int x;
            scanf("%d",&x);
            add(i,x,inf);
            du[i]--;
            du[x]++;
        }
        add(S,i,inf);
        add(i,T,inf);
    }
    for (int i = 1; i <= T; i++)
    {
        if (du[i] > 0)
            add(SS,i,du[i]);
        if (du[i] < 0)
            add(i,TT,-du[i]);
    }
    dinic();
    add(T,S,inf);
    dinic();
    printf("%d\n",w[tot - 1]);

    return 0;
}

 

posted @ 2018-03-21 14:55  zbtrs  阅读(171)  评论(0编辑  收藏  举报