題目鏈接: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;
}