POJ-3680 Intervals & NOI 2008 志愿者招募 费用流

POJ-3680 Intervals

Description

You are given N weighted open intervals. The ith interval covers (ai, bi) and weighs wi. Your task is to pick some of the intervals to maximize the total weights under the limit that no point in the real axis is covered more than k times.

Input

The first line of input is the number of test case.
The first line of each test case contains two integers, N and K (1 ≤ KN ≤ 200).
The next N line each contain three integers ai, bi, wi(1 ≤ ai < bi ≤ 100,000, 1 ≤ wi ≤ 100,000) describing the intervals.
There is a blank line before each test case.

Output

For each test case output the maximum total weights in a separate line.

Sample Input

4

3 1
1 2 2
2 3 4
3 4 8

3 1
1 3 2
2 3 4
3 4 8

3 1
1 100000 100000
1 2 3
100 200 300

3 2
1 100000 100000
1 150 301
100 200 300

Sample Output

14
12
100000
100301

题意

给定n条线段,每个点都不允许重复覆盖k次及以上,每条线段有一个权重,问能选出的最大权重是多少。

题解

首先题目给定的线段数很少,我们可以把线段的端点离散化,这样点的范围就降低到400。

然后dp是做不了k重覆盖的,所以我们考虑网络流

首先面临的就有一个问题,我们怎么通过建图保证选择了一条线段,所有点一定都被选择呢,这里有一个技巧,就是所有相邻的点之间连一条边,容量为k,费用为0,即\(adde(i,i+1,k,0)\),同时每条线段的端点之间连一条边,容量为1,费用为\(c[i]\),同时源点向1连边,最右的端点向汇点连边,容量均为k,费用均为0,这样保证每条线段只被选择一次,而且不会出现一个点被重复选择了k次的情况。

然后求一遍最大费用最大流,即可在保证不被覆盖k次以上的条件下获得最大的权重

