LA 3645 Objective: Berlin 最大流

传送门:LA 3645 Objective: Berlin

题目大意:给出了n个城市和m条航线。给出每条航线的出发地、目的地、座位数、起飞时间、到达时间(用HHMM方式表达),再给出起点城市A,终点城市B,以及最完到达B的时间T(用HHMM方式表示)。问在规定时间内,最多能有多少人能从A飞到B。其中转乘需要另外耗费至少30分钟,即如果从一架飞机下来后,要等至少30分钟才能继续坐飞机。

题目分析:

时序模型。

对于每一条航线,我们用(u,v,Cap,Leave_Time,Arrive_Time)表示。

用链式前向星保存所有到达城市 u 的航线和所有从城市 u 出发的航线。

首先对于起点出发的所有航线 i 建边(s,i,oo),对于所有在给定时间到达终点的航线建边(i + m,t,oo)。

然后,对于第 i 条航线,建边(i,i + m,cap),同时按照之前说的链式前向星保存航线信息。接下来,枚举所有从城市 u 出发的航线 i 以及到达城市 u 的航线 j,如果leave_time - arrive_time >= 30 则建边(i,j + m,oo)。

然后跑一遍最大流即可。

代码如下:


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define clear(A, X) memset(A, X, sizeof A)
#define copy(A, B) memcpy(A, B, sizeof A)
using namespace std;

const int maxE = 1000000;
const int maxN = 10005;
const int maxM = 160;
const int maxQ = 1000000;
const int oo = 0x3f3f3f3f;

struct Edge{
	int v, c, n;
}edge[maxE];//边组

struct Node{
	union{
		int Leave;//离开
		int Arrive;//到达
	}LA;//转换
	int i;//航线编号
	int n;//下一个节点
 }leave[maxN], arrive[maxN];//离开 以及 到达的航线信息组

int adj[maxN], Adj_Leave[maxM], Adj_Arrive[maxM], cntE, Cnt_Leave, Cnt_Arrive;//表头们^ _ ^
int Q[maxQ], head, tail;//队列
int d[maxN], cur[maxN], pre[maxN], num[maxN];
int s, t, nv;//s:源点,t:汇点,nv:编号修改的上限
int n, m, End_Time, top;//End_Time是最迟到达的时间,top是保存当前已经编号的城市数。
char city[maxM][10];//一维是城市编号

int Get_Time(int x){//时间转换
	return x / 100 * 60 + x % 100;
}

void addedge(int u, int v, int c){//添加边
	edge[cntE].v = v;
	edge[cntE].c = c;
	edge[cntE].n = adj[u];
	adj[u] = cntE++;
	
	edge[cntE].v = u;
	edge[cntE].c = 0;
	edge[cntE].n = adj[v];
	adj[v] = cntE++;
}

void Add_Time(int id, int u, int v, int Leave_Time, int Arrive_Time){//添加航线信息
	//添加离开城市u的航线的信息
	leave[Cnt_Leave].i = id;//航线编号
	leave[Cnt_Leave].LA.Leave = Get_Time(Leave_Time);//离开时间
	leave[Cnt_Leave].n = Adj_Leave[u];
	Adj_Leave[u] = Cnt_Leave++;
	
	//添加到达城市v的航线的信息
	arrive[Cnt_Arrive].i = id;//航线编号
	arrive[Cnt_Arrive].LA.Arrive = Get_Time(Arrive_Time);//到达时间
	arrive[Cnt_Arrive].n = Adj_Arrive[v];
	Adj_Arrive[v] = Cnt_Arrive++;
}

void rev_bfs(){
	clear(num, 0);
	clear(d, -1);
	d[t] = 0;
	num[0] = 1;
	head = tail = 0;
	Q[tail++] = t;

	while(head != tail){
		int u = Q[head++];
		for(int i = adj[u]; ~i; i = edge[i].n){
			int v = edge[i].v;
			if(~d[v]) continue;
			d[v] = d[u] + 1;
			Q[tail++] = v;
			num[d[v]]++;
		}
	}
}
int ISAP(){
	copy(cur, adj);
	rev_bfs();
	int flow = 0, u = pre[s] = s, i;

	while(d[s] < nv){
		if(u == t){
			int f = oo, neck;
			for(i = s; i != t; i = edge[cur[i]].v){
				if(f > edge[cur[i]].c){
					f = edge[cur[i]].c;
					neck = i;
				}
			}
			for(i = s; i != t; i = edge[cur[i]].v){
				edge[cur[i]].c -= f;
				edge[cur[i] ^ 1].c += f;
			}
			flow += f;
			u = neck;
		}
		for(i = cur[u]; ~i; i = edge[i].n) if(d[edge[i].v] + 1 == d[u] && edge[i].c) break;
		if(~i){
			cur[u] = i;
			pre[edge[i].v] = u;
			u = edge[i].v;
		}
		else{
			if(0 == (--num[d[u]])) break;
			int mind = nv;
			for(i = adj[u]; ~i; i = edge[i].n){
				if(edge[i].c && mind > d[edge[i].v]){
					cur[u] = i;
					mind = d[edge[i].v];
				}
			}
			d[u] = mind + 1;
			num[d[u]]++;
			u = pre[u];
		}
	}
	
	return flow;
}
void init(){//初始化
	clear(adj, -1);
	clear(Adj_Leave, -1);
	clear(Adj_Arrive, -1);
	cntE = Cnt_Leave = Cnt_Arrive = 0;
}
int check(char *str){//读取城市编号
	for(int i = 0; i < top; ++i) if(!strcmp(str, city[i])) return i;//属于当前已编号的城市
	strcpy(city[top++], str);//还未编过号,添加为新的编号
	return top - 1;//返回新编号
}
void work(){
	int Cap, Leave_Time, Arrive_Time;//容量,离开时间,到达时间
	char s1[10], s2[10];
	init();
	top = 2;//把起点和终点先标号
	scanf("%s%s", city[0], city[1]);//起点,终点
	scanf("%d", &End_Time);//最迟的时间
	scanf("%d", &m);
	s = m << 1; t = m << 1 | 1; nv = t + 1;//s = m * 2; t = m * 2 + 1'
	for(int i = 0; i < m; ++ i){
		scanf("%s%s%d%d%d", s1, s2, &Cap, &Leave_Time, &Arrive_Time);
		if(Arrive_Time > End_Time) continue;//到达时间超过End_Time则不在条件范围之内,忽略
		int u = check(s1);
		int v = check(s2);
		if(0 == u) addedge(s, i, oo);//从源点拉一条容量为oo到起点的边
		if(1 == v) addedge(i + m, t, oo);//从终点拉一条容量为oo到汇点的边
		Add_Time(i, u, v, Leave_Time, Arrive_Time);//添加信息
		addedge(i, i + m, Cap);//为每个航线建边
	}
	for(int u = 0; u < top; ++ u){//枚举每个城市
		for(int i = Adj_Arrive[u]; ~i; i = arrive[i].n){//枚举每个到u的航线
			int X = arrive[i].i, Arrive = arrive[i].LA.Arrive;
			for(int j = Adj_Leave[u]; ~j; j = leave[j].n){//枚举每个离开u的航线
				int Y = leave[j].i, Leave = leave[j].LA.Leave;
				if(Leave - Arrive >= 30 && Leave <= End_Time){
					//如果从一班飞机下来后来得及到达下一班,建边
					addedge(X + m, Y, oo);
				}
			}
		}
	}
	printf("%d\n", ISAP());//最大流就是要求的结果
}
int main(){
	while(~scanf("%d", &n)) work();
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值