版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yz467796454/article/details/81949892
题目链接:hdu 6437 Problem L.Videos
Sample Input
2
10 3 1 10
1 5 1000 0
5 10 1000 1
3 9 10 0
10 3 1 10
1 5 1000 0
5 10 1000 0
3 9 10 0
Sample Output
2000
1990
题意:一天有n个小时,m个节目,k个人,以及W,每一个节目从s小时开始t小时结束,能够带给人w的快乐,属于op类型,一个节目只能被一个人看,一个人能看多个节目,但不能同时看两个节目(时间3到4,4到5的两个节目是可以都看的),如果一个人连续看的两个节目属于同一个类型,就会失去W的快乐。
思路:最小费用流。
建图:将节目拆成两个点,代表开始时间点和结束时间点。人作为源点,超级源点ssp到源点sp流量为k,费用为0,限制k个人看节目;源点到节目开始点流量为inf,费用为0;节目开始点s,到节目结束点t,流量为1,费用为-w,限制一个节目只能被一个人看;第i个节目结束点t,到第j个节目开始点s,若i的t小于等于j的s,说明这两个节目时间没有重叠,可以都被一个人看,所以建边,流量为1,当两个节目属于统一类型,费用为W,不同类型,费用为0;节目结束点t到汇点tp建边,流量为1,费用为0;最后超级源点ssp到汇点tp建边,流量为k,费用为0,保证最小费用为0,不会出现负值。
补充:一直以来都把最小费用当作是最小费用最大流的简称,其实两者是不一样的,最小费用最大流是在保证最大流的前提下求得最小费用的,而最小费用流则只需要保证费用最小即可。两者在模板上是一样的,区别在建图,最小费用流多了两条边,一条是超级源点到源点的边,一条是超级源点到汇点的费用为0的边,可以保证答案不为负数。
写代码千万别忘了初始化,一下午跑不出答案,一直以为建图错误,其实是没初始化
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int maxn=1000;
const int maxm=100000;
const int INF=0x3f3f3f3f;
struct Edge {
int to,next,cap,cost;
Edge(){}
Edge(int to,int next,int cap,int cost):to(to),next(next),cap(cap),cost(cost){}
}edge[maxm];
int first[maxn],tot;
int pre[maxn],dis[maxn];
bool vis[maxn];
int N; //节点总个数,节点编号从0~N-1
void init(int n){
N=n;
tot=0;
memset(first,-1,sizeof(first));
}
void addedge(int u,int v,int cap,int cost){
edge[tot]=Edge(v,first[u],cap,cost);
first[u]=tot++;
edge[tot]=Edge(u,first[v],0,-cost);
first[v]=tot++;
}
bool spfa(int s,int t){
queue<int>q;
for(int i=0;i<N;i++) {
dis[i]=INF;
vis[i]=false;
pre[i]=-1;
}
dis[s]=0;
vis[s]=true;
q.push(s);
while(!q.empty()) {
int u=q.front();
q.pop();
vis[u]=false;
for(int i= first[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(edge[i].cap&&dis[v]>dis[u]+edge[i].cost ){
dis[v]=dis[u]+edge[i].cost;
pre[v]=i;
if(!vis[v]) {
vis[v]=true;
q.push(v);
}
}
}
}
if(pre[t]==-1) return false;
else return true;
}
int minCostMaxflow(int s,int t,int &cost) {
int flow=0;
cost = 0;
while(spfa(s,t)) {
int Min=INF;
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
if(Min >edge[i].cap)Min=edge[i].cap;
}
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
edge[i].cap-=Min;
edge[i^1].cap+=Min;
cost+=edge[i].cost*Min;
}
flow+=Min;
}
return flow;
}
struct node{
int s,t,v,op;
}a[100005];
bool cmp(const node &a,const node &b){
if(a.s==b.s)return a.t<b.t;
return a.s<b.s;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
init(500);//初始化!!!!!!!!!!
int n,m,k,w;
scanf("%d%d%d%d",&n,&m,&k,&w);
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&a[i].s,&a[i].t,&a[i].v,&a[i].op);
}
sort(a+1,a+1+m,cmp);
int ssp=0;
int sp=1;
addedge(ssp,sp,k,0);//超级源点到源点
for(int i=1;i<=m;i++){
addedge(1,i+1,INF,0);//源点到节目开始点
addedge(i+1,i+201,1,-a[i].v);//节目开始点到节目结束点
}
for(int i=1;i<=m;i++){
for(int j=i+1;j<=m;j++){
if(a[i].t<=a[j].s){//i节目结束点到j节目开始点
if(a[i].op==a[j].op)addedge(j+201,i+1,1,w);
else addedge(j+201,i+1,1,0);
}
}
}
int tp=402;
for(int i=1;i<=m;i++){
addedge(i+201,tp,1,0);//节目结束点到汇点
}
addedge(ssp,tp,k,0);//超级源点到汇点
int cost;
minCostMaxflow(ssp,tp,cost);
printf("%d\n",-cost);
}
return 0;
}