poj 3762 费用流 同3680

题意:

    给你每个任务的时间段和它的任务得分,再给你你所拥有的天数,问在这有限的天数里怎么安排时间才能的最多的分。

题解:

    和3680一样的建图方法。离散化一下,每个点之间连边,容量无穷费用为零。某个任务时间段的两端点连边,容量为1,费用为任务得分。源点与第一个点相连,容量为天数,费用为零。

    之所以每个点都连一条边并且容量无穷费用为零,是使之就像一个分叉路口一样,如果你要做这个时间点上的任务,那你就走时间段那条边,但如果你不做这个时间点上的任务,为了确保你还可以沿着时间继续走下去,所以建了一条容量为无穷费用为零的边。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MX 10000000
#define nMax 100000
#define eMax 100000
using namespace std;
int time[90000];
struct Task{
	int st,ed,numb;
}task[2222];
struct Egde{
	int u,v,c,f,w,next,pre;  // u起点 v终点 c容量 f流量 w费用 next下一条边 pre反边
}eg[eMax];
int N, K, ans,e,vs,vt;
int k, list[nMax],time1[nMax];
int que[10000000], pre[nMax], dis[nMax];
bool vis[nMax];
void add(int u, int v, int c, int w){//u起点,v终点,c容量,w费用
	eg[k].u=u;eg[k].v=v;eg[k].pre=k+1;eg[k].next=list[u];eg[k].c=c;eg[k].f=0;eg[k].w=w;list[u]=k++;
	eg[k].u=v;eg[k].v=u;eg[k].pre=k-1;eg[k].next=list[v];eg[k].c=0;eg[k].f=0;eg[k].w=-w;list[v]=k++;
}
int min(int a,int b){
	return a<b?a:b;
}
bool spfa(){                  //  源点为0,汇点为n。
	int i, head = 0, tail = 1;
	for(i = vs; i <= vt; i ++){//源点到汇点初始化
		dis[i] = MX;
		vis[i] = false;
	}
	dis[vs] = 0;
	que[0] = vs;
	vis[vs] = true;
	while(tail > head){       //  这里最好用队列,有广搜的意思,堆栈像深搜。
		int u = que[head ++];
		for(i = list[u]; i != -1; i = eg[i].next){
			int v = eg[i].v;
			if(eg[i].c-eg[i].f && dis[v] > dis[u] + eg[i].w){
				dis[v] = dis[u] + eg[i].w;
				pre[v] = i;
				if(!vis[v]){
					vis[v] = true;
					que[tail ++] = v;
				}
			}
		}
		vis[u] = false;
	}
//	printf("! dis=%d\n",dis[vt]);
	if(dis[vt] == MX) return false;//如果汇点为MX那么没有最短路了
	return true;
}
void end(){
	int u, p, sum = MX;
	for(u = vt; u != vs; u = eg[eg[p].pre].v){//寻找最大流
		p = pre[u];
		sum = min(sum, eg[p].c-eg[p].f);
	}
	for(u = vt; u != vs; u = eg[eg[p].pre].v){//更改流量,并算出最小费用
		p = pre[u];
		eg[p].f += sum;
		eg[eg[p].pre].f -= sum;
		ans += sum * eg[p].w; 
	}
}
int main(){
	while(scanf("%d%d",&N,&K)!=EOF){
		memset(list,-1,sizeof(list));
		k=0;
		e=0;
		char s[20];
		for(int i=0;i<N;i++){
			scanf("%s",s);
			time[e++]=task[i].st=((s[0]-'0')*10+(s[1]-'0'))*3600+((s[3]-'0')*10+(s[4]-'0'))*60+((s[6]-'0')*10+(s[7]-'0'));
			scanf("%s",s);
			time[e++]=task[i].ed=((s[0]-'0')*10+(s[1]-'0'))*3600+((s[3]-'0')*10+(s[4]-'0'))*60+((s[6]-'0')*10+(s[7]-'0'));
			scanf("%d",&task[i].numb);
		}
		sort(time,time+e);
		int ee=1;
		memset(time1,-1,sizeof(time1));
		for(int i=0;i<e;i++){
			if(time1[time[i]]==-1){
				time1[time[i]]=ee++;
			}
		}
		for(int i=1;i<ee-1;i++){
			add(i,i+1,MX,0);
		}
		add(0,1,K,0);
		for(int i=0;i<N;i++){
			add(time1[task[i].st],time1[task[i].ed],1,-task[i].numb);
		}
	//	for(int i=0;i<k;i++){
	//		printf("! u=%d v=%d c=%d w=%d \n",eg[i].u,eg[i].v,eg[i].c,eg[i].w);
	//	}
		vs=0;vt=ee-1;
		ans = 0;
		while(spfa()) end();
		printf("%d\n",-ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值