题目大意:
Jill要乘火车去参加比赛。为了避免迟到,她要选择能最早到达的方案。
Jill换乘时间非常快,看做0.
输入:第一行场景数,每个场景由3部分组成
part1: 列举所有被铁路连接的城市的名称
第一行是城市数 1<C<100,接下来的C行是城市的名称
part2:描述当天所有会运行的火车。
第一行T表示有T班火车。T<1000
每班火车都由一个数字ti,其后跟随ti行描述。
ti表示会经过多少站。
ti后的每一行 站名和时间 在该时间该站可以上下车
part3:第三部分由三行表示。
第一行最早出发时间,第二行出发地,第三行目的地
要求输出出发地出发时间,目的地和到达时间
如果存在多种方式使到达时间最小,选出发时间最晚的那一个。
思考:
每个点包含时间、站名,除了以及存在的联通关系,该图是一个有向图
<u,v>存在的条件是 u,v站名相同&&u.time<=v.time
题目要求如果存在多种方式使到达时间最小,选出发时间最晚的那一个。
第一个前提是到达时间最早,第二个前提是出发时间最晚
因此比较适合逆着去寻路
把站名是目的地的点 按照 时间 从早到晚排序(从小到大) ---->如果不存在这样的点,显然不存在所需的路
从小到大依次处理:
dfs
寻找一条从目的地到出发地的路,如果有多条,选出发时间最大的那个。如果找到,结束循环。输出
否则换下一个出发点,重复上述过程
如果一直没有找到,则不存在这样的路。
因此我们需要建立一个逆序图
其实通过优化,实际上还有另一种思路:
其实这题可以看成最短路径问题
先从最早出发时间开始,使用dijkstra算法找到最早到达时间。(把到达时间看做权)
再从最早到达时间开始,逆序寻找最晚出发时间。(以出发时间为权)
参考博客:10039
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define MAX_CITY 200
using namespace std;
//边结构体
struct Edge
{
int y;
int d,a;
struct Edge *next;
Edge(int dst,int dep,int arr):y(dst),d(dep),a(arr),next(NULL) {;}
Edge () {;}
};
int nCity;
Edge* edges[MAX_CITY]; //正向图
Edge* reverse[MAX_CITY]; //逆向图
map <string,int> m; //城市与编号的映射
//分别向正向图与逆向图中加入边
void insert_edge (int nsc)
{
int nstation,time,last,lastTime;
int current,currentTime;
string city;
for (int i=0;i<nsc;i++)
{
cin >> nstation;
cin>>time>>city;
last = m[city];
lastTime = time;
for (int j=1;j<nstation;j++)
{
cin >> time >> city;
current = m[city];
Edge *newEdge = new Edge(current,lastTime,time);
newEdge->next = edges[last];
edges[last] = newEdge;
Edge *revEdge = new Edge (last,lastTime,time);
revEdge->next = reverse[current];
reverse[current] = revEdge;
last = current;
lastTime = time;
}
}
}
//通过最早出发时间计算最早到达时间
//使用dijkstra算法
int calDst (int src,int dst,int sTime)
{
//arrival表示到达某点的最早时间
int arrival[MAX_CITY],v,min;
bool intree[MAX_CITY];
for (int i=0;i<nCity;i++)
arrival[i] = INT_MAX;
memset (intree,false,sizeof(intree));
arrival[src] = sTime;
intree[src]= true;
v = src;
do
{
for (Edge* p=edges[v];p;p=p->next)
{
//只有出发时间 >= 到达v的时间才是有效的边
if (p->d>=arrival[v])
{
int y = p->y;
//当到达y的时间较小时更新arrival
if (p->a < arrival[y])
arrival[y] = p->a;
}
}
min = INT_MAX;
v = -1;
for (int i=0;i<nCity;i++)
if (!intree[i] && arrival[i]<min)
{
v = i;
min = arrival[i];
}
intree[v] = true;
}while (v!=-1);
return arrival[dst];
}
//利用上面算出的最早到达时间计算最晚出发时间
//注意这个函数用的是反向图
int calSrc (int dst,int src,int eTime)
{
//depture 表示从某点出发的最晚时间
int depture[MAX_CITY],v,max;
bool intree[MAX_CITY];
for (int i=0;i<nCity;i++)
depture[i] = -1;
memset (intree,false,sizeof(intree));
depture[dst] = eTime;
intree[dst]= true;
v = dst;
do
{
for (Edge* p=reverse[v];p;p=p->next)
{
if (p->a<=depture[v])
{
//和上面的函数相似
int y = p->y;
if (p->d > depture[y])
depture[y] = p->d;
}
}
max=0;
v = -1;
for (int i=0;i<nCity;i++)
if (!intree[i] && depture[i]>max)
{
v = i;
max = depture[i];
}
intree[v] = true;
}while (v!=-1);
return depture[src];
}
int main (void)
{
int testCase,nsc,src,dst,sTime,eTime,realSTime;
string str,srcStr,dstStr;
cin >> testCase;
for (int test = 1;test<=testCase;test++)
{
cin >> nCity;
m.clear();
memset (edges,NULL,sizeof(edges));
memset (reverse,NULL,sizeof(reverse));
for (int i=0;i<nCity;i++)
{
cin >> str;
m[str] = i;
}
cin >> nsc;
insert_edge (nsc);
cin >> sTime;
cin >> srcStr >> dstStr;
src = m[srcStr];
dst = m[dstStr];
eTime = calDst (src,dst,sTime);
cout <<"Scenario "<<test <<endl;
if (eTime<INT_MAX)
{
realSTime = calSrc (dst,src,eTime);
printf ("Departure %04d %s\n",realSTime,srcStr.c_str());
printf ("Arrival %04d %s\n",eTime,dstStr.c_str());
}
else
printf ("No connection\n");
cout << endl;
}
return 0;
}
自己的代码如下:
#include<iostream>
#include<stdio.h>
#include<vector>
#include<map>
#include<string>
#include<string.h>
using namespace std;
struct Edge {
int d, a, c;
Edge* next;
Edge(int dst, int depart, int arrive) {
c = dst;
d = depart;
a = arrive;
next = NULL;
}
Edge() { next = NULL; }
};
Edge* edges[110];
Edge* reverseEdges[110];
map<string, int> m;
const int INF = 1 << 20;
//建立正向图和逆向图
void create(int nTrain)
{
while (nTrain--)
{
int nstation, time, lastTime, currTime, lastCity, currCity;
string city;
cin >> nstation;
cin >> time >> city;
lastTime = time;
lastCity = m[city];
nstation--;
while (nstation--)
{
cin >> time >> city;
currTime = time;
currCity = m[city];
//正向图
Edge* e1 = new Edge(currCity, lastTime, currTime);
e1->next = edges[lastCity];
edges[lastCity] = e1;
//逆向图
Edge*e2 = new Edge(lastCity, lastTime, currTime);
e2->next = reverseEdges[currCity];
reverseEdges[currCity] = e2;
lastTime = currTime;
lastCity = currCity;
}
}
}
//如果找到了最小到达时间,返回到达时间,否则返回的是INF
int minArriveTime(int startTime, int src, int des, int ncity)
{
//arrive用来维护最早到达时间
int arrive[110], have[110];
//初始化arrive和have
for (int i = 0; i <= ncity; i++)
arrive[i] = INF;
memset(have, 0, sizeof(have));
//初始化到每一个点的最小到达时间
have[src] = 1;
arrive[src] = startTime;
for (Edge*p = edges[src]; p; p = p->next)
{
int city = p->c;
//只有到达的时间早于出发的时间才能更新
if(arrive[src]<=p->d)
arrive[city] = p->a;
}
int n = ncity - 1;
while (n--)
{
int min, next;
//找到arrive最小的那个
min = INF-1; next = 0;
for (int i = 1; i <= ncity; i++)
{
if (!have[i] && arrive[i] <= min)
{
min = arrive[i];
next = i;
}
}
//更新src到每一个城市的最小到达时间
for (Edge*p = edges[next]; p; p=p->next)
{
int city = p->c;
//只有到达时间早于出发时间
if (arrive[next] <= p->d)
{
if (arrive[city] > p->a)arrive[city] = p->a;
}
}
}
return arrive[des];
}
//逆向搜索,找到最大离开时间 Dijkstra
int maxLeaveTime(int arriveTime,int src, int des, int ncity)
{
//depart表示离开某点的最晚时间
int depart[110],have[110];
for (int i = 1; i <= ncity; i++)
depart[i] = -1;
memset(have, 0, sizeof(have));
have[des] = 1;
depart[des] = arriveTime;
for (Edge*p = reverseEdges[des]; p; p = p->next)
{
//最晚出发时间应该大于到达时间
if(depart[des]>=p->a)
depart[p->c] = p->d;
}
int num = ncity-1;
while (num--)
{
int max = 0, next = 0;
for (int i = 1; i <= ncity; i++)
{
if (!have[i] && depart[i] >= max)
{
max = depart[i];
next = i;
}
}
have[next] = 1;
for (Edge*p = reverseEdges[next]; p; p = p->next)
{
if (depart[next] >= p->a)
{
if (depart[p->c] < p->d)depart[p->c] = p->d;
}
}
}
return depart[src];
}
int main()
{
freopen( "c:\\data\\10039.txt", "r", stdin);
int scenarios, ncity, nTrain, startTime;
string source, destination;
cin >> scenarios;
for(int cas=1;cas<=scenarios;cas++)
{
cin >> ncity;
//为每个城市创建映射
for (int i = 1; i <= ncity; i++)
{
string cityname;
cin >> cityname;
m[cityname] = i;
}
//创建正向和逆向图
memset(edges, 0, sizeof(edges));
memset(reverseEdges, 0, sizeof(edges));
cin >> nTrain;
create(nTrain);
//从最早出发时间寻找最早到达时间,以到达时间为权
cin >> startTime >> source >> destination;
int minATime = minArriveTime(startTime, m[source], m[destination], ncity);
cout << "Scenario " << cas << endl;
if (minATime == INF)
cout << "No connection" << endl;
else
{
int maxLTime = maxLeaveTime(minATime, m[source], m[destination], ncity);
printf("Departure %04d %s\n", maxLTime, source.c_str());
printf("Arrival %04d %s\n", minATime, destination.c_str());
}
cout<<endl;
}
return 0;
}