今天早上起来,好怪哦,昨天明明完成了3题今天显示只完成2道。噢仔细一看,有道题被移出去了。
一个单源最短路题目,用dijkstra。
不过没经过优化的朴素dijkstra好像过不了,地图过大(m <= 5 * 10^5),因此需要采用链式前向星的优化空间才行。
dijkstra的算法思路是:从起点开始,选出从起点开始最短的一个点,然后从那个点再继续往下找最短的点,直到所有点都遍历一遍,完成更新,得到了从s点开始到其他点的最短距离。
根据题意,我们只要用一遍dijkstra,参数为起点,处理完成后输出dis数组(即从s点到其他点的最短距离)即可。
下面上代码。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = pow(2, 31) - 1;
int n, m, Beg;
int dis[10001]; //从起点到各点的距离
int vis[10001];
struct node {
ll to, next, v;
} edge[500001];
int head[500001];
int cnt = 0;
void add(int a, int b, int v)
{
cnt ++;
edge[cnt].v = v;
edge[cnt].to = b;
edge[cnt].next = head[a];
head[a] = cnt;
}
void dijkstra()
{
for (int i = 1; i <= n; i++)
{
dis[i] = INF;
vis[i] = false;
}
dis[Beg] = 0;
int idx = Beg;
while (true)
{
vis[idx] = true;
for (int i = head[idx]; i != 0; i = edge[i].next)
{
int a = idx, b = edge[i].to, v = edge[i].v;
if (!vis[b] && dis[b] > dis[a] + v) dis[b] = dis[a] + v;
}
ll Min = INF;
for (int i = 1; i <= n; i++)
{
if (!vis[i] && Min > dis[i])
{
Min = dis[i];
idx = i;
}
}
if (vis[idx]) break;
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m >> Beg;
for(int i = 1;i <= m;i ++)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w);
}
dijkstra();
for(int i = 1;i <= n;i ++)
{
cout << dis[i] << ' ';
}
return 0;
}
原本以为套个模板稍微改一点点就可以的,样例也过了,代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = pow(2, 31) - 1;
int n, m, Beg, ed;
int dis[10001]; //从起点到各点的距离
int vis[10001];
struct node {
ll to, next, v;
} edge[500001];
int head[500001];
int cnt = 0;
void add(int a, int b, int v)
{
cnt ++;
edge[cnt].v = v;
edge[cnt].to = b;
edge[cnt].next = head[a];
head[a] = cnt;
}
void dijkstra()
{
for (int i = 1; i <= n; i++)
{
dis[i] = INF;
vis[i] = false;
}
dis[Beg] = 0;
int idx = Beg;
while (true)
{
vis[idx] = true;
for (int i = head[idx]; i != 0; i = edge[i].next)
{
int a = idx, b = edge[i].to, v = edge[i].v;
if (!vis[b] && dis[b] > dis[a] + v) dis[b] = dis[a] + v;
}
ll Min = INF;
for (int i = 1; i <= n; i++)
{
if (!vis[i] && Min > dis[i])
{
Min = dis[i];
idx = i;
}
}
if (vis[idx]) break;
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m >> Beg >> ed;
for(int i = 1;i <= m;i ++)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w);
}
dijkstra();
cout << dis[ed];
return 0;
}
可是没想到哇
错在哪里了呢?
噢,原来是无向的,那么就改成无向的就行了!(添加两次,如下)
for(int i = 1;i <= m;i ++)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w);
add(v, u, w);
}
为了保险一些,再把数组开适合一些
int dis[55001]; //从起点到各点的距离
bool vis[55001];
struct node {
ll to, next, v;
} edge[25001];
int head[25001];
ll cnt = 0;
交上去,开吸氧
过了
一看,数据这么大,明显要优化。一般的空间优化dijkstra可能已经不够了,请出堆优化dijkstra。 经过一些些的修改,堆优化dijkstra也成功地AC了。去试了一下仅仅有空间优化的dijkstra,毫不意外地RE了。下面上代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 100001;
const ll inf = pow(2, 31) - 1;
const ll M = 200001;
ll n, m, s;
ll low[N];
int vis[N];
struct edge {
int to, v, next;
} edge[M];
int head[N];
ll cnt = 0;
struct node {
int idx;
int v;
bool operator < (const node &x) const
{
return x.v < v;
}
};
void add(int a, int b, int c)
{
cnt ++;
edge[cnt].v = c;
edge[cnt].to = b;
edge[cnt].next = head[a];
head[a] = cnt;
}
void init()
{
for (int i = 1; i <= n; i++)
{
// printf("low[%d] = %lld\n", i, low[i]);
low[i] = inf;
vis[i] = 0;
}
}
void dijkstra()
{
init();
low[s] = 0;
priority_queue<node> q;
q.push({s, 0});
while (q.size() != 0)
{
int idx = q.top().idx;
q.pop();
if (vis[idx]) continue;
vis[idx] = 1;
for (int i = head[idx]; i != 0;i = edge[i].next)
{
int a = idx, b = edge[i].to, v = edge[i].v;
if (low[b] > low[a] + v)
{
low[b] = low[a] + v;
if (!vis[b]) q.push({b, low[b]});
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m >> s;
for (int i = 1; i <= m; i++)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
dijkstra();
for(int i = 1;i <= n;i ++)
{
cout << low[i] << ' ';
}
return 0;
}
一看,过去再回来,多源最短路!好!Floyd!结果第一批写出来的结果就是:
人麻咯。(上没吸氧,下吸氧)
哪里错了?
仔细一看,我靠。
for(int i = 1;i <= m;i ++)
{
ll a, b, c;
cin >> a >> b >> c;
r[a][b] = c;
// printf("添加从%lld到%lld的线路,长度为%lld\n", a, b, r[a][b]);
}
r[a][b] = c,
两个地方可能会有多条路,而我们要记录最短的那一条,如果按照上面这么写,那么后面输入的"长的路"会覆盖掉原本"相对短的路",于是最后答案就WA了。改成这样就可以了:
for(int i = 1;i <= m;i ++)
{
ll a, b, c;
cin >> a >> b >> c;
r[a][b] = min(r[a][b], c);
// printf("添加从%lld到%lld的线路,长度为%lld\n", a, b, r[a][b]);
}
交了,怕TLE就开了O2优化,于是就给我过了:
挺好的,下面上代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 1e9 + 7;
int n, m;
ll r[1001][1001];
void init()
{
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= n;j ++)
{
if(i == j)
{
r[i][j] = 0;
}
else
{
r[i][j] = inf;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
init();
for(int i = 1;i <= m;i ++)
{
ll a, b, c;
cin >> a >> b >> c;
r[a][b] = min(r[a][b], c);
// printf("添加从%lld到%lld的线路,长度为%lld\n", a, b, r[a][b]);
}
for(int k = 1;k <= n;k ++)
{
for(int i = 1;i <= n;i ++)
{
if(r[i][k] < inf)
for(int j = 1;j <= n;j ++)
{
if(r[i][j] > r[i][k] + r[k][j])
{
// printf("更新%d到%d的线路,经过%d点,耗时%lld -> %lld\n", i, j, k, r[i][j], r[i][k] + r[k][j]);
r[i][j] = r[i][k] + r[k][j];
}
}
}
}
ll sum = 0;
for(int i = 2;i <= n;i ++)
{
sum += r[1][i] + r[i][1]; //过去再回来
// printf("从1号点到%d号点,耗时%lld + %lld\n", i, r[1][i], r[i][1]);
}
cout << sum;
return 0;
}
今天太困了,昨天没睡好,就这样吧。明天继续完成新任务咯。