P4779 【模板】单源最短路径(标准版)
spfa算法
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10,M=5e5+10;
int n,m,x,y,z,s,cnt,dis[N],vis[N],head[N];
struct edge
{
int w,to,next;
}e[M];//定义边的三个信息,w表示边权,to表示边的终点,next表示上一条边
void add(int x,int y,int z)//链式前向星存边
{
e[cnt].to=y;
e[cnt].w=z;
e[cnt].next=head[x];//next表示以x为起点的上一条边的编号
head[x]=cnt++;//head[x]表示以x为起点的最后一条边的编号
}
queue<int>q;
void spfa()
{
q.push(s);//起点入队
dis[s]=0;//起点到自身距离为0
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=0;//出队标记
for(int i=head[u];i!=-1;i=e[i].next)//遍历以u为起点的所有边
{
int v=e[i].to;
if(dis[u]+e[i].w<dis[v])
{
dis[v]=dis[u]+e[i].w;//更新最短距离
if(vis[v]==0)
q.push(v),vis[v]=1;//入队标记
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m>>s;
memset(head,-1,sizeof(head));//head数组初始化
for(int i=1;i<=m;i++)
{
cin>>x>>y>>z;
add(x,y,z);
}
for(int i=1;i<=n;i++)//dis数组初始化
dis[i]=2147483647;
spfa();
for(int i=1;i<=n;i++)
i==n?printf("%d\n",dis[i]):printf("%d ",dis[i]);
return 0;
}
/*
思路:
用链式向前星存储顶点之间的关系与权值
用优先队列存储边权小的先出
首先将起点入队,向四周发散,再将起点与到达点的总权值入队,
保证了是一步一步到达(如果不连通不可能遍历到),同时遍历的过程当中不断找更小的权值并更新
优先队列的用处:若有两种不同的到达方式,优先队列会先遍历到短的那一条,然后将其入队,那远的一条就不会入队。
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+10,M=2e5+10;
int n,m,s,x,y,z,cnt,vis[N],head[N];
long long dis[N];
struct edge
{
int w;//权重
int to;//终点
int next;//下一条边的索引
}e[N];
void add(int x,int y,int z)
{
e[++cnt].to=y;
e[cnt].w=z;
e[cnt].next=head[x];//记录上一条边的索引
head[x]=cnt;//记录新边的索引
}
struct node
{
int u;//起点
int w;//边权
};
bool operator < (const node &s1,const node &s2)
{
return s1.w>s2.w;
}
priority_queue<node>q;
void dij()
{
q.push((node){s,0});//起点入队
dis[s]=0;//起点路程为0
while(!q.empty())
{
int u=q.top().u;q.pop();//取出
if(vis[u])continue;//如果该顶点被遍历过就下一个找下一个顶点
vis[u]=1;//标记
//遍历所有以u为起点的边
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;//边的终点
//dis[u]为起点到u的权值,e[i].w是u到v的权值,dis[v]是起点到v的权值
if(dis[u]+e[i].w<dis[v])
{
dis[v]=dis[u]+e[i].w;//更新
q.push((node){v,dis[v]});//将终点入队
}
}
}
}
int main()
{
cin>>n>>m>>s;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
scanf("%d %d %d",&x,&y,&z);
add(x,y,z);
}
for(int i=0;i<=n;i++)
dis[i]=2147483647;
dij();
for(int i=1;i<=n;i++)
printf("%lld ",dis[i]);
return 0;
}
P1186 玛丽卡
思路就是将最短路上的路径遍历一遍,都堵一次,找花费时间最大的。
/*
思路:
用链式向前星存储顶点之间的关系与权值
用优先队列存储边权小的先出
首先将起点入队,向四周发散,再将起点与到达点的总权值入队,
保证了是一步一步到达(如果不连通不可能遍历到),同时遍历的过程当中不断找更小的权值并更新
优先队列的用处:若有两种不同的到达方式,优先队列会先遍历到短的那一条,然后将其入队,那远的一条就不会入队。
*/
#include <bits/stdc++.h>
using namespace std;
const int N=1e3+10,M=2e5+10;
int n,m,s,x,y,z,cnt,vis[N],head[N],st,ed,flag;
int dis[N],last[N];
struct edge
{
int w,to,next;
}e[N*N];
void add(int x,int y,int z)
{
e[++cnt].to=y;
e[cnt].w=z;
e[cnt].next=head[x];
head[x]=cnt;
}
struct node
{
int u,w;
};
bool operator < (const node &s1,const node &s2)//重载运算符,优先队列中的自定义排序
{return s1.w>s2.w;}//注意写大于号还是小于号与实际的符号相反,实际是小于号,则要写大于号
priority_queue<node>q;
void dij()
{
memset(vis,0,sizeof(vis));
for(int i=0;i<=n;i++)
dis[i]=2147483647;
q.push({s,0});
dis[s]=0;
while(!q.empty())
{
int u=q.top().u;q.pop();
if(vis[u])continue;
vis[u]=1;
//遍历所有以u为起点的边
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(u==st&&v==ed)continue;
//dis[u]为起点到u的权值,e[i].w是u到v的权值,dis[v]是起点到v的权值
if(dis[u]+e[i].w<dis[v])
{
if(!flag)last[v]=u;
dis[v]=dis[u]+e[i].w;
q.push((node){v,dis[v]});
}
}
}
}
int main()
{
cin>>n>>m;
s=1;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dij();
int ans=0;
ed=n;
flag=1;
while(ed!=1)
{
st=last[ed];
s=1;
dij();
ans=max(ans,dis[n]);
ed=st;
}
cout<<ans<<endl;
return 0;
}
P1462 通往奥格瑞玛的道路
题目的关键词“最大最小值”,一看就是二分的题,二分大小,满足即可右区间调左,不满足则左区间右移,第一次没做出来,日后复习再做。