题目链接: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;
}