[最短路徑:C]2010年上海交通大學計算機研究生機試真題Dijkstra+二進制大數or帶權並查集 Apare_xzc

最短路徑: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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章