LA 2796 Concert Hall Scheduling 费用流(k覆盖模型)

题目大意:一个著名的音乐厅因为财务状况恶化快要破产,你临危受命,试图通过管理的手段来拯救它,方法之一就是优化演出安排,即聪明的决定接受和拒绝哪些乐团的演出申请,使得音乐厅的收益最大化。该音乐厅有两个完全相同的房间,因此各乐团在申请演出的时候并不会指定房间,你只需要随便分配一个即可。每个演出都会持续若干天,每个房间每天只能举行一场演出。申请数目n为不超过1000的正整数,每个申请用三个整数i, j, w描述,表示从第i天演到第j天,愿意支付w元。


题目分析:这道题还是重点在于建模,主要是k覆盖模型,想一下,每个区间划分为[i,j),但是每一个演出都只能在一家音乐厅演出,所以在i—>j+1上建边(i,j+1,1,-w),然后366天,第i—>i+1天建边(i,i+1,2,0),超级源点s:(s,1,2,0),超级汇点t=366

然后从最左边跑到最右边最小费用最大流即可。

区间 k 覆盖模型。

区间 k 覆盖问题。数轴上有一些带全值的左闭右开区间,选出权和尽量大的一些区间,使得任意一个数最多被 k 个区间覆盖。

解:首先这是可以用最小费用流解决的。构图方法是把每个数作为一个结点,然后对于权值为 w 的区间[ u,v ),建边u -> v,容量为1,费用为 -w。在对所有相邻的点建边 i -> i + 1,容量为 k,费用为0。最后,求最左点到最右点的最小费用最大流即可,其中每个流量对应一组互不相交的区间。如果数值范围太大,可以先离散化。


代码:

#include <stdio.h>  
#include <string.h>  
#include <algorithm>  
#define clear(A, X) memset(A, X, sizeof A)  
#define min(A, B) ((A) < (B) ? (A) : (B))  
using namespace std;  
  
const int maxE = 2000000;  
const int maxN = 2048;  
const int oo = 0x3f3f3f3f;  
  
struct Edge{  
    int v, c, w, n;  
    Edge(){}  
    Edge(int V, int C, int W, int N) : v(V), c(C), w(W), n(N){}  
};  
  
struct Node{  
    int l, r, w;  
};  
  
Edge edge[maxE];  
Node sche[maxN];  
int adj[maxN], cntE;  
int Q[maxE], head, tail;  
int d[maxN], inq[maxN], cur[maxN], f[maxN];  
int cost, flow, s, t;  
int n;  
  
void addedge(int u, int v, int c, int w){  
    edge[cntE] = Edge(v, c,  w, adj[u]); adj[u] = cntE++;  
    edge[cntE] = Edge(u, 0, -w, adj[v]); adj[v] = cntE++;  
}  
  
int spfa(){  
    clear(d, oo);  
    clear(inq, 0);  
    cur[s] = -1;  
    f[s] = oo;  
    d[s] = 0;  
    head = tail = 0;  
    Q[tail++] = s;  
    inq[s] = 1;  
    while(head != tail){  
        int u = Q[head++];  
        inq[u] = 0;  
        for(int i = adj[u]; ~i; i = edge[i].n){  
            int v = edge[i].v, c = edge[i].c, w = edge[i].w;  
            if(c && d[v] > d[u] + w){  
                d[v] = d[u] + w;  
                f[v] = min(f[u], c);  
                cur[v] = i;  
                if(!inq[v]){  
                    Q[tail++] = v;  
                    inq[v] = 1;  
                }  
            }  
        }  
    }  
    if(d[t] == oo) return 0;  
    flow += f[t];  
    cost += d[t] * f[t];  
    for(int i = cur[t]; ~i; i = cur[edge[i ^ 1].v]){  
        edge[i].c -= f[t];
        edge[i ^ 1].c += f[t];  
    }  
    return 1;  
}  
  
int MCMF(){  
    flow = cost = 0;  
    while(spfa());  
    return cost;  
}  
  
void init(){  
    clear(adj, -1);  
    cntE = 0;  
}  
  
void build(){  
    s = 0; t = n * 2 + 3;  
    int s1 = n * 2 + 1, s2 = n * 2 + 2;  
    addedge(s, s1, 1, 0);  
    addedge(s, s2, 1, 0);  
    for(int i = 1; i <= n; ++i) scanf("%d%d%d", &sche[i].l, &sche[i].r, &sche[i].w);  
    for(int i = 1; i <= n; ++i){  
        addedge(s1, i, 1, 0);  
        addedge(s2, i, 1, 0);  
        addedge(i, i + n, 1, -sche[i].w);  
        addedge(i + n, t, 1, 0);  
        for(int j = 1; j <= n; ++j){  
            if(sche[i].r < sche[j].l) addedge(i + n, j, 1, 0);  
        }  
    }  
}  
  
void work(){  
    init();  
    build();  
    printf("%d\n", -MCMF());  
}  
  
int main(){  
    while(~scanf("%d", &n) && n) work();  
    return 0;  
}  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值