至于为什么不会覆盖k次以上,可以这么想,每个交叉的线段左端点向右端点的边都会增加1的流量,而这单位流量是不会从交叉线段的左端点流入的,也就确实增加了1的流量,而i向i+1的连边保证了不会有同时超过k的流量存在,所以不会有超过k条交叉的线段,而不交叉的线段,从右端点流出的流量又会流进下一条线段的左端点,所以是同一单位的流量,不会对k的限制有影响。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 550;
const int inf = 0x3f3f3f3f;
struct node {
    int v, cap, cost, nxt;
    node() {}
    node(int v, int cap, int cost, int nxt): v(v), cap(cap), cost(cost), nxt(nxt) {}
} edge[N * N];
int head[N], tot;
void init() {
    memset(head, -1, sizeof(head));
    tot = 0;
}
void adde(int u, int v, int w, int c) {
    edge[tot] = node(v, w, c, head[u]);
    head[u] = tot++;
    edge[tot] = node(u, 0, -c, head[v]);
    head[v] = tot++;
}
int d[N], pre[N];
bool vis[N];
int spfa(int s, int t) {
    queue<int> q;
    memset(d, -inf, sizeof(d));
    memset(pre, -1, sizeof(pre));
    memset(vis, 0, sizeof(vis));
    d[s] = 0;
    vis[s] = 1;
    q.push(s);
    while (!q.empty()) {
        int u = q.front(); q.pop(); vis[u] = 0;
        for (int i = head[u]; ~i; i = edge[i].nxt) {
            int v = edge[i].v;
            if (edge[i].cap && d[v] < d[u] + edge[i].cost) {
                d[v] = d[u] + edge[i].cost;
                pre[v] = i;
                if (!vis[v]) {
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    return d[t] != -inf;
}
int MCMF(int s, int t) {
    int cost = 0;
    while (spfa(s, t)) {
        int minn = inf;
        for (int i = pre[t]; ~i; i = pre[edge[i ^ 1].v]) {
            minn = min(minn, edge[i].cap);
        }
        for (int i = pre[t]; ~i; i = pre[edge[i ^ 1].v]) {
            edge[i].cap -= minn;
            edge[i ^ 1].cap += minn;
        }
        if (d[t] < 0) break;
        cost += minn * d[t];
    }
    return cost;
}
int lis[N * 2];
int a[N], b[N], c[N];
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n, k;
        scanf("%d%d", &n, &k);
        init();
        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d%d%d", &a[i], &b[i], &c[i]);
            lis[++cnt] = a[i];
            lis[++cnt] = b[i];
        }
        sort(lis + 1, lis + cnt + 1);
        cnt = unique(lis + 1, lis + cnt + 1) - lis - 1;
        for (int i = 1; i <= n; i++) {
            a[i] = lower_bound(lis + 1, lis + cnt + 1, a[i]) - lis;
            b[i] = lower_bound(lis + 1, lis + cnt + 1, b[i]) - lis;
            adde(a[i], b[i], 1, c[i]);
        }
        for (int i = 1; i < cnt; i++) {
            adde(i, i + 1, k, 0);
        }
        int s = 0, t = cnt + 1;
        adde(s, 1, k, 0);
        adde(cnt, t, k, 0);
        printf("%d\n", MCMF(s, t));
    }
    return 0;
}

洛谷P3980 [NOI2008]志愿者招募

Description

申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。

Input

  第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。 接下来的一行中包含N 个非负整数,表示每天至少需要的志愿者人数。 接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了方便起见,我们可以认为每类志愿者的数量都是无限多的。

Output

  仅包含一个整数,表示你所设计的最优方案的总费用。

Sample Input

3 3
2 3 4
1 2 2
2 3 5
3 3 2

Sample Output

14

Hint

1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。

题解

这个题和上个题建图方式有点相似,所以放在一起了

这个题的问题是,我们怎么保证每天都有k个志愿者。代表每个志愿者的流量只有1,我们怎么表示志愿者的连续工作。

方法和上题有点类似,我们把i向i+1连一条容量为\(\infin -a[i]\),费用为0的边,每类志愿者从\(s[i]\)\(t[i]+1\)连一条容量为\(\infin\),花费为c[i]的边,源点向1连一条容量为\(\infin\),花费为0的边,n+1向汇点连一条容量为\(\infin\),花费为0的边。

显然,这个图是可以跑满流量为\(\infin\)的,但第一次肯定会经过i到i+1从而使流量小于\(\infin\),所以在后面的增流中,我们就会经过带权边,去增加流量,而这个过程由于在最小费用最大流中,我们可以保证增流的过程费用是最小的,最后的情况就是每条i向i+1的边流量都是inf,也就是都有k个人在工作了

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 2050;
typedef long long ll;
const ll inf = 0x3f3f3f3f3f3f3f3f;
struct node {
    int v; ll cap, cost; int nxt;
    node() {}
    node(int v, ll cap, ll cost, int nxt): v(v), cap(cap), cost(cost), nxt(nxt) {}
} edge[N * N];
int head[N], tot;
void init() {
    memset(head, -1, sizeof(head));
    tot = 0;
}
void adde(int u, int v, ll w, ll c) {
    edge[tot] = node(v, w, c, head[u]);
    head[u] = tot++;
    edge[tot] = node(u, 0, -c, head[v]);
    head[v] = tot++;
}
ll d[N], pre[N];
bool vis[N];
int spfa(int s, int t) {
    queue<int> q;
    memset(d, inf, sizeof(d));
    memset(pre, -1, sizeof(pre));
    memset(vis, 0, sizeof(vis));
    d[s] = 0;
    vis[s] = 1;
    q.push(s);
    while (!q.empty()) {
        int u = q.front(); q.pop(); vis[u] = 0;
        for (int i = head[u]; ~i; i = edge[i].nxt) {
            int v = edge[i].v;
            if (edge[i].cap && d[v] > d[u] + edge[i].cost) {
                d[v] = d[u] + edge[i].cost;
                pre[v] = i;
                if (!vis[v]) {
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    return d[t] != inf;
}
ll MCMF(int s, int t) {
    ll cost = 0;
    while (spfa(s, t)) {
        ll minn = inf;
        for (int i = pre[t]; ~i; i = pre[edge[i ^ 1].v]) {
            minn = min(minn, edge[i].cap);
        }
        for (int i = pre[t]; ~i; i = pre[edge[i ^ 1].v]) {
            edge[i].cap -= minn;
            edge[i ^ 1].cap += minn;
        }
        if (d[t] == inf) break;
        cost += minn * d[t];
    }
    return cost;
}
ll a[N];
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    init();
    for (int i = 1; i <= n; i++) {
        ll x;
        scanf("%lld", &x);
        adde(i, i + 1, inf - x, 0);
    }
    int s = 0, t = n + 2;
    adde(s, 1, inf, 0);
    adde(n + 1, t, inf, 0);
    for (int i = 1; i <= m; i++) {
        ll s, t, c;
        scanf("%lld%lld%lld", &s, &t, &c);
        adde(s, t + 1, inf, c);
    }
    printf("%lld\n", MCMF(s, t));
    return 0;
}

网络流妙不可言

posted @ 2020-01-15 16:11  Artoriax  阅读(161)  评论(0编辑  收藏  举报