<>
题目大意:
一天有N个小时,有m个节目(每种节目都有类型),有k个人,连续看相同类型的节目会扣w快乐值。每一种节目有都一个播放区间[l,r]。每个人同一时间只能看一个节目,看完可以获得快乐值,每个节目只能被人看一次。问最多可以获得多少快乐?
解题分析:
本题用费用流求解的方式还是比较直观的。因为本题要求的是最大费用,所以我们需要建图的时候需要将所有实际费用取反,然后将最后最小费用最大流求出的答案取反,就是要求的最大费用。具体建图过程见代码:
#includeusing namespace std;#define clr(a,b) memset(a,b,sizeof(a))template inline void read(T&x){ x=0;int f=1;char c=getchar(); while(c<'0' || c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0' && c<='9'){ x=x*10+c-'0';c=getchar(); } x*=f;}//最小费用最大流模板struct MCMF{ static const int N = 5e4+100, M = 5e5+5, INF = 0x7fffffff; int n, m, head[N], s, t, cnt; int dist[N], inq[N], pre[N], incf[N]; int mxflow, mncost; struct Edge{ int to, cap, cost, next; } edge[M * 4]; inline void init(int l, int r, int ss, int tt){ //for(int i = l; i <= r; i++ ){head[i] = -1;} memset(head, -1, sizeof(head)); s = ss, t = tt, cnt = 1; mxflow = mncost = 0; } inline void add(int u,int v,int cap,int cost){ edge[++cnt]=(Edge){v,cap,cost,head[u]};head[u]=cnt; edge[++cnt]=(Edge){u,0,-cost,head[v]};head[v]=cnt; } bool spfa(int s, int t){ //slf优化的spfa(双端队列优化) for(int i = 0; i < N; i++) dist[i] = INF, inq[i] = 0; clr(pre,0); deque q; inq[s]=1,dist[s] = 0; q.push_back(s); incf[s] = INF; while(!q.empty()){ int u = q.front();q.pop_front(); inq[u] = 0; for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].to,cap=edge[i].cap,cost=edge[i].cost; if(cap>0 && dist[v]>dist[u]+cost){ //更新每个点的最小费用 dist[v]=dist[u]+cost; incf[v]=min(incf[u], cap); //得到v点所在路径上的最小容量 pre[v]=i; //记录下这个点的前驱正向边 if(!inq[v]){ inq[v]=1; if(q.empty() || dist[v]>dist[q.front()])q.push_back(v); else q.push_front(v); //如果最小费用小于等于队首,直接塞入队列首 } } } } return dist[t]!=INF; } void update(int s,int t){ int x=t; while(x!=s){ int pos=pre[x]; edge[pos].cap-=incf[t]; edge[pos^1].cap+=incf[t]; x=edge[pos^1].to; } mxflow+=incf[t]; mncost+=dist[t]*incf[t]; } void minCostMaxFlow(int s,int t){ while(spfa(s,t)) update(s,t); }}mcmf;const int NN = 500 + 7;struct Node{ int l, r, w, fp; } node[NN];int st,ed;inline void GetMap(){ int n,m,k,w; read(n);read(m);read(k);read(w); st=0,ed=2*m+2; //0代表源点,2*m+1代表慈元典,ed代表汇点 mcmf.init(st,ed,st,ed); mcmf.add(st,2*m+1,k,0); //源点向次源点连一条容量为k,费用为0的边 for(int i=1;i<=m;i++) scanf("%d%d%d%d",&node[i].l,&node[i].r,&node[i].w,&node[i].fp); for(int i=1;i<=m;i++){ for(int j=1;j<=m;j++){ if(i==j)continue; if(node[i].r<=node[j].l) //对于那些满足条件的video, i的出点向j的入点连一条容量为1,费用为0(或者如果这两个点类型相同的话,费用就为W,因为本题是将所有费用置为它的相反数了) mcmf.add(i+m,j,1,(node[i].fp==node[j].fp)?w:0); //因为本题是求最大费用,所以要将所有费用取反,这里就是 w } } for(int i=1;i<=m;i++) mcmf.add(i,i+m,1,-node[i].w); //拆点,所有点的入点向入点连一条容量为1,费用为node[i].w的边 for(int i=1;i<=m;i++) mcmf.add(2*m+1,i,1,0); //源点向所有的点连上一条容量为1,费用为0的边 for(int i=1;i<=m;i++) mcmf.add(i+m,ed,1,0); //所有点的出度连一条容量为1,费用为0的边}inline void Solve(){ mcmf.minCostMaxFlow(st,ed); printf("%d\n",-mcmf.mncost); //取相反数,就是实际意义上的最大费用}int main(){ int T;cin>>T; while(T--){ GetMap(); Solve(); }}