关于王道机试指南习题11.6(牛客网考研真题) 上海交通大学复试上机题解:最短路径

关于王道机试指南习题11.6 上海交通大学复试上机题解:最短路径

 之前存在的问题:当专门为大数定义一个结构体时,那么在新的结构体内无法利用大数的结构体内已经重定义的运算符;

两个思路:

思路一:
  • 以字符串存储大数,如此在Edge和Point的结构体内就可以利用字符串的比较规则;
    • 由于字符串比较是按字典序比较,为了保证两个大数能够直接比较,必须要让所有大数的位数相同,如此才能直接比较。否则会出现明明大数x更大,位数更多,但反而因为大数x的高位比大数y的高位小而得出x<y的结论。
    • 由题意可知,题中两地的距离最大不过2^500次方,且距离都是2的n次方,因此我们可以以二进制来对距离进行统计。由此我们对每个大数都定义一个位数为500大小的字符串,str[0]属于高位,str[500-1]属于低位,不足高位我们补0,以方便后续的大数作比较。
    • 另外,对于大数的字符串的位数,我们应当在500基础上加一个额外的安全量,定义为MAXM=512;
  • 利用Dijkstra算法得出各点到源点的最短距离;
  • 最后输出的时候利用快速幂的方法得到距离对mod=100000的余数;
    • 快速幂有:和的模=模的和,积的模=模的积 的特点;
  • 这个思路用大数的必要性:Dijkstra算法在过程中会进行dist[i]的大小比较,如果不使用大数,那么就要在过程中利用快速幂的方法不断取模,而在过程中取模后得到的大小比较结果是有误的,因此必须要用大数来保存这个中间数据;

代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 110;
const int MAXM = 512;

typedef string BigInteger;

struct Edge {
	int to;
	BigInteger distance;
	Edge(int t, BigInteger d) :to(t), distance(d) {}
};

struct Point {
	int number;
	BigInteger distance;
	Point(int n, BigInteger d) :number(n), distance(d) {}
	bool operator<(const Point& p) const {
		return distance > p.distance;
	}
};

vector<Edge> graph[MAXN];
BigInteger dist[MAXN];

BigInteger getBigInt(int i) {//大数(二进制表示)从低到高的第(i+1)位赋值为1
	string str;
	str.insert(0, MAXM, '0');
	str[MAXM - i - 1] = '1';
	return str;
}

BigInteger addString(BigInteger a, BigInteger b) {
	string ans;
	for (int i = 0; i < MAXM; i++) {
		ans.append(1, a[i] - '0' + b[i] - '0' + '0');
	}
	return ans;
}

const string INF = getBigInt(511);

void Dijkstra(int s) {
	priority_queue<Point> myPriorityQueue;
	dist[s][0] = '0';//点s到源点s的最近距离是0
	myPriorityQueue.push(Point(s, dist[s]));
	while (!myPriorityQueue.empty()) {
		int u = myPriorityQueue.top().number;
		myPriorityQueue.pop();
		for (int i = 0; i < graph[u].size(); i++) {
			int v = graph[u][i].to;
			BigInteger d = graph[u][i].distance;
			if (dist[v] > addString(dist[u], d)) {
				dist[v] = addString(dist[u], d);
				myPriorityQueue.push(Point(v, dist[v]));
			}
		}
	}
	return;
}

int StrToInt(string str) {//利用快速幂得到对应的十进制数对100000的取余
	int mod = 100000;
	int answer = 0, multiple = 1;
	for (int i = MAXM-1; i>=0; i--) {
		if (str[i] == '1') {
			answer += multiple;
			answer %= mod;
		}
		multiple *= 2;
		multiple %= mod;
	}
	return answer;
}

int main() {
	int n, m;
	while (scanf("%d%d", &n, &m) != EOF) {
		memset(graph, 0, sizeof(graph));
		for (int i = 0; i < n; i++) {
			dist[i] = INF;
		}
		int from, to;
		BigInteger distance;
		for (int i = 0; i < m; i++) {
			scanf("%d%d", &from, &to);
			distance = getBigInt(i);
			graph[from].push_back(Edge(to, distance));
			graph[to].push_back(Edge(from, distance));
		}
		Dijkstra(0);
		for (int i = 1; i < n; i++) {
			if (dist[i] == INF) {
				printf("-1\n");
				continue;
			}
			printf("%d\n", StrToInt(dist[i]));
		}
	}
	return 0;
}
思路二:(可以不用大数保存中间数据,每步运算都可以直接取模保存,因为事先已经明确了大小关系,是代码的内在逻辑)
  • 题目给的数据有一个特点:越往后,给出的两个点之间的距离就越大,且大于前面所有的边的权值之和,那么意味着,任意一个点在第一次和源点连通时,他们的距离即是最短距离。这个思路可以考虑一下。
发布了3 篇原创文章 · 获赞 1 · 访问量 1238

猜你喜欢

转载自blog.csdn.net/Ryn_love/article/details/105074685
今日推荐