最短路徑:C Apare_xzc
2010年上海交通大學計算機研究生機試真題
2020/3/21
題目鏈接:codeup contest 100000621C題 <–
據說多組輸入,有重邊
樣例輸入1:
4 3
0 1
1 2
2 0
樣例輸出1:
4 3
0 1
1 2
2 0
樣例輸入2:
4 4
1 2
2 3
1 3
0 1
樣例輸出2:
8
9
11
分析:
這顯然是求單源最短路。我們第一反應就是Dijkstra。其實這道題的節點個數才100,O(n^3)的
Floyd算法也可以。但是這道題與衆不同的地方,就是它的邊權都是2^k,這些邊我們無法用long long存儲。 我們要充分挖掘題目的特殊性。
我們可以知道,邊長指數型增長非常之快。2^k > 2^0 + 2^1 + 2^2 + 2^3 + ... + 2^(k-1))
。由於輸入的第K條邊的長度爲2^K
,所以後面的邊比前面所有邊之和還要大。換句話說,如果前面兩點已經連同,那麼我們沒有必要用後面的邊去更新最短路。
解法一:寫一個二進制大數類,然後開心地跑Dijkstra。
解法二:由剛纔的分析可得,出現在前面的邊,只要能使兩點連通,那麼這兩點見的最短距離就定下來了。我們可以維護一個帶權並查集,權值代表到集合標誌點的距離。我們讓0號節點作爲它所在集合的標誌點即可。我們讓id小的當節點即可。
代碼一:
二進制大數類+Dijkstra 27ms
#include <bits/stdc++.h>
using namespace std;
const int mod = 100000;
struct BigNum {
int s[504],h;
int toInt() {
if(h==502) return -1;
int ans = 0;
for(int i=h; i>=0; --i) ans = (ans*2+s[i])%mod;
return ans;
}
BigNum operator = (const BigNum& rhs) {
h = rhs.h;
for(int i=0; i<504; ++i) s[i] = rhs.s[i];
return *this;
}
BigNum(int k) {
memset(s,0,sizeof(s));
h = k; s[k] = 1;
}
BigNum() {
memset(s,0,sizeof(s));
h = 0;
}
bool operator < (const BigNum& rhs)const {
if(h!=rhs.h) return h<rhs.h;
for(int i=h-1; i>=0; --i) {
if(s[i]>rhs.s[i]) return false;
else if(s[i]<rhs.s[i]) return true;
}
return false;
}
bool operator > (const BigNum& rhs)const {
if(h!=rhs.h) return h>rhs.h;
for(int i=h-1; i>=0; --i) {
if(s[i]>rhs.s[i]) return true;
else if(s[i]<rhs.s[i]) return false;
}
return false;
}
friend BigNum operator + (const BigNum& a,const BigNum& b) {
BigNum res;
int H = max(a.h,b.h);
for(int i=0; i<=H; ++i) {
res.s[i] += a.s[i]+b.s[i];
res.s[i+1] = res.s[i]/2;
res.s[i] %= 2;
}
if(res.s[H+1]!=0) res.h = H+1;
else res.h = H;
return res;
}
};
struct Node {
int to,Next;
BigNum d;
} node[1005];
int head[102],tot;
void initEdge(int n) {
tot = 0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int k) {
node[tot].to = v;
node[tot].d = BigNum(k);
node[tot].Next = head[u];
head[u] = tot++;
}
struct A {
int id;
BigNum dd;
bool operator < (const A& rhs)const {
return dd > rhs.dd;
} A() {}
A(int i,BigNum ddd) {
id = i; dd = ddd;
}
}An;
BigNum dis[101];
bool vis[101];
void Dijkstra(int n) {
for(int i=1; i<n; ++i) dis[i] = BigNum(502);
memset(vis,false,sizeof(vis));
priority_queue<A> Q;
Q.push(A(0,BigNum()));
int u = 0;
BigNum dd;
while(!Q.empty()) {
An = Q.top(); Q.pop();
u = An.id; dd = An.dd;
vis[u] = true;
for(int i=head[u]; i!=-1; i=node[i].Next) {
int to = node[i].to;
if(vis[to]) continue;
BigNum nd = node[i].d+dd;
if(nd<dis[to]) {
dis[to] = nd;
Q.push(A(to,dis[to]));
}
}
}
for(int i=1; i<n; ++i)
printf("%d\n",dis[i].toInt());
}
int main(void) {
int n,m,x,y;
while(cin>>n>>m) {
int k = 0;
initEdge(n);
while(m--) {
scanf("%d%d",&x,&y);
addedge(x,y,k);addedge(y,x,k++);
}
Dijkstra(n);
}
return 0;
}
代碼二:
帶權並查集:
#include <bits/stdc++.h>
using namespace std;
const int N = 101;
const int mod = 100000;
int pre[N],dis[N];
void initUFset() {
for(int i=0;i<101;++i) pre[i] = i, dis[i] = 0;
}
int Find(int x) {
if(x==pre[x]) return x;
int y = pre[x];
pre[x] = Find(pre[x]);
dis[x] += dis[y];
return pre[x];
}
bool join(int x,int y,int d) {
int fx = Find(x), fy = Find(y);
if(fx==fy) return false;
pre[fy] = fx;
dis[fy] = (dis[x]+dis[y]+d)%mod;
return true;
}
int main(void) {
int n,m,x,y;
while(cin>>n>>m) {
initUFset();
int d = 1;
while(m--) {
scanf("%d%d",&x,&y);
if(x>y) swap(x,y);
join(x,y,d);
d = (d<<1)%mod;
}
for(int i=1;i<n;++i)
printf("%d\n",Find(i)?-1:dis[i]);
}
return 0;
}
xzc
2020/3/21 20:24