码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 3762 The Bonus Salary! 最小费用最大流

时间:2015-01-23 18:29:31      阅读:242      评论:0      收藏:0      [点我收藏+]

标签:


题意最后可以简化为

给出若干个区间,每个区间由左端点,右端点, 权值构成。

挑出若干个区间,使得权值和最大,但必须满足区间任意部分重叠次数不超过k


这题跟POJ3680一样一样的

构图是这样

先把这些区间都给hash了。 

hash完必然这些区间端点都落在1,2,3..., cnt   这些数中, cnt是hash完 不同数的个数

然后建边, 源点为S,汇点为T

S到1  建边  流量为k  费用为0

1到2,2到3,3到4....cnt-1到cnt   分别建边 流量为k 费用为0

cnt到T建边  流量为k  费用为0

然后对于每个区间[l,r] 费用为w的

建边 l 到 r  流量为1  费用为-w

这样你在图上一画

就能知道这样做的巧妙的。

那样互相之间不会重叠的区间,  一个流量就可以把他们都走完。

重叠了的区间自然要受到k这个限制。

每个区间走掉1个流量之后,会在右端点回归大部队


#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <map>
#include <ctime>
#define MAXN 4005
#define MAXM 1122222
#define INF 1000000001
using namespace std;
struct Interval {
    int a, b, w;
}p[2222];
int a[5555];
int nt, k;
int getnum(char a, char b) {
    return (a - '0') * 10 + (b - '0');
}
int gethash(char s[]) {
    int a = getnum(s[0], s[1]);
    int b = getnum(s[3], s[4]);
    int c = getnum(s[6], s[7]);
    return a * 3600 + b * 60 + c;
}
struct EDGE
{
    int v, cap, cost, next, re;    //  re记录逆边的下标。
} edge[MAXM];
int n, m, ans, flow, src, des;
int e, head[MAXN];
int que[MAXN], pre[MAXN], dis[MAXN];
bool vis[MAXN];
void init()
{
    e = ans = flow = 0;
    memset(head, -1, sizeof(head));
}
void add(int u, int v, int cap, int cost)
{
    edge[e].v = v;
    edge[e].cap = cap;
    edge[e].cost = cost;
    edge[e].next = head[u];
    edge[e].re = e + 1;
    head[u] = e++;
    edge[e].v = u;
    edge[e].cap = 0;
    edge[e].cost = -cost;
    edge[e].next = head[v];
    edge[e].re = e - 1;
    head[v] = e++;
}
bool spfa()
{
    int i, h = 0, t = 1;
    for(i = 0; i <= n; i ++)
    {
        dis[i] = INF;
        vis[i] = false;
    }
    dis[src] = 0;
    que[0] = src;
    vis[src] = true;
    while(t != h)
    {
        int u = que[h++];
        h %= n;
        vis[u] = false;
        for(i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if(edge[i].cap && dis[v] > dis[u] + edge[i].cost)
            {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v])
                {
                    vis[v] = true;
                    que[t++] = v;
                    t %= n;
                }
            }
        }
    }
    if(dis[des] == INF) return false;
    return true;
}
void end()
{
    int u, p, mi = INF;
    for(u = des; u != src; u = edge[edge[p].re].v)
    {
        p = pre[u];
        mi = min(mi, edge[p].cap);
    }
    for(u = des; u != src; u = edge[edge[p].re].v)
    {
        p = pre[u];
        edge[p].cap -= mi;
        edge[edge[p].re].cap += mi;
        ans += mi * edge[p].cost;     //  cost记录的为单位流量费用,必须得乘以流量。
    }
    flow += mi;
}

int main() {
    while(scanf("%d%d", &nt, &k) != EOF) {
        char s[11];
        int w;
        int cnt = 0;
        for(int i = 0; i < nt; i++) {
            scanf("%s", s);
            p[i].a = gethash(s);
            scanf("%s", s);
            p[i].b = gethash(s);
            scanf("%d", &w);
            p[i].w = w;
            a[cnt++] = p[i].a;
            a[cnt++] = p[i].b;
        }
        sort(a, a + cnt);
        cnt = unique(a, a + cnt) - a;
        for(int i = 0; i < nt; i++) {
            p[i].a = lower_bound(a, a + cnt, p[i].a) - a + 1;
            p[i].b = lower_bound(a, a + cnt, p[i].b) - a + 1;
        }
        init();
        src = cnt + 1;
        des = cnt + 2;
        n = cnt + 2;
        for(int i = 1; i < cnt; i++) add(i, i + 1, k, 0);
        add(src, 1, k, 0);
        add(cnt, des, k, 0);
        for(int i = 0; i < nt; i++) {
            add(p[i].a, p[i].b, 1, -p[i].w);
        }
        while(spfa() && dis[des] < 0) end();
        printf("%d\n", -ans);
    }
    return 0;
}


POJ 3762 The Bonus Salary! 最小费用最大流

标签:

原文地址:http://blog.csdn.net/sdj222555/article/details/43057353

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!