LA 2796 Concert Hall Scheduling 费用流

来源:互联网 发布:淘宝网服装男士 编辑:程序博客网 时间:2024/06/15 13:23

传送门:LA 2796 Concert Hall Scheduling

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

接下来分成两个思路建模。


朴素建模分析:将每个乐队拆成两个点,之间建边(i,i + n,1,-w)。然后为每个房间分配一个流量,每个房间 x 向所有的乐队 i 建边(x,i,1, 0),然后每个乐队向能在他之后使用该房间的乐队 j 建边(i + n,j,1,0)。最后每个乐队向汇点建边(i + n,t,1,0)。跑一遍最小费用流,答案就是cost的相反数。

不得不说这是最容易想到的建模方法。但是效率太低了。


朴素建模代码如下:


//1012ms#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].v了,我今天没吃药,感觉自己萌萌哒QUQedge[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;}



接下来是另一种:

//------------------------------------

先说一下区间 k 覆盖模型。

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

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

//------------------------------------

对于本题,其中每个乐团的一个申请 (i,j,w)可以看做一个权值为 w 的 [ i,j + 1 ) 的左闭右开区间, k 在这个题目中就是房间的个数 2 。按照区间 k 覆盖模型建边跑一遍最小费用最大流即可。

代码如下:


//3ms(这就是差距!)#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 = 400;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(){f[s] = oo;clear(d, oo);clear(inq, 0);cur[s] = -1;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(){int l, r, w;s = 0; t = 366;addedge(s, 1, 2, 0);for(int i = 1; i <= n; ++i){scanf("%d%d%d", &l, &r, &w);addedge(l, r + 1, 1, -w);}for(int i = 1; i <= 365; ++i) addedge(i, i + 1, 2, 0);}void work(){init();build();printf("%d\n", -MCMF());}int main(){while(~scanf("%d", &n) && n) work();return 0;}


0 0