POJ Intervals (最小费用最大流)

题目: LINK

给定 N 个带权的开区间,第 i 个区间覆盖(ai, bi),权为 wi。现在要你挑出一些区间使得总权值最大,并且满足实轴上任意一个点被覆盖不超过 K 次。(1 <= K <= N <= 200, 1 <= ai < bi <= 100,000, 1 <= wi <= 100,000)

首先要将每个区间的端点离散化,1..M,另加源 s=0,汇 t=M+1;对每个点 i (0 <= i <= M)加边(i, i+1, K, 0);
对每个区间(ai, bi)加边(ai’, bi’, 1, -wi),其中ai’, bi’分别表示 ai, bi 离散化后对应的数值
按照这个图求最小费用最大流,得到的最小费用的相反数就是要求的结果.

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std; 
#define INF 1000000000
//typedef __int64 LL; 
#define N 444
int t, n, k , tot, hh[N], dis[N], inq[N], S, T, pre[N]; 
set<int> se; 
map<int, int> ma; 
struct node {
    int u, v, w, c, next; 
}edge[1000000]; 
struct no {
    int u, v, w; 
}ee[N]; 
void init() {
    memset(hh, -1, sizeof(hh)); 
    tot = 0; 
}
void add(int u, int v, int w, int c) {
    edge[tot].u = u; edge[tot].v = v; 
    edge[tot].w = w; edge[tot].c = c; 
    edge[tot].next = hh[u]; hh[u] = tot ++; 
}
int spfa() {
    queue<int > Q; 
    memset(pre, -1, sizeof(pre)); 
    memset(inq, 0, sizeof(inq)); 
    for(int i = 0; i <= T; i++) dis[i] = INF; // ?
    dis[S] = 0; inq[S] = 1; 
    Q.push(S); 
    while(!Q.empty()) {
        int u = Q.front(); Q.pop(); 
        inq[u] = 0; 
        for(int i = hh[u]; i != -1; i = edge[i].next) {
            int v = edge[i].v; 
            if(edge[i].w && dis[v] > dis[u] + edge[i].c){
                pre[v] = i; dis[v] = dis[u] + edge[i].c; 
                if(!inq[v]) {
                    inq[v] = 1; Q.push(v); 
                }
            }
        }
    }
    return dis[T] != INF; 
}
int mcml() {
    int flow, cost; 
    flow = cost = 0; 
    while(spfa()) {
        int tmp = INF; 
        for(int i = pre[T]; i != -1; i = pre[edge[i].u])
            tmp = min(tmp, edge[i].w); 
        flow += tmp; 
        cost += dis[T] * tmp; 
        for(int i = pre[T]; i != -1; i = pre[edge[i].u] )
            edge[i].w -= tmp, edge[i^1].w += tmp; 
    }
    return cost; 
}
int main() 
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin); 
#endif // ONLINE_JUDGE
    scanf("%d", &t); 
    while(t -- ) {
        scanf("%d%d", &n, &k); 
        init(); 
        se.clear(); 
        ma.clear(); 
        for(int i = 1; i <= n; i++) {
            scanf("%d%d%d", &ee[i].u, &ee[i].v, &ee[i].w); 
            se.insert(ee[i].u); se.insert(ee[i].v); 
        }
        int id = 1; 
        for(set<int >::iterator it = se.begin(); it != se.end(); it ++) {
            ma[*it] = id++; 
        }
        // build map
        S = 0; T = id; 
        for(int i = 0; i < T; i++) {
            add(i, i+1, k, 0); add(i+1, i, 0, 0); 
        }
        for(int i = 1; i <= n; i++) {
            int id1= ma[ee[i].u]; 
            int id2 = ma[ee[i].v]; 
            add(id1, id2, 1, -ee[i].w); add(id2, id1, 0, ee[i].w); 
        }
        // finish build.
        int ans = mcml(); 
        printf("%d\n", -ans); 
    }
    return 0; 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值