2018 Multi-University Training Contest 10 1012 Problem L.Videos(hdu 6437)(最小费用流)

版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}

猜你喜欢

转载自blog.csdn.net/yz467796454/article/details/81949892