今天,一个小学弟问了这个最短路的问题,最开始他问了个板子题P3771
题目背景
本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779。
题目描述
如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。
输入输出格式
输入格式:
第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。
接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。
输出格式:
一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)
输入:
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
输出:
0 2 4 3
显然这就是一个dijkstra的板子题,直接写就ok了
#include<bits/stdc++.h>
const int N = 1e5 + 7;
const int M = 5e6 + 7;
const int inf = 2147483647;
using namespace std;
struct graph{
int d,b;
};
vector<graph> w[N];
int dis[N],f[N],minn = inf;
int n,m,k;
void set_graph(int x,int y,int l)
{
graph res;
res.d = y;
res.b = l;
w[x].push_back(res);
}
void dij()
{
f[k] = 1;
for(int i = 1;i <= n;++i) dis[i] = inf;
for(int i = 0;i < w[k].size();++i)
dis[w[k][i].d]=min(w[k][i].b,dis[w[k][i].d]);
dis[k] = 0;
for(int i = 1;i < n;++i){
minn = inf;
int cnt = 0;
for(int j = 1;j <= n;++j)
if(f[j] == 0 && dis[j] < minn){
minn = dis[j];
cnt = j;
}
f[cnt] = 1;
if(!cnt) break;
for(int j = 0;j < w[cnt].size();++j){
if(w[cnt][j].b + dis[cnt] < dis[w[cnt][j].d])
dis[w[cnt][j].d] = w[cnt][j].b+dis[cnt];
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i = 1;i <= m;++i){
int x,y,l;
scanf("%d%d%d",&x,&y,&l);
set_graph(x,y,l);
}
dij();
printf("%d",dis[1]);
for(int i = 2;i <= n;++i)
printf(" %d",dis[i]);
printf("\n");
return 0;
}
然后,这个可爱的(恶魔的)小学弟问了我这个数学选手,升级版。。。
数据变成了
1<=N<=1000000;
1<=M<=200000 ;
1≤ui,vi,S≤N ;
0≤wi≤109 ,
0≤∑wi≤109 。
这个就很爆炸了,当然啦,隔壁一大佬说不就是一个堆排序吗,可惜,并不会用堆。。。
然后,在某个不经意间,看到了一个天才的博文,他说可以用线段树优化dijkstra!!!按照他的方法试了一波,我的老天,这都行!!!附上AC代码。
线段树瞎搞版:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 7;
const int M = 5e5 + 7;
const int inf = 0x7fffffff;
const int imf = 0x3f3f3f3f;
int n,m,k;
inline int read() {
int x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9'){
x = x * 10 + c - '0';
c = getchar();
}
return x;
}
struct graph{
int v,next,w;
}edge[M];
int num = 0,head[N];
void set_graph(int a,int b,int c)
{
edge[++num].v = b;
edge[num].w = c;
edge[num].next = head[a];
head[a] = num;
}
int dis[N],ans[N],s,t;
int tree[N << 2],leaf;
int check(int i,int j)
{
return dis[i]<dis[j]?i:j;
}
inline void build()
{
memset(dis,imf,sizeof dis);
for(leaf = 1;leaf <= n;leaf <<= 1) ;
leaf--;
for(int i = 1;i <= n;++i) tree[leaf + i] = i;
}
void modify(int x,int y) {
dis[x] = y;
x += leaf;
x >>= 1;
while(x){
tree[x] = check(tree[x << 1],tree[x << 1|1]);
x >>= 1;
}
}
void dij() {
build();
dis[k] = 0;
int u = k;
for(int i = 1;i <= n;++i) {
ans[u] = dis[u];
int disu = dis[u];
modify(u,inf);
for(int j = head[u];j;j = edge[j].next){
int v = edge[j].v;
if(dis[v] < inf && dis[v] > disu + edge[j].w)
modify(v,disu + edge[j].w);
}
u = tree[1];
}
}
int main() {
n = read(),m = read(),k = read();
for(int i = 1;i <= m;++i) {
int a = read(),b = read(),c = read();
set_graph(a,b,c);
}
dij();
for(int i = 1;i <= n;++i) {
if(dis[i] == imf) ans[i] = inf;
printf("%d",ans[i]);
printf(i == n ? "\n" : " ");
}
return 0;
}
隔壁大佬正解版:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=1e5+10;
const int inf=2147483647;
int head[maxn],dis[maxn],cnt,n;
struct Edge
{
int nex,to,w;
}edge[20*maxn];
void add(int u,int v,int w)
{
edge[++cnt].nex=head[u];
edge[cnt].w=w;
edge[cnt].to=v;
head[u]=cnt;
}
struct node
{
int u,dis;
node(){}
node(int a,int b):u(a),dis(b){}
bool operator <(const node &no) const
{
return dis>no.dis;
}
};
void dijkstra(int s)
{
for(int i=1;i<=n;i++) dis[i]=inf;
dis[s]=0;
priority_queue<node> que;
node st(s,0);
que.push(st);
while(!que.empty())
{
node f=que.top();
que.pop();
int u=f.u,d=f.dis;
if(d!=dis[u]) continue;
for(int i=head[u];~i;i=edge[i].nex)
{
int v=edge[i].to,w=edge[i].w;
if(dis[u]+w<dis[v])
{
dis[v]=dis[u]+w;
node tmp(v,dis[v]);
que.push(tmp);
}
}
}
}
int main()
{
int m,s,u,v,w;
scanf("%d%d%d",&n,&m,&s);
memset(head,0xff,sizeof(head));
cnt=0;
while(m--)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
dijkstra(s);
for(int i=1;i<=n;i++)
{
if(i!=1) printf(" ");
printf("%d",dis[i]);
}
printf("\n");
return 0;
}