關於王道機試指南習題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;
}
思路二:(可以不用大數保存中間數據,每步運算都可以直接取模保存,因爲事先已經明確了大小關係,是代碼的內在邏輯)
  • 題目給的數據有一個特點:越往後,給出的兩個點之間的距離就越大,且大於前面所有的邊的權值之和,那麼意味着,任意一個點在第一次和源點連通時,他們的距離即是最短距離。這個思路可以考慮一下。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章