2019安徽省程序设计竞赛 D自驾游 题解

从无编程比赛经验,程序设计艺术与方法作业所迫,采用方法十分普通,还请进来的大佬莫要笑话

目录

    • 原题
    • 题解
    • 源代码

原题

  • 描述
    P 省有 N 个城市,编号分别为 1…N,烦烦家住 1 号城市,N 号城市是省会。P省的交通非常发达,有 M 条有向的高速公路连接这 N 个城市,第 i 条高速公路(1<=i<=M)从城市 ui 连向城市 vi。这天,烦烦想自己开车从家前往省会城市游玩。烦烦是个做事很细致的人,为了有备无患,她决定同时开着 heroMap 和 amap 这两个不同的导航软件来帮助自
    己完成这次旅程。这两个导航软件内部采用了不同的算法,对于第 i 条高速公路(1<=i<=M),heromap 认为通过时间为 Pi 分钟,amap 则认为通过时间为Qi 分钟。这两个导航软件会根据自己的数据来计算从当前位置到目标位置所需
    的最短时间是多少,对于第 i 个城市(1<=i<=N),记 heromap 认为从 i 到 N的最短时间为 hero(i),记 amap 认为 i 到 N 的最短时间为 a(i)。烦烦开车途径某条高速公路(1<=i<=M)时,如果 heromap 认为从 ui 到 N 不应该走这条
    路,即 hero(vi)+Pi>hero(ui),则发出一次警告,同样的,如果 amap 认为从 ui 到 N 不应该走这条路,即 a(vi)+Qi>a(ui),也会发出一次警告。现在烦烦希望自己选择一条路径,使得受到的警告总数最少。请你编程解决这一问题。
  • 输入
    第一行是两个整数 N 和 M
    接下来 M 行,第 i 行有四个整数 ui, vi, Pi, Qi;分别表示第 i 条边的出发点和到达点编号,两个导航软件认为走过这条边所用的时间。
    2 <= N <= 10,000
    1 <= M <= 50,000
    1 <= ui, vi <= N
  • 输出
    输出从 1 走到 N 最少受到多少次警告
  • 样例输入
    5 7
    3 4 7 1
    1 3 2 20
    1 4 17 18
    4 5 25 3
    1 2 10 1
    3 5 4 14
    2 4 6 5
  • 样例输出
    1

题解

这道题本质上是求解图中两点的最短路径,根据题意,路径权值为警告次数,警告次数的可能取值为0, 1,2;
而警告次数与一个判断条件有关:hero(vi)+Pi>hero(ui),a(vi)+Qi>a(ui),即若通过ui先到vi再经vi到终点的最短路径的距离比从ui走最短路径到终点的距离大,则导航仪会进行警告。简单的说,如果你走了弯路,则警告。
这就将警告次数这个权值与到终点最短距离联系起来了。如果将每个点到终点的最短距离求解出来,那么这个问题就可以解决。
算出每个点到终点的距离可以采用在数据结构中学过的Dijkstra算法,Dijkstra算法是一个贪心算法,在求解到1到终点的距离的同时,路径上的每个点到起点点的距离都是最短距离。于是可以将hero(vi)+Pi>hero(ui)改写为dist(N)-dist(vi)+Pi>dist(N)-dist(ui),两边消去dist(N),原条件转换为-dist(vi)+Pi>-dist(ui)。
dist(i)为第1个结点到第i个结点的最短路径长度,在Dijkstra算法中每个dist(i)都可以被更新。

void update_dist(int map[][MAX_N], vector& dist)
{
	vector solved;
	for (int i = 1; i <= N; i++)
	{
		if (map[1][i] != 0) dist[i] = map[1][i];
		else dist[i] = endless;
	}
	for (int i = 0; i <= 1; i++) solved.push_back(true);
	for (int i = 2; i <= N; i++) solved.push_back(false);
	for (int i = 1; i <= N; i++)
	{
		int Minlen = endless, v = 0;
		for (int j = 1; j <= N; j++)
		{
			if (!solved[j] && dist[j] < Minlen)
			{
				Minlen = dist[j];
				v = j;
			}
		}
		solved[v] = true;
		for (int j = 1; j <= N; j++)
			if (map[v][j] != 0)
				if (dist[v] + map[v][j] < dist[j]) dist[j] = dist[v] + map[v][j];
	}

}

