原题: http://acm.hdu.edu.cn/showproblem.php?pid=6437
题意:
一天有N个小时,有m个节目(每种节目都有类型),有k个人,连续看相同类型的节目会扣w快乐值。
每一种节目有都一个播放区间[l,r]。每个人同一时间只能看一个节目,看完可以获得快乐值。问最多可以获得多少快乐?
解析:
乍一看是dp题,但是k个人不好维护。
最小费用最大流可以做,将快乐值的相反数作为费用,流量为人数。因为如果人多的话就不需要两个人连续挤一段了,所以答案流量一定会达到最值。
建图:
- 超级源点到源点流K(只能有k条路线)
- 源点到各个点(时间段)流1
- 点到其拆点流1,花费-w[i]
- 各拆点到汇点流1
- 可以连接的点(i->j),i的拆点到j流1,如果连续且种类相同,花费W
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
#define LL long long
#define pill pair<int,int>
const int inf=0x3f3f3f3f;
const LL infll=1e18;
const int N=1010,M=202020;
int head[N],nex[M],to[M],val[M],cost[M],now;
void add(int a,int b,int v,int c){
to[++now]=b;val[now]=v;cost[now]=c;nex[now]=head[a];head[a]=now;
to[++now]=a;val[now]=0;cost[now]=-c;nex[now]=head[b];head[b]=now;
}
//*********************
int sp,ep,dis[N];
bool vis[N];
int pre[N];
bool SPFA(int &flow,int &cos){
memset(vis,0,sizeof(vis));
memset(pre,-1,sizeof(pre));
for(int i=0;i<N;i++)dis[i]=inf;
queue<int>Q;
dis[sp]=0;vis[sp]=1;Q.push(sp);
int d=inf;
while(!Q.empty()){
int p=Q.front();Q.pop();
vis[p]=0;
for(int i=head[p];~i;i=nex[i]){
int u=to[i];
if(val[i]>0&&dis[u]-cost[i]>dis[p]){
dis[u]=dis[p]+cost[i];
pre[u]=i;
if(!vis[u]){
vis[u]=1;Q.push(u);
}
}
}
}
if(dis[ep]==inf)return 0;
for(int i=pre[ep];~i;i=pre[to[i^1]]){
d=min(d,val[i]);
}
for(int i=pre[ep];~i;i=pre[to[i^1]]){
val[i]-=d;
val[i^1]+=d;
cos+=cost[i]*d;
}
flow+=d;
return 1;
}
int MinCost(){
int flow=0,cost=0;
while(SPFA(flow,cost)){}
return cost;
}
//***********************
int n,m,k,W;
struct node{
int id,s,t,v,op;
bool operator < (const node &r)const{
return s<r.s;
}
}e[201];
void init(){
now=-1;//要求第一条边为0
memset(head,-1,sizeof(head));
}
//sp -p-> point --> ep
int main(){int t;scanf("%d",&t);while(t--){
scanf("%d%d%d%d",&n,&m,&k,&W);
init();
sp=0;ep=N-1;
int tmp_sp=N-2;
add(sp,tmp_sp,k,0);
for(int i=1;i<=m;i++){
e[i].id=i;
scanf("%d%d%d%d",&e[i].s,&e[i].t,&e[i].v,&e[i].op);
add(tmp_sp,i,1,0);
add(m+i,ep,1,0);
add(i,m+i,1,-e[i].v);
}
sort(e+1,e+1+m);
for(int i=1;i<m;i++){
for(int j=i+1;j<=m;j++){
if(e[i].t<=e[j].s){
if(e[i].op!=e[j].op){
add(e[i].id+m,e[j].id,1,0);
}
else{
add(e[i].id+m,e[j].id,1,W);
}
}
}
}
int ans=MinCost();
printf("%d\n",-ans);
}}