这样写答案不对
#include <iostream>
using namespace std;
const int mod = 100000;
const int INF = 100000000;
const int N = 100;
int n, m;
int path[N][N];
int dis[N];
int vis[N];
void init(){
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
path[i][j] = INF;
}
dis[i] = INF;
}
}
void Djikstra(int s){
int i, j;
for(i = 0; i < n; i++){
dis[i] = path[s][i];
vis[i] = 0;
}
dis[s] = 0;
vis[s] = 1;
int mindis, minnode;
for(i = 0; i < n; i++){
mindis = INF;
minnode = 0;
for(j = 0; j < n; j++){
if(!vis[j] && mindis > dis[j]){
minnode = j;
mindis = dis[j];
}
}
if(mindis == INF) break;
vis[minnode] = 1;
for(j = 0; j < n; j++){
if(!vis[j] && dis[j] > dis[minnode] + path[minnode][j]){
dis[j] = (dis[minnode] + path[minnode][j]);
}
}
}
}
int main(){
int from, to;
while(cin >> n >> m){
init();
int len = 1;
for(int i = 0; i < m; i++){
cin >> from >> to;
if(path[from][to] > len) path[from][to] = path[to][from] = len;
len = len * 2;
len = len % mod;
}
Djikstra(0);
for(int i = 1; i < n; i++){
if(dis[i] == INF) printf("-1\n");
else printf("%d\n", dis[i] % mod);
}
}
}
注意到第k条路径的长度为2^K,则第k条路径的长度会大于前k-1条路径的总和。
由此我们可知两个推断:
(1)在添加第k条路径时,如果路径k连接的两个城市A 、B已经通过其他路径间接的连通了,那么第k条路径绝对不是AB间的最短路径(第k条路径之后的路径也不可能有比当前AB路径更短的路径了,因为第k条路径的长度会大于前k-1条路径的总和)
(2)在添加第k条路径时,如果路径k连接的两个城市A 、B是第一次被连通了(也就是说此前没有任何路径能连通AB,包括通过多条路径间接连通),那么路径k就是AB之间的最短距离了,因为以后不可能存在更短的路径连接AB,以后的单条路径只会越来越长。
通过上述的推断,我们可以利通过建立一个最小连通树的同时算出0号城市到各个城市的最小路径。
具体的算法:
(1)构造一个并查集,每个城市指向自己,二维数组d记录城市之间的距离,初始化为-1不可达,d[i][i]初始化为0
(2)取第一条路径,第一条路径连接了城市a、b,城市a、b间最短路径就是第一条路径,然后把a、b合并在一个集合里
(3)再取一条路径,
(一)如果这条边连接的城市c、d,已经在同一个集合里了(即已经联通了),那么当前的路径一定不是cd之间的最短路径了,忽略之(理由是推断(1)),继续看下一条路径。
(二)如果这条路径连接的城市c、d不在同一个集合里(之前没有联通过),很好,这条路径是cd之间的最短路径(理由是推断(2));除此之外我们还可以看看通过这条路径能不能让c集合里的城市到d集合里的城市之间的路径更短呢?即是不是d[i][j]>(d[i][C]+dist+d[D][j]) ?(注意i和C一个集合,D和j一个集合),如果是的话可以更新一下这个值。
(4)重复(3)直到所有路径都处理过了
0号城市到各个城市i的最小路径结果就是d[0][i]了
这样写AC
#include <iostream>
#include <stdio.h>
#include <memory.h>
using namespace std;
const int mod = 100000;
const int INF = 100000000;
const int N = 102;
int n, m;
int path[N][N];
int father[N];
int vis[N];
void init(){
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
path[i][j] = INF;
}
path[i][i] = 0; //对角线
father[i] = i;
vis[i] = 0;
}
}
int find(int x){
if(x != father[x]){
father[x] = find(father[x]);
}
return father[x];
}
int main(){
int from, to;
while(cin >> n >> m){
init();
int len = 1;
for(int i = 0; i < m; i++){
cin >> from >> to;
int x = find(from);
int y = find(to);
if(x != y){
//由于代价特点,后面的大于前面所有代价之和,这样一旦连通,后面的在连接也没有意义
father[x] = y;
path[from][to] = path[to][from] = len;
}
len = len * 2;
len = len % mod;
}
vis[0] = 1;
int i, j;
int mindis, minnode;
for(i = 0; i < n; i++){
mindis = 123123123;
minnode = 0;
for(j = 1; j < n; j++){
if(!vis[j] && mindis > path[0][j]){
minnode = j;
mindis = path[0][j];
}
}
vis[minnode] = 1;
for(j = 1; j < n; j++){
if(!vis[j] && path[0][j] > path[0][minnode] + path[minnode][j]){
path[0][j] = path[0][minnode] + path[minnode][j];
}
}
}
for(i = 1; i < n; i++){
if(path[0][i] == INF) printf("-1\n");
else printf("%d\n", path[0][i] % mod);
}
}
}
京公网安备 11010502036488号