牛客oj 习题11.6最短路径(并查集+Dijkstra+大数加乘比较)

题目链接:click here

题意:中问题,数据非常大

思路:

(1)、由于后输入的边一定要大于之前所有边的总和,所以用并查集解决多余边问题,若输入边的两点不在同一集合,则合并,代表这个集合内的边都是尽可能小的边,若输入边的两点已经在同一集合,则将边舍弃;

(2)、使用Dijkstra算法解决最短路问题,由于输入数据太庞大,所以用字符串存储以及运算,这里用到了大数加法、乘法、比较;

(3)、将求得的字符串转化为整形数字并输出后5位。

PS:这题昨天写了我一天,练到了好多知识点(并查集属于优化范畴)。越长的代码越是能锻炼自己的代码能力,也值了。只是这题因为不会改bug也看了些题解,其中有些人只用并查集+最短路没用大数就过去了,他们大多在输入边的时候就对100000取了模,我认为这样想是错误的。题目中说数值太大的以MOD 100000 的结果输出,没说输入的边太大就模100000,而且不管floyd还是dijkstra都要比较距离值,取模后很显然无法正确比较。然而他们还是过了,我认为是数据还是弱,弱到边的大小在小于100000的时候就全将节点合并,以至于后面大于100000的边都舍去,导致结果依然不变,但这并不能说明这题解答正确。

再PS:大数比较函数一开始写居然写成了从低位比到高位,一度改代码改到绝望半天才找出问题,总体来说这题体验不错。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <sstream>
#include <queue>
#include <climits>
 
using namespace std;
 
const int MAXN = 105;
const int INF = INT_MAX;

bool BigSmallerCmp(string str1, string str2){
	if(str1.size() < str2.size()) return true;
	else if(str1.size() > str2.size()) return false;
	else{
		for(int i = 0; i < str1.size(); i++){//从高位向低位比较 
			if((str1[i] - '0') > (str2[i] - '0')) return false;
			else if((str1[i] - '0') < (str2[i] - '0')) return true;
		}
	}
	return false;
}

struct Point{
	int number;
	string distance;
	Point(int n, string d): number(n), distance(d) {}
	bool operator< (const Point& c) const{
		return BigSmallerCmp(c.distance, distance);
	}
};

struct Edge{
	int to;
	string length;
	Edge(int t, string l): to(t), length(l) {}
};

int N, M;
int dis[MAXN], father[MAXN], height[MAXN];
string disstr[MAXN], strINF;
bool visit[MAXN];
vector<Edge> graph[MAXN];

string BigMultiple(string str){
	bool carry = false;
	int current;
	string ans;
	for(int i = str.size() - 1; i >= 0; i--){
		current = str[i] - '0';
		current *= 2;
		if(carry) current++;
		carry = false;
		if(current >= 10){
			current -= 10;
			carry = true;
		}
		ans.insert(ans.begin(), 1, current + '0');
	}
	if(carry) ans.insert(ans.begin(), 1, '1');
	return ans;
}

void Change(string& str1, string& str2){
	if(str1.size() < str2.size()){
		string tmp = str1;
		str1 = str2;
		str2 = tmp;
	}
	return;
}

string BigPlus(string str1, string str2){
	Change(str1, str2);
	bool carry = false;
	string ans;
	int current, pos = str1.size() - 1;
	for(int i = str2.size() - 1; i >= 0; i--){
		current = (str2[i] - '0') + (str1[pos--] - '0');
		if(carry) current++;
		carry = false;
		if(current >= 10){
			current -= 10;
			carry = true;
		}
		ans.insert(ans.begin(), 1, current + '0');
	}
	for(int i = pos; i >= 0; i--){
		current = str1[i] - '0';
		if(carry) current++;
		carry = false;
		if(current >= 10){
			current -= 10;
			carry = true;
		}
		ans.insert(ans.begin(), 1, current + '0');
	}
	if(carry) ans.insert(ans.begin(), 1, '1');
	return ans;
}

int Find(int x){
	if(x != father[x]) father[x] = Find(father[x]);
	return father[x];
}

void Union(int x, int y){
	if(height[x] < height[y]) father[x] = y;
	else if(height[y] < height[x]) father[y] = x;
	else{
		father[y] = x;
		height[x]++;
	}
}

void Dijkstra(int start){
	disstr[start] = "0";
	priority_queue<Point> myqueue;
	myqueue.push(Point(start, disstr[start]));
	while(!myqueue.empty()){
		int from = myqueue.top().number;
		myqueue.pop();
		if(visit[from]) continue;
		visit[from] = true;
		for(int i = 0; i < graph[from].size(); i++){
			int to = graph[from][i].to;
			string newedge = BigPlus(disstr[from],  graph[from][i].length);
			if(BigSmallerCmp(newedge, disstr[to])){
				disstr[to] = newedge;
				myqueue.push(Point(to, disstr[to]));
			}
		}
	}
}

void CutAndChange(){
	for(int i = 0; i < N; i++){
		stringstream tmp;
		if(disstr[i] == strINF){
			dis[i] = INF;
			continue;
		}
		if(disstr[i].size() > 5) disstr[i] = disstr[i].substr(disstr[i].size() - 5, 5);
		tmp << disstr[i];
		tmp >> dis[i];
	}
}

void Initial(){
	for(int i = 0; i < N; i++){
		graph[i].clear();
		disstr[i] = strINF;
		father[i] = i;
		height[i] = 0;
		visit[i] = false;
	}
}

int main(){
//	freopen("in.txt", "r", stdin);
	strINF = "1";
	for(int i = 0; i < 502; i++){
		strINF = BigMultiple(strINF);
	}
	while(~scanf("%d %d", &N, &M)){
		Initial();
		string road = "1";
		int A, B, x, y;
		for(int i = 0; i < M; i++){
			scanf("%d %d", &A, &B);
			x = Find(A);
			y = Find(B);
			if(x != y){
				Union(x, y);
				graph[A].push_back(Edge(B, road));
				graph[B].push_back(Edge(A, road));
			}
			road = BigMultiple(road);
		}
		Dijkstra(0);
		CutAndChange();
		for(int i = 1; i < N; i++){
			if(dis[i] == INF) printf("-1\n");
			else printf("%d\n", dis[i]);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Flynn_curry/article/details/105484933