在两个导航软件的dist(i)都更新后,我们可以对Dijkstra算法进行微改,改为求解最短警告次数权值和,代码如下:

int get_min_warning(int heroMap[][MAX_N], int aMap[][MAX_N], vector&hero_dist, vector&a_dist)
{
	vector solved;
	vector warning(hero_dist.size());
	for (int i = 1; i <= N; i++)
	{
		if (heroMap[1][i] != 0)  warning[i] = 0;
		else warning[i] = endless;
	}
	for (int i = 0; i <= 1; i++) solved.push_back(true);
	for (int i = 2; i <= N; i++) solved.push_back(false);
	for (int i = 1; i <= N; i++)
	{
		int Minlen = endless, v = 0;
		for (int j = 1; j <= N; j++)
		{
			if (!solved[j] && warning[j] < Minlen)
			{
				Minlen = warning[j];
				v = j;
			}
		}
		solved[v] = true;
		for (int j = 1; j <= N; j++)
		{
			if (heroMap[v][j] != 0)
			{
				int hero_warning = (-hero_dist[j] + HeroMap[v][j]) > -hero_dist[v];
				int a_warning = (-a_dist[j] + aMap[v][j]) > -a_dist[v];
				if (warning[v] + hero_warning + a_warning < warning[j]) warning[j] = warning[v] + hero_warning + a_warning;
			}
		}
	}
	return warning[N];
}

源代码

#include
#include
using namespace std;

#define MAX_N 10001
#define endless 9999

int HeroMap[MAX_N][MAX_N], aMap[MAX_N][MAX_N], M, N;

void update_dist(int map[][MAX_N], vector& dist)
{
	vector solved;
	for (int i = 1; i <= N; i++)
	{
		if (map[1][i] != 0) dist[i] = map[1][i];
		else dist[i] = endless;
	}
	for (int i = 0; i <= 1; i++) solved.push_back(true);
	for (int i = 2; i <= N; i++) solved.push_back(false);
	for (int i = 1; i <= N; i++)
	{
		int Minlen = endless, v = 0;
		for (int j = 1; j <= N; j++)
		{
			if (!solved[j] && dist[j] < Minlen)
			{
				Minlen = dist[j];
				v = j;
			}
		}
		solved[v] = true;
		for (int j = 1; j <= N; j++)
			if (map[v][j] != 0)
				if (dist[v] + map[v][j] < dist[j]) dist[j] = dist[v] + map[v][j];
	}

}

int get_min_warning(int heroMap[][MAX_N], int aMap[][MAX_N], vector&hero_dist, vector&a_dist)
{
	vector solved;
	vector warning(hero_dist.size());
	for (int i = 1; i <= N; i++)
	{
		if (heroMap[1][i] != 0)  warning[i] = 0;
		else warning[i] = endless;
	}
	for (int i = 0; i <= 1; i++) solved.push_back(true);
	for (int i = 2; i <= N; i++) solved.push_back(false);
	for (int i = 1; i <= N; i++)
	{
		int Minlen = endless, v = 0;
		for (int j = 1; j <= N; j++)
		{
			if (!solved[j] && warning[j] < Minlen)
			{
				Minlen = warning[j];
				v = j;
			}
		}
		solved[v] = true;
		for (int j = 1; j <= N; j++)
		{
			if (heroMap[v][j] != 0)
			{
				int hero_warning = (-hero_dist[j] + HeroMap[v][j]) > -hero_dist[v];
				int a_warning = (-a_dist[j] + aMap[v][j]) > -a_dist[v];
				if (warning[v] + hero_warning + a_warning < warning[j]) warning[j] = warning[v] + hero_warning + a_warning;
			}
		}
	}
	return warning[N];
}

int main()
{
	cin >> N >> M;
	vector dist_hero(N + 1), dist_a(N + 1);
	for (int i = 1; i <= M; i++)
	{
		int x, y;
		cin >> x >> y;
		cin >> HeroMap[x][y] >> aMap[x][y];
	}
	update_dist(HeroMap, dist_hero);
	update_dist(aMap, dist_a);
	cout << get_min_warning(HeroMap, aMap, dist_hero, dist_a) << endl;
	return 0;
}

未测试数据较大时的情况(没有找到OJ,如果有hxd有这道题的OJ,希望能在评论区打出来)。理论上O(n²)的算法这题在2s内应该可以解决。

你可能感兴趣的:(编程题解,数据结构)