题目大意:给出了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;
}