2018年清华软院保研夏令营第4题:建高铁(堆优化双向Dijkstra)

题目

编号为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;
}

猜你喜欢

转载自blog.csdn.net/da_kao_la/article/details/82468781