【高手回避】poj3268,一道很水的dijkstra算法题

1 篇文章 0 订阅
1 篇文章 0 订阅

网上看的大多是转置矩阵,然后套用两次dijkstra算法

个人认为,转置一个n*n的矩阵在时间方面是很不划算的,虽然dijkstra算法本身的复杂度就是O(n^2)(当然你能用堆优化达到O((n + m) * log n),用斐波那契堆优化可以达到O(m + n * log n))……但是个人认为这道题完全没有必要转置这个矩阵。

当牛去第x个牧场的时候,我们不太习惯这种模式,一般都是单源多终点的题,这次来个多元单终点的。。。其实算法是一样的,就是看对Dijstra算法的理解度。

dijkstra算法就是不断维护并更新dist[n]的最小值,那么(我个人比较偏向于tab缩进,tab宽度=4):

#include <cstdio>
#include <cstring>
#include <climits>
#define MAXN 1001         // 这里可以用1000,但是为了保险还是给个1001吧

unsigned to_dist[MAXN];   // 牛要过去时的dist[n]
unsigned back_dist[MAXN]; // 牛回来时的dist[n]
unsigned mp[MAXN][MAXN];
bool s[MAXN];

int main(int argc, char* argv[])
{
	int n, m, x, i, j, a, b, t, k; // n,m,x都是题目给的,i,j是临时变量(我懒得每for就临时变量)
	unsigned mins;
	while (scanf("%d%d%d", &n, &m, &x) != EOF)
	{
		memset(mp, 0xff, sizeof(mp)); // 默认边长为0xffffffff,unsigned int的最大值
		memset( s,    0, n);          // 这里之所以是n是因为我们知道bool占一个字节
		while (m-- && scanf("%d%d%d", &a, &b, &t))
			mp[a - 1][b - 1] = t;
		mp[x - 1][x - 1] = 0;
		// 先做不常见的倒过来行走
		for (i = 0; i < n; ++i)
			to_dist[i] = mp[i][x - 1]; // 倒过来赋dist[n]的值即可,从每个起点到x(即数组里的x-1)
		s[x - 1] = true;
		for (i = 1; i < n; ++i) // 这里可以使用while(1),因为在运行了n-1次后必定终止
		{
			mins = UINT_MAX;
			k = -1;
			for (j = 0; j < n; ++j)
			{
				if (!s[j] && mins > to_dist[j])
				{
					mins = to_dist[j];
					k = j;
				}
			}
			if (k == -1)
				break;
			s[k] = true;
			for (j = 0; j < n; ++j)
			{
				// 倒过来思考即可,如果从j到k有路而且dist[j]比从j到k再通过k到x的路径要长,就更新它
				if (!s[j] && mp[j][k] != UINT_MAX && to_dist[j] > mins + mp[j][k])
					to_dist[j] = mins + mp[j][k];
			}
		}

		memset(s, 0, n); // 计算返回的dist[n]前更新s[n],下面都是套公式,不解释了
		for (i = 0; i < n; ++i)
			back_dist[i] = mp[x - 1][i];
		s[x - 1] = true;
		for (i = 1; i < n; ++i)
		{
			mins = UINT_MAX;
			k = -1;
			for (j = 0; j < n; ++j)
			{
				if (!s[j] && mins > back_dist[j])
				{
					mins = back_dist[j];
					k = j;
				}
			}
			if (k == -1)
				break;
			s[k] = true;
			for (j = 0; j < n; ++j)
			{
				if (!s[j] && mp[k][j] != UINT_MAX && back_dist[j] > mins + mp[k][j])
					back_dist[j] = mins + mp[k][j];
			}
		}
		mins = 0;
		for (i = 0; i < n; ++i)
		{
			if (mins < to_dist[i] + back_dist[i])
				mins = to_dist[i] + back_dist[i];
		}
		printf("%u\n", mins); // 注意这里的输出格式,不然WA别怪我
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值