projecteuler No.83 four ways

题目链接:

Problem 83 four ways

题目83:允许向上下左右移动的情况下从左上角到右下角的最小路径和是多少?

通过人数:8961


题目分析:

前面的两道题都用动态规划做了,而这一问缺是典型的单源最短路径问题,我使用了Dijkstra算法。


题过程(代码仅供参考,因为偷懒,代码风格什么的实在不好意思...)

【原谅我因为偷懒,使用纯c风格写了这个图论算法...】

一、首先,从文件中读入数据

FILE *fp = fopen("matrix.txt", "r");
char c[110000];
for (int i = 0; i < 80; i++)
{
	fgets(c, 100000, fp);
	char * pc = c;
	int l = 1;
	sscanf(pc, "%d", &a[i][0]);
	while(1)
	{
		if (*pc == 0)
			break;
		if (*pc == ',')
		{
			sscanf(pc+1, "%d", &a[i][l]);
			l++;
		}
		pc++;
	}
}
这步和81、82题是一样的,读入数据存在数组int a[80][80]中。


二、初始化标记和优先队列

m[0][0] = 2;
m[1][0] = 1;
m[0][1] = 1;
b[0][0] = a[0][0];
b[0][1] = b[0][0] + a[0][1];
b[1][0] = b[0][0] + a[1][0];
pq[0] = &b[0][1];
pq[1] = &b[1][0];
end = 2;
标记数组m[80][80]储存的是该点位的状态。2为已解出到开始点的最短路径。1为已解出其相邻点 到开始点的最短路径。0为其它。

数组b[80][80]储存的是计算到当前该店到开始点的最短距离。具体是不是最终值取决于m值。

优先队列pq的定义是

int *pq[10000] ,start = 0, end = 0;
本质上就是一个指针数组,存的是数组b中的一些元素的地址。如果你想问我是怎么实现优先的,我会告诉你是在每次取值之前使用qsort先排个序~相对于使用堆的确会对时间复杂度产生影响,但这道题的时间还是够用的~并且写个堆也挺麻烦的~STL中的priortyqueue也没那么好用...并且不是说好的是纯c风格的代码么...


三、不断地从优先队列中取值,标记,并在终止点的最短路径被计算出来的时候终止。

for (int Z = 0; Z < 6399; Z++)
{
	qsort(pq + start, end-start, sizeof(int *),comp);

	int px = (pq[start] - b[0])/80;
	int py = (pq[start] - b[0])%80;

	if (px > 0)
		Do(px - 1, py, *pq[start]);
	if (px < 79)
		Do(px + 1, py, *pq[start]);
	if (py > 0)
		Do(px, py - 1, *pq[start]);
	if (py < 79)
		Do(px, py + 1, *pq[start]);

	start++;
	m[px][py] = 2;

	if (m[79][79] == 2)
	{
		ans = b[79][79];
		break;
	}

}
px和py的运算时根据二原数组的元素纯粹地址的规律计算从优先队列中取出的元素在b中的位置。

函数Do:

void Do(int px, int py, int d)
{
	if (m[px][py] == 2)
		return;

	if (m[px][py] == 1)
	{
		if (a[px][py] + d < b[px][py])
			b[px][py] = a[px][py] + d;
		return;
	}

	if (m[px][py] == 0)
	{
		b[px][py] = a[px][py] + d;
		m[px][py] = 1;
		pq[end] = &b[px][py];
		end++;
		return;
	}
}
完成标记工作。(这几个数组我设的是全局变量,方便起见~)
而qsort的比较函数comp为:

int comp(const void * a, const void * b)
{
	return **(int **)a - **(int **)b;
}

最终,输出的答案为425185。




以上只是我做题时的解法。

如果有更好的解法、更好的思路,欢迎评论讨论~O(∩_∩)O~





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值