zoukankan      html  css  js  c++  java
  • 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
    ",w[tot - 1]);
    
        return 0;
    }
  • 相关阅读:
    编程里的数字游戏
    SqlServer2005Express下的事件探查器
    来玩玩这个
    把一个bitmap在内存中的数据块 搬到另一个bitmap中
    一些常用的sql
    C#图像处理
    来玩玩画直线
    关于未来的思考
    年末个人小结
    [转载]编写超级可读代码的15个最佳实践
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8617014.html
Copyright © 2011-2022 走看看