acwing1050
题目:
N  个城市,标号从 0 到 N−1,M 条道路,第 K 条道路(K 从 0 开始)的长度为 2K,求编号为 0 的城市到其他城市的最短距离。
输入格式
第一行两个正整数 N,M,表示有 N 个城市,M 条道路。
接下来 M 行两个整数,表示相连的两个城市的编号。
输出格式
N−1 行,表示 0 号城市到其他城市的最短路,如果无法到达,输出 −1,数值太大的以 mod100000 的结果输出。
数据范围
2≤N≤100,
1≤M≤500
输入样例:
4 4
1 2
2 3
1 3
0 1
输出样例:
8
9
11
思路:
本鼠本来想找个最小生成树的题做,没想到在acwing找到了这道题,完全没用到最小生成树的知识,笑的。
这道题比pat甲级的题恶心了很多,本鼠一开始没考虑周全,直接用SPFA做,挂掉了70%的数据,经过抄袭其他博客才弄明白怎么做。
首先分析题目,每条边的权重是2的幂次,这个可以提醒我们,对第i条边,它的权重大于前i-1条边的权重和;同时最多有500条边,\(2^{500}\)太大了,long long都存不下,所以要取模。但是取模后边的权值大小关系可能会发生变化,比如9999与10000同时对10000取模,后者的结果远小于前者,这样处理后直接求最短路径无疑会带来错误。
考虑到对第i条边,它的权重大于前i-1条边的权重和这个启发性的结论,我们不妨这样考虑:如果新输入的边的两个节点在一个已经连通的分量中,那么这条边一定不可能成为最短路径树上的边。于是我们使用并查集,边读取输入边构建union。对于将两个union连接到一起的边,它必定是最短路径树的一条边,那么就将这条边加入邻接表;若一条边的两个节点已经在同一个union中,那么就将这条边丢弃。经过这样处理后,我们就可以得到一个最短路径树(森林)。剩下的就是遍历0号节点所在的树,求最短距离。这一步用dijkstra,SPFA,DFS都可以。
求解边权时,要使用快速幂算法,在算法中每步都要对100000取模。
代码1:
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
#include<queue>
#include <limits.h>
using namespace std;
#define ll long long
//用于邻接表
struct node {
	int id;
	ll w;
	node(int x,ll y):id(x),w(y){}
};
const int MAX = 105;
const ll INF = LLONG_MAX;
const int MOD = 100000;
int n, m;
//邻接表
vector<node> adj[MAX];
//判别节点是否在队列中
bool inq[MAX] = { false };
ll disto[MAX];
//并查集
int uf[MAX] = { 0 };
//储存两个集合的根节点编号
int root1, root2;
//判别并查集中a和b是否在同一个union中
bool connect(int a, int b) {
	while (a != uf[a])		a = uf[a];
	while (b != uf[b])		b = uf[b];
	root1 = a;
	root2 = b;
	return a == b;
}
//快速幂,求(a^b)%MOD
ll qmi(ll a, ll b) {
	ll ans = 1, base = a;
	while (b != 0) {
		if ((b & 1) != 0)
			ans = ans * base % MOD;
		base = base * base % MOD;
		b = b >> 1;
	}
	return ans;
}
void input() {
	cin >> n >> m;
	//初始化并查集
	for (int i = 0; i < n; i++)
		uf[i] = i;
	for (int i = 0; i < m; i++) {
		int u, v;
		cin >> u >> v;
		if (connect(u, v))
			continue;
		//将两个集合合并
		uf[root2] = root1;
		ll w = qmi(2,i);		
		adj[u].push_back(node(v, w));
		adj[v].push_back(node(u, w));
	}
}
void SPFA() {
	disto[0] = 0;
	queue<int> q;
	q.push(0);
	inq[0] = true;
	while (!q.empty()) {
		int u=q.front();
		q.pop();
		inq[u] = false;
		for (int i = 0; i < adj[u].size(); i++) {
			int v = adj[u][i].id;
			ll w = adj[u][i].w;
			//松弛边
			if (disto[v] > disto[u] + w) {
				disto[v] = disto[u] + w;
				if (!inq[v]) {
					q.push(v);
					inq[v] = true;
				}
			}
		}
	}
}
int main(void) {
	input();
	fill(disto, disto + MAX, INF);
	SPFA();
	for (int i = 1; i < n; i++) {
		if (disto[i] == INF)
			cout << "-1" << endl;
		else
			cout << disto[i]% MOD << endl;
	}
	return 0;
}
代码2:
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
#include<queue>
#include <limits.h>
using namespace std;
#define ll long long
//用于邻接表
struct node {
	int id;
	ll w;
	node(int x,ll y):id(x),w(y){}
};
const int MAX = 105;
const ll INF = LLONG_MAX;
const int MOD = 100000;
int n, m;
//邻接表
vector<node> adj[MAX];
//判别节点是否在队列中
bool inq[MAX] = { false };
ll disto[MAX];
//并查集
int uf[MAX] = { 0 };
//用于DFS
bool visit[MAX] = { false };
//储存两个集合的根节点编号
int root1, root2;
//判别并查集中a和b是否在同一个union中
bool connect(int a, int b) {
	while (a != uf[a])		a = uf[a];
	while (b != uf[b])		b = uf[b];
	root1 = a;
	root2 = b;
	return a == b;
}
//快速幂,求(a^b)%MOD
ll qmi(ll a, ll b) {
	ll ans = 1, base = a;
	while (b != 0) {
		if ((b & 1) != 0)
			ans = ans * base % MOD;
		base = base * base % MOD;
		b = b >> 1;
	}
	return ans;
}
void input() {
	cin >> n >> m;
	//初始化并查集
	for (int i = 0; i < n; i++)
		uf[i] = i;
	for (int i = 0; i < m; i++) {
		int u, v;
		cin >> u >> v;
		if (connect(u, v))
			continue;
		//将两个集合合并
		uf[root2] = root1;
		ll w = qmi(2,i);		
		adj[u].push_back(node(v, w));
		adj[v].push_back(node(u, w));
	}
}
//用DFS求解
void DFS(int id,ll dist) {
	visit[id] = true;
	for(int i = 0; i < adj[id].size(); i++) {
		int v = adj[id][i].id;
		int w = adj[id][i].w;
		if (!visit[v]) {
			disto[v] = dist + w;
			DFS(v,disto[v]);
		}
		
	}
}
int main(void) {
	input();
	fill(disto, disto + MAX, INF);
	DFS(0,0);
	for (int i = 1; i < n; i++) {
		if (disto[i] == INF)
			cout << "-1" << endl;
		else
			cout << disto[i]% MOD << endl;
	}
	return 0;
}

 
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号