最短路徑:C Apare_xzc
2010年上海交通大學計算機研究生機試真題
2020/3/21
題目鏈接:codeup contest 100000621C題 <–
據說多組輸入,有重邊
樣例輸入1:
4 3
0 1
1 2
2 0
樣例輸出1:
1
3
-1
樣例輸入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。
解法二:由剛纔的分析可得,出現在前面的邊,只要能使兩點連通,那麼這兩點見的最短距離就定下來了。我們每次得到的邊(x,y,d),如果x,y已經聯通,那麼這條邊沒有意義,因爲它比之前所有的邊之和都大,我們捨棄掉。如果x,y沒有聯通,就用這條長爲d的邊,去計算兩個連通塊之間兩兩結點的距離。對於任意X in [x, …] and Y in [y, …] dis[x][y] = dis[X][x] + d + dis[y][Y]
代碼一:
二進制大數類+Dijkstra 27ms
O(mlogm * m)
#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;
}
代碼二:
Folyd:O(m*n^2)
#include <bits/stdc++.h>
using namespace std;
const int N = 101;
const int mod = 100000;
int dis[N][N];
int main(void) {
int n,m,x,y;
freopen("1.in","r",stdin);
while(cin>>n>>m) {
memset(dis,-1,sizeof(dis)); //初始化爲均不連通
for(int i=0;i<n;++i) dis[i][i] = 0; //自己和自己聯通,便於後續更新
int d = 1;
while(m--) {
scanf("%d%d",&x,&y);
if(dis[x][y]!=-1) { //若x,y已經聯通,則沒有必要更新
d = (d<<1)%mod;
continue;
}
for(int i=0;i<n;++i) {
if(dis[x][i]==-1) continue;
for(int j=0;j<n;++j) { //i和x聯通,j和y聯通
if(dis[y][j]!=-1&&dis[i][j]==-1) //且i和j不連通
dis[i][j]=dis[j][i]=(dis[x][i]+dis[y][j]+d)%mod;
}
}
dis[x][y] = dis[y][x] = d;
d = (d<<1)%mod;
}
for(int i=1;i<n;++i)
printf("%d\n",dis[0][i]);
}
return 0;
}
xzc
2020/3/21 20:24