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条边,太大了,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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】