题目
编号为1到N的N个城市间有M条铁路,通过每条铁路花费时间T_i,为了减少从城市1到N的时间,现在决定将其中一条铁路改建成高铁,改建后通过该条铁路的时间将减半(向下取整数),求改建哪条铁路可以使得改建后从城市1到城市N的时间最短
输入:第一行N和M
接下来M行,每行A_i, B_i, T_i分别是第i条铁路的起点和终点,以及时间
输出:改建的铁路编号以及改建后从1到N的时间
注意:铁路是双向的,保证解唯一
80%数据N,M<5000,100%数据N,M<1000000
--------------------------------------------------------------------------------
思路
从节点1和节点n分别做一次Dijkstra求从节点1出发的到每个点的最短路(保存在d1里)和从节点n出发到每个点的最短路(保存在d2里)。然后遍历m条边,建高铁后从节点1到节点n的最短路为min(d1[u] + d2[v] + len[u,v])
经tydety97指正,d1[u]的路径有可能包含边(u,v),d2[v]的路径也有可能包含边(u,v),因此最短路的表达式应作
min(min(d1[u] + d2[v], d1[v] + d2[u]) + len[u,v])
数据规模显示是稀疏图,因此用堆优化的Dijkstra,相应的采用邻接表的数据结构(本来邻接矩阵就会爆内存——vs显示数组过大)存储路网信息,复杂度为O(mlogn + m) = O(mlogn)
--------------------------------------------------------------------------------
代码
主程序
// 用堆优化的Dijkstra算法算两遍,分别从源点和汇点开始算,
// 算得d1数组记录所有点到源点的距离,d2数组记录所有点到汇点的距离
// 遍历m条边,ans = min(min(d1[u] + d2[v], d1[v] + d2[u]) + len[u,v])
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
const int NMAX = 1000005, INF = 0x3f3f3f3f;
struct toEdge {
int v,t;
toEdge(int vv, int tt): v(vv), t(tt) {}
toEdge(void) {}
};
struct cmp {
bool operator() (const toEdge & a, const toEdge & b)
{
return a.t > b.t;
}
};
struct Edge {
int u, v, t;
Edge(int uu, int vv, int tt): u(uu), v(vv), t(tt) {}
Edge(void){}
}edges[NMAX];
int n, m;
std::vector<toEdge> E[NMAX]; // 邻接表
int d[NMAX] = {};
int vis[NMAX] = {};
int d1[NMAX] = {}; // 从节点1出发到各点的最短路
int d2[NMAX] = {}; // 从节点2出发到各点的最短路
void dijkstra(int src) // 用Dijkstra算法求从src出发到各点的最短路
{
int i, v, t;
std::priority_queue<toEdge, std::vector<toEdge>, cmp> q;
memset(vis, 0, sizeof(vis));
memset(d, 0x3f, sizeof(d));
vis[src] = 1;
d[src] = 0;
for (i = 0; i < E[src].size(); i++)
{
q.push(toEdge(E[src].at(i).v, E[src].at(i).t));
}
while (!q.empty())
{
v = q.top().v;
while (vis[v])
{
q.pop();
if (q.empty())
{
return;
}
v = q.top().v;
}
vis[v] = 1;
t = q.top().t;
d[v] = t;
for (i = 0; i < E[v].size(); i++)
{
if (!vis[E[v].at(i).v] && d[E[v].at(i).v] > E[v].at(i).t + t)
{
q.push(toEdge(E[v].at(i).v, E[v].at(i).t + t));
}
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("xlySE18_04.txt", "r", stdin);
#endif
scanf("%d%d", &n, &m);
int i = 0, u, v, t, minv = INF, minid = -1;
for (i=0; i<m; i++)
{
scanf("%d%d%d", &u, &v, &t);
E[u].push_back(toEdge(v,t));
E[v].push_back(toEdge(u,t));
edges[i] = Edge(u,v,t);
}
dijkstra(1);
memcpy(d1, d, sizeof(d));
dijkstra(n);
memcpy(d2, d, sizeof(d));
for (i=0; i<m; i++)
{
u = edges[i].u;
v = edges[i].v;
t = edges[i].t;
if (t/2 + d1[u] + d2[v] < minv) // 取std::min(d1[u] + d2[v], d1[v] + d2[u])
{
minv = t/2 + d1[u] + d2[v];
minid = i + 1;
}
else if (t/2 + d1[v] + d2[u] < minv)
{
minv = t/2 + d1[v] + d2[u];
minid = i + 1;
}
}
printf("%d %d", minid, minv);
return 0;
}
测试程序(随机生成无向图)
// 生成xlySE18_04.cpp的测试用例:随机无向图
#include<cstdio>
#include<cstdlib>
#include<ctime>
const int NMAX = 10005, MMAX = 100005;
int mat[NMAX][NMAX] = {}; // 邻接矩阵
int U[MMAX] = {}, V[MMAX] = {}, C[MMAX] = {}; // 边数组
int main()
{
srand(time(NULL));
int n = 100, m = 500, cmax = 100, i, u, v, c;
// 先生成一个包括所有节点的环,保证最短路一定存在
for (i=0; i<n-1; i++)
{
c = rand() % cmax + 1;
mat[i+1][i+2] = c;
mat[i+2][i+1] = c;
U[i] = i+1;
V[i] = i+2;
C[i] = c;
}
c = rand() % cmax + 1;
mat[n][1] = c;
mat[1][n] = c;
U[n-1] = n;
V[n-1] = 1;
C[n-1] = c;
// 再随机增添道路
for (i = n; i < m; i++)
{
c = rand() % cmax + 1;
do {
u = rand() % n + 1;
v = rand() % n + 1;
}
while (u == v || mat[u][v]);
mat[u][v] = c;
mat[v][u] = c;
U[i] = u;
V[i] = v;
C[i] = c;
}
FILE * fp = fopen("xlySE18_04.txt", "w");
fprintf(fp, "%d %d\n", n, m);
for (i=0; i<m; i++)
{
fprintf(fp, "%d %d %d\n", U[i], V[i], C[i]);
}
fclose(fp);
}
对拍程序(暴力遍历每一条边做一次Dijkstra)
// xlySE18_04.cpp的对拍程序:暴力枚举m条边将之减半求Dijkstra
#include<cstdio>
#include<queue>
const int NMAX = 1000005, INF = 0x3f3f3f3f;
struct toEdge {
int v,t;
toEdge(int vv, int tt): v(vv), t(tt) {}
toEdge(void) {}
};
struct cmp {
bool operator() (const toEdge & a, const toEdge & b)
{
return a.t > b.t;
}
};
int n, m;
std::vector<toEdge> E[NMAX]; // 邻接表
int vis[NMAX] = {}; // 节点i是否被访问过
int d[NMAX] = {}; // 从节点src出发到各点的最短路
int dijkstra(int src, int dst)// 用Dijkstra算法求从src出发到dst的最短路
{
int i, v, t;
std::priority_queue<toEdge, std::vector<toEdge>, cmp> q;
memset(vis, 0, sizeof(vis));
memset(d, 0x3f, sizeof(d));
vis[src] = 1;
d[src] = 0;
for (i = 0; i < E[src].size(); i++)
{
q.push(toEdge(E[src].at(i).v, E[src].at(i).t));
}
while (!q.empty())
{
v = q.top().v;
while (vis[v])
{
q.pop();
if (q.empty())
{
return -1; // 返回-1表示src与dst不连通
}
v = q.top().v;
}
vis[v] = 1;
t = q.top().t;
d[v] = t;
if (v == dst)
{
return t;
}
for (i = 0; i < E[v].size(); i++)
{
if (!vis[E[v].at(i).v] && d[E[v].at(i).v] > E[v].at(i).t + t)
{
q.push(toEdge(E[v].at(i).v, E[v].at(i).t + t));
}
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("xlySE18_04.txt", "r", stdin);
#endif
scanf("%d%d", &n, &m);
int i = 0, j, u, v, t, pret, uid, minv = INF, minid = -1;
for (i=0; i<m; i++)
{
scanf("%d%d%d", &u, &v, &t);
E[u].push_back(toEdge(v,t));
E[v].push_back(toEdge(u,t));
}
for (u=0; u<n; u++)
{
for (j=0; j<E[u].size(); j++)
{
v = E[u].at(j).v;
pret = E[u].at(j).t;
E[u].at(j).t = pret/2;
for (i=0; i<E[v].size(); i++)
{
if (E[v].at(i).v == u)
{
E[v].at(i).t = pret/2;
uid = i;
break;
}
}
minv = std::min(dijkstra(1, n), minv);
E[u].at(j).t = pret;
E[v].at(uid).t = pret;
}
}
printf("%d", minv);
return 0;
}