原题下载:http://icpc.baylor.edu/download/worldfinals/problems/icpc2013.pdf

题目翻译:

试题来源

  ACM/ICPC World Finals 2013 C

问题描述

  你现在要为智能汽车负责设计一种很高级的集中管理系统。目的是利用全球信息指导早上从郊区赶往市中心的乘客如何在避免交通堵塞的情况下更好地到达城市中心。
  不幸的是,乘客们对城市非常了解,而且都相当自私,你不能简单地甩给他们一条比平常走的还要长的路径(否则他们会直接无视你的指导),所以只能说服他们改走另外一条长度相同的路径。
  城市的道路网络由路口和连接它们的双向道路组成,通过不同的道路所需时间是不同的。所有乘客都会从各自的路口出发,当然不同的乘客出发的路口可能不同。但是所有乘客都会在同一个地点结束他们的旅程,那就是位于路口1的市中心。如果两个乘客试图在相同的时间,从同一方向,开始沿着相同的道路移动,就会出现堵塞——这是你必须避免种情况的。但是,两名乘客可以在同一时间通过同一个路口,或者在不同时间从同一条道路沿同一方向出发。
  请确定最多能有多少人能够在没有堵塞的前提下开车前往市中心。注意,所有乘客刚好在同一时间从他们所在路口出发,而且乘客只会走能够最快到达路口1的路径。
ACM - ICPC World Finals 2013 C Surely You Congest-LMLPHP
  在图C.1中,汽车图案标记了每名乘客最开始所在的路口,其中一辆车已经在市中心了。而路口4的车辆,可以走通过路口3的红色的点线,或者通过路口2的蓝色虚线。但是剩下的两辆车不可能在避免堵塞的前提下前往市中心。所以,在避免堵塞的情况下,最多只有3辆车能够抵达位于路口1的市中心。

输入格式

  输入只会包含一组数据。第一行是三个正整数n,m,c,其中n(1≤n≤25000)是路口的个数,m(0≤m≤50000)是连接路口的道路的个数,而c(1≤c≤1000)则是乘客的个数。接下来m行,每行有三个正整数x,y,t来描述一条道路,x,y是该道路连接的两个不同的路口,而t,是开车通过这个道路的时间(两个方向的时间一样)。所有的路口都能够抵达市中心。最后一行的c个数,分别代表了c个乘客出发的路口。

输出格式

  一个整数,表示在没有堵塞的情况下最多有多少乘客能够抵达市中心。

样例输入

3 3 2
1 2 42
2 3 1
2 3 1
2 3

样例输出

2

样例输入

4 4 5
1 2 5
1 3 4
4 2 5
4 3 6
4 4 4 4 1

样例输出

3

题目大意:

有n个城市和m条双向道路,每条道路的同一方向在同一时刻只能通过一辆车,现在有C辆车分布在这些城市中,他们想要到1号城市,并且一定要走最短路,问最多能有多少辆车可以按要求到达(不能被堵在半道上)

思路分析:

这道题十分类似于之前写过的SGU185,要求最短路的条数。所以一开始我们一1号城市为起点做一遍单源最短路,求出第 i 号城市到1 号城市的举例dist[i],并将所有不是在最短路上的边删去,然后准备求最大流。注意到拥堵只有可能发生在两辆车的起点具有相同的dist值的时候,所以我们可以按照dist值对车的起点排序,然后每次将相同dist值的车辆起点与源点相连,原来的删边之后的图照拷过去(注意到删边之后原来的无向图就变成了一棵有向树),然后求最大流即可

算法过程:

1、读入并建图,无向边按照两条有向边处理。

2、求所有点到1的最短路,使用SPFA或Dijkstra均可

3、将所有不可能在最短路上的边删掉(打上一个删除标记即可),剩下的图记为图M

4、读入c 辆车的起点,并按照这些店的dist值排序

5、将图M拷贝到当前的残量网络中(边的容量为1),新建源点并向dist值相同的起点连边,求一遍最大流,将最大流累加到答案中

6、重复第5步直至处理完所有的起点,输出答案

复杂度分析:

本题的理论渐进复杂度比较大,但事实上并没有被卡的那么死,建图的复杂度是O(m),最短路、删边复杂度O(m),第5步要做c次,它的复杂度是\(O(c*(m+n^{2}m))\),总渐进复杂度就是\(O(c*(m+n^{2}m))\)

参考代码:

 //date 20140122
