POJ3037 滑雪(单源最短路径)

给定一个M*N的网格,已知在每个网格中的点可以向上下左右四个方向移动一个单位,每个点都有一个高度值,从每个点开始移动时存在一个速度值,从A点移动到B点,则此时B点的速度为"A的速度*2^(A的高度值-B的高度值)",而A点移动到B点所用的时间则是A点开始移动的速度值的倒数。提供网格的长和宽,每个点的高度,以及在左上角的点的出发速度,问从左上角的点到右下角的点最少需要多少时间。

由于2^(h1-h2)*2^(h2-h3)=2^(h1-h3),因此其实从某个点出发的速度可以由第一个点的速度和高度差直接算出来。每个点可以扩展出上下左右四条边。以这些为条件构建一个图,用SPFA算法以左上角的点为源点求单源最短路径,就可以得到右下角的点的最短时间了。

#include 
#include 
#include 
#include 
#include 
using namespace std;

const int N = 105;
const double MAX = 999999999999;

int m, n;
double speed[N][N];
int height[N][N];
double mindis[N][N];
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};
int quex[N * N], quey[N * N], front, rear;
bool inque[N][N];

void spfa()
{
	for (int i = 0; i < m; ++i)
	{
		for (int j = 0; j < n; ++j)
		{
			mindis[i][j] = MAX;
			inque[i][j] = false;
		}
	}
	front = rear = 0;
	mindis[0][0] = 0.0;
	inque[0][0] = true;
	quex[rear] = 0;
	quey[rear++] = 0;
	while (front != rear)
	{
		int prex = quex[front];
		int prey = quey[front];
		inque[prex][prey] = false;
		for (int i = 0; i < 4; ++i)
		{
			int tx = prex + dx[i];
			int ty = prey + dy[i];
			if (tx < 0 || ty < 0 || tx >= m || ty >= n) continue;
			if (mindis[prex][prey] + speed[prex][prey] < mindis[tx][ty])
			{
				mindis[tx][ty] = mindis[prex][prey] + speed[prex][prey];
				if (!inque[tx][ty])
				{
					inque[tx][ty] = true;
					quex[rear] = tx;
					quey[rear++] = ty;
					if (rear == N * N) rear = 0;
				}
			}
		}
		if (++front == N * N) front = 0;
	}
}

int main()
{
	scanf("%lf%d%d", &speed[0][0], &m, &n);
	for (int i = 0; i < m; ++i)
	{
		for (int j = 0; j < n; ++j)
		{
			scanf("%d", &height[i][j]);
			speed[i][j] = speed[0][0] * pow(2.0, height[0][0] - height[i][j]);
		}
	}
	for (int i = 0; i < m; ++i)
	{
		for (int j = 0; j < n; ++j)
		{
			speed[i][j] = 1.0 / speed[i][j];
		}
	}
	spfa();
	printf("%.2lf\n", mindis[m - 1][n - 1]);
	return 0;
}


你可能感兴趣的:(ACM解题报告)