BZOJ 2502 清理雪道 有上下界的网络流

39 篇文章 0 订阅
21 篇文章 0 订阅

求DAG最少用多少条链覆盖完。
建立源汇连接入出度为0的点。
然后对于每条边,下界为1,上界为无穷大。
跑一次有上下界的最小流即可。
二分答案转化为判定性问题。
由于建立了超级源汇以后要建一条反向边,上界为总流量。
于是二分答案即可,判定转化以后的无源汇上下界网络流是否存在可行流即可。

代码写的有点复杂了。求建议?

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
const int N = 1005, M = 50005, inf = 0x7f7f7f7f;
struct Graph {
    int h[N], p[M], v[M], w[M], q[M * 8], level[N], cnt, s, t;

    void init(int a, int b) {
        s = a; t = b;
        memset(h, -1, sizeof h);
        cnt = 0;
    }

    void add(int a, int b, int c) {
        p[cnt] = h[a]; v[cnt] = b; w[cnt] = c; h[a] = cnt++;
        p[cnt] = h[b]; v[cnt] = a; w[cnt] = 0; h[b] = cnt++;
    }

    bool bfs() {
        int f = 0, r = 0, u, i;
        memset(level, -1, sizeof level);
        q[r++] = s; level[s] = 0;
        while (f < r) {
            u = q[f++];
            for (i = h[u]; i != -1; i = p[i])
                if (w[i] > 0 && level[v[i]] == -1) {
                    level[v[i]] = level[u] + 1;
                    if (v[i] == t) return 1;
                    q[r++] = v[i];
                }
        }
        return level[t] != -1;
    }

    int dfs(int x, int low) {
        if (x == t) return low;
        int tmp, i, res = 0;
        for (i = h[x]; i != -1 && res < low; i = p[i])
            if (w[i] > 0 && level[v[i]] == level[x] + 1) {
                tmp = dfs(v[i], min(low - res, w[i]));
                w[i] -= tmp; w[i ^ 1] += tmp; res += tmp;
            }
        if (!res) level[x] = -1;
        return res;
    }

    int dinic() {
        int ans = 0;
        while (bfs()) ans += dfs(s, inf);
        return ans;
    }

    void restore() {
        for (int i = 0; i < cnt; i += 2)
            w[i] += w[i ^ 1], w[i ^ 1] = 0;
    }
};

struct MinFlow : public Graph {
    bool valid() {
        for (int i = h[s]; i != -1; i = p[i])
            if (w[i]) return 0;
        return 1;
    }
} g;

int main() {
    static int in[N];
    int n, ss, st, s, t, p, i, j;
    scanf("%d", &n);
    ss = n + 2, st = n + 3, s = 0, t = n + 1;
    memset(in, 0, sizeof in);
    g.init(ss, st);
    FOR(i,1,n) {
        scanf("%d", &p);
        if (!p) g.add(i, t, inf);
        else while (p--) {
            scanf("%d", &j);
            g.add(i, st, 1);
            g.add(ss, j, 1);
            g.add(i, j, inf);
            in[j] = 1;
        }
    }
    FOR(i,1,n) if (!in[i]) g.add(s, i, inf);
    g.add(t, s, 0);
    int l = 1, r = 100000, ans = -1;
    while (l <= r) {
        int mid = l + r >> 1;
        g.restore(); g.w[g.cnt - 2] = mid;
        g.dinic();
        if (g.valid()) r = mid - 1, ans = mid;
        else l = mid + 1;
    }
    printf("%d", ans);
    return 0;
}

2502: 清理雪道

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 449 Solved: 231
[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

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值