【图论】POJ-2253 Frogger

版权声明:Johnson https://blog.csdn.net/m0_38055352/article/details/88920038

这段时间要沉迷刷题一段时间了,就让CSDN陪我一起吧!

一、题目大意

题目的大致意思如下:处于某一个石头上的青蛙(男)想去找另一个石头上的青蛙(女),但由于两人的直接距离比较远,青蛙(男)无法直接跳过去,那么就需要经过一下中间的石头,多跳几次,来到达青蛙(女)所在的石头。我们把青蛙距离定义成最小的最长路,两个石头的青蛙距离就是对于两个石头之间的所有路径来说,它们的最长段中的最小值。可以给大家举一个例子。
在这里插入图片描述
比如对于这个图(有点丑,大家凑合看),顶点1和顶点2的青蛙距离就是6,怎么算的呢?从1到2有两个路径,1-3-4-2和1-5-2,在第一条路径中,其最长段为4-2,也就是6;在第二条路径中,其最长段为5-2,也就是8,两者的最小值就是6,也就是顶点1和顶点2的青蛙距离。相信看到这里,题目的意思应该很清楚了。

二、解题思路及AC代码

解这道题的思路就是用Dijkstra的变形,即将Dijkstra中的dist数组,dist[i]定义为从源到顶点 i 所有路径中最长段的最小值,那这样就需要重新考虑其选择和更新的问题,也就对应Dijkstra的选最小的dist和更新dist。

首先考虑更新,这样的一个dist数组要怎么更新呢?我们还是同以前一样,先假设每一步可以固定求解出一个dist数组中的值,那么设这个值为dist[x],那么我们接下来只需要对所有的顶点进行遍历,看哪一个顶点满足:dist[j] > max(dist[x], edges[x][j]),这个式子的意思就是说,因为x已经是确定的了,代表从源到顶点x的所有路径最长段的最小值,所以对于dist[j]的最长段最小值,要么就是dist[x],要么就是x到j的那条边的权值。(上述的j是遍历变量)

然后考虑选择。更新的问题已经解决了,就是说,我们如果已知dist[x]代表确定的最长段最小值,那么所有的dist都会得到正确的更新。现在就只需要考虑怎样得到最开始的dist[x]了,对于Dijkstra来说,最开始肯定是从dist[s]=0来开始更新的,那么我们只需要选择dist最小的作为开始的dist[x]就可以了,而且Dijkstra保证出现结果的单调性,只有这样我们才能进行后续的求解。

思路捋清楚啦! 下面给出代码的实现(Dijkstra和其heap优化):
Dijkstra:

#include <iostream>
#include <cmath>
#define MAXN 202
#define INF 10000000
using namespace std;

typedef pair <double, double> pp;
pp V[MAXN];
double edges[MAXN][MAXN];
double dist[MAXN];
bool vis[MAXN];

int n;

double Distance(double x1, double y1, double x2, double y2) {
	return sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
}

double Max(double a, double b) {
	return a > b ? a : b;
}

double Dijkstra(int s) {
	for (int i = 0; i < MAXN; i++) {
		dist[i] = INF;
		vis[i] = false;
	}

	dist[s] = 0;
	for (int i = 1; i <= n; i++) {
		int min = INF;	int x = -1;
		for (int j = 1; j <= n; j++) {
			if (!vis[j] && dist[j] < min) {
				min = dist[x = j];
			}
		}

		vis[x] = true;
		for (int j = 1; j <= n; j++) {
			if (!vis[j] && dist[j] > Max(dist[x], edges[x][j])) {
				dist[j] = Max(dist[x], edges[x][j]);
			}
		}
	}

	return dist[2];
}

int main()
{
	int Case = 0;
	while (scanf("%d", &n) != EOF) {
		Case++;
		if (!n) break;
		for (int i = 1; i <= n; i++) {
			cin >> V[i].first >> V[i].second;
		}
		
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				double weight = Distance(V[i].first, V[i].second, V[j].first, V[j].second);
				edges[i][j] = edges[j][i] = weight;
			}
		}

		printf("Scenario #%d\n", Case);
		printf("Frog Distance = %.3f\n\n", Dijkstra(1));
	}

    return 0;
}

Dijkstra + heap:

#include <iostream>
#include <cmath>
#include <queue>
#include <vector>
#define MAXN 202
#define INF 10000000
using namespace std;

int N;

typedef pair<int, double> pp;
typedef pair<double, double> ipp;

double edges[MAXN][MAXN];
double dist[MAXN];
bool vis[MAXN];

ipp V[MAXN];

struct cmp {
	bool operator ()(pp a, pp b) {
		return a.second > b.second;
	}
};

double Distance(double x1, double y1, double x2, double y2) {
	return sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
}

double Max(double a, double b) {
	return a > b ? a : b;
}

double Dijkstra(int s) {
	for (int i = 0; i < MAXN; i++) {
		dist[i] = INF;
		vis[i] = false;
	}
	priority_queue<pp, vector<pp>, cmp> pq;
	pq.push(make_pair(s, 0));
	dist[s] = 0;
	while (!pq.empty()) {
		int v = pq.top().first;
		double d = pq.top().second;
		pq.pop();

		if (vis[v]) continue;

		vis[v] = true;

		for (int i = 1; i <= N; i++) {
			if (!vis[i] && dist[i] > Max(dist[v], edges[v][i])) {
				dist[i] = Max(dist[v], edges[v][i]);
				pq.push(make_pair(i, dist[i]));
			}
		}
	}

	return dist[2];
}

int main()
{
	int Case = 0;
	while (scanf("%d", &N)) {
		Case++;
		if (!N) break;

		for (int i = 1; i <= N; i++) {
			cin >> V[i].first >> V[i].second;
		}

		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				double weight = Distance(V[i].first, V[i].second, V[j].first, V[j].second);
				edges[i][j] = edges[j][i] = weight;
			}
		}

		printf("Scenario #%d\n", Case);
		printf("Frog Distance = %.3f\n\n", Dijkstra(1));
	}

    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_38055352/article/details/88920038
今日推荐