#include <cstdio>
#include <cstring>
#include <algorithm> using namespace std; const int maxn = ;
const int maxm = ;
const int maxc = ;
const int INF = 0x7FFFFFFF; inline int getint()
{
int ans (); char w = getchar();
while(w < '' || w > '')w = getchar();
while('' <= w && w <= '')
{
ans = ans * + w - '';
w = getchar();
}
return ans;
} inline int min(int a, int b){return a < b ? a : b;}
inline int max(int a, int b){return a > b ? a : b;} int n, m, c; struct edge
{
int u, v, w, c, next;
}Etmp[maxm], E[maxm];
int deled[maxm];
int atmp[maxn], a[maxn];
int nedgetmp, nedge; inline void addtmp(int u, int v, int w)
{
Etmp[++nedgetmp].v = v;
Etmp[nedgetmp].u = u;
Etmp[nedgetmp].w = w;
Etmp[nedgetmp].next = atmp[u];
atmp[u] = nedgetmp;
} inline void add(int u, int v, int c)
{
E[++nedge].v = v;
E[nedge].u = u;
E[nedge].c = c;
E[nedge].next = a[u];
a[u] = nedge;
} int dis[maxn]; inline void SPFA()
{
static int q[maxn];
static int inq[maxn];
int l = , r = ;
memset(dis, 0x7F, sizeof dis);
memset(inq, , sizeof inq);
dis[] = ; inq[] = ; q[] = ;
while(l < r)
{
int x = q[(++l) % maxn];
for(int i = atmp[x]; i; i = Etmp[i].next)
if(dis[Etmp[i].v] > dis[x] + Etmp[i].w)
{
dis[Etmp[i].v] = dis[x] + Etmp[i].w;
if(!inq[Etmp[i].v])
{
inq[Etmp[i].v] = ;
q[(++r) % maxn] = Etmp[i].v;
}
}
inq[x] = ;
}
for(int i = ; i <= n; ++i)printf("%d ", dis[i]);
printf("\n");
} inline void deledge()
{
memset(deled, , sizeof deled);
for(int i = ; i <= nedgetmp; ++i)
if(dis[Etmp[i].u] + Etmp[i].w > dis[Etmp[i].v])deled[i] = ;
} int s, t;
int lab[maxn], now[maxn]; inline int label()
{
static int q[maxn];
memset(lab, 0xFF, sizeof lab);
int l = , r = ;
q[] = s; lab[s] = ;
while(l < r)
{
int x = q[++l];
for(int i = a[x]; i; i = E[i].next)
if(E[i].c > && lab[E[i].v] == -)
{
lab[E[i].v] = lab[x] + ;
q[++r] = E[i].v;
}
}
// for(int i = 1; i <= s; ++i)
// fprintf(stderr, "%d ", lab[i]);
// fprintf(stderr, "\n");
return lab[t] != -;
} int Dinic(int v, int f)
{
if(v == t)return f;
int w, res = ;
for(int i = now[v]; i; i = now[v] = E[i].next)
if((E[i].c > ) && (f > ) && (lab[E[i].v] == lab[v] + ) && (w = Dinic(E[i].v, min(f, E[i].c))))
{
res += w;
E[i].c -= w;
E[i ^ ].c += w;
f -= w;
if(f == )break;
}
return res;
} inline int max_flow()
{
int ans = ;
while(label())
{
for(int i = ; i <= s; ++i)now[i] = a[i];
ans += Dinic(s, INF);
}
// printf("%d\n", ans);
return ans;
} int tar[maxc]; inline bool cmptar(int a, int b)
{
return dis[a] < dis[b];
} inline void rebuild(int l, int r)
{
nedge = ;
memset(a, , sizeof a);
for(int i = ; i <= nedgetmp; ++i)
if(!deled[i]){add(Etmp[i].v, Etmp[i].u, ); add(Etmp[i].u, Etmp[i]., );}
for(int i = l; i <= r; ++i) {add(s, tar[i], ); add(tar[i], s, );}
// for(int i = 2; i <= nedge; ++i)fprintf(stderr, "%d %d %d %d %d\n", i, E[i].u, E[i].v, E[i].c, E[i].next);
} inline int solve()
{
int ans = , now = dis[tar[]], l = , r = ;
for(int i = ; i <= c + ; ++i)
{
if(dis[tar[i]] == now)++r;
else
{
if(l == r)++ans;
else
{
rebuild(l, r);
ans += max_flow();
}
now = dis[tar[i]];
l = r = i;
}
}
return ans;
} int main()
{
freopen("congest.in", "r", stdin);
freopen("congest.out", "w", stdout); n = getint(); m = getint(); c = getint();
nedgetmp = ; nedge = ;
s = n + ; t = ;
for(int i = ; i <= m; ++i)
{
int x, y, t;
x = getint(); y = getint(); t = getint();
addtmp(x, y, t); addtmp(y, x, t);
}
SPFA();
deledge(); for(int i = ; i <= c; ++i)tar[i] = getint();
sort(tar + , tar + c + , cmptar); int ans = solve();
printf("%d\n", ans);
return ;
}

需要注意的问题:

1、之所以每次都需要重新建图,是因为跑完最大流之后我们剩下的图是上一次网络流的残量网络,图的结构已经发生了变化,所以必须重建

2、如果具有某一dist值的起点只有一个,那么它不会和其它车辆发生冲突,也自然无需重建图、求最大流了,直接将答案+1即可

3、从渐进复杂度角度来看这题肯定超时,所以无论用哪种网络流算法一定要将它优化到底(实践证明SAP的效果远不如Dinic)

05-04 00:57