一、单源最短路算法
最短路算法_yan__kai_的博客-CSDN博客
二、例题
1.acwing1129

题意解读:走过一条路存在花费c,求最小费用即求最短路。无向图
直接背模板:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N =3000,M= 6200 * 2 + 10;
int h[N],e[M],ne[M],w[M],idx;
int n,m;
int start,ed;
bool st[N];
int dist[N];
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    priority_queue<PII,vector<PII>,greater<PII> > heap;
    dist[start]=0;    
    heap.push({0,start});
    while(heap.size())
    {
        auto t=heap.top();
        heap.pop();
        int ver=t.second;
        if(st[ver]) continue;
        st[ver]=true;
        for(int i=h[ver];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[ver]+w[i])
            {
                dist[j]=dist[ver]+w[i];
                heap.push({dist[j],j});
            }
        }
    }
    return dist[ed];
}
int main()
{
    cin>>n>>m>>start>>ed;
    memset(h,-1,sizeof h);
    for(int i=0;i<m;i++)
    {
        int a,b,w;
        scanf("%d%d%d",&a,&b,&w);
        add(a,b,w);
        add(b,a,w);
    }
    int t=dijkstra();
    printf("%d",t);
}
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4279854/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。2.acwing1128

题意:从起点到其他各点的送信时间实际上仍然是最短路算法,,答案要求我们输出最短时间,实际上就是起点到其它各点的最长时间(最远的点收到通信即代表送信完成)
直接背板子,本着复习的态度,这里不再用dijkstra,而是用floyd算法
复习:floyd算法需要初始化自己到自己的距离为0,初始化其它距离为无穷
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N =110,INF=0x3f3f3f3f;
int n,m;
int d[N][N];
int main()
{
    cin>>n>>m;
    memset(d,0x3f,sizeof d);
    for(int i=1;i<=n;i++) d[i][i]=0;
    for(int i=0;i<m;i++)
    {
        int a,b,w;
        cin>>a>>b>>w;
        d[a][b]=d[b][a]=min(d[a][b],w);
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
            }
    int res=0;
    for(int i=1;i<=n;i++)
        if(d[1][i]>=INF)
        {
            res=-1;
            break;
        }
        else res=max(res,d[1][i]);
    cout<<res;
    return 0;
}
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4279898/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。3.acwing1127

点数800,边数1500。求一次最短路mlogn,枚举所有点放黄油,复杂度mnlogn,本题数据量很小可以接受直接暴力枚举放黄油的点。
直接模板求最短路,然后算距离即可。本着复习的原则,本题用spfa算法
spfa算法认为,只有被更新过的点,才能更新后继的结点。即如果一个节点没有被更新过,那么再拿这个节点去更新其他结点是没有效果的。所以使用队列,存储被更新的结点,st表示这个节点是否在队列当中,防止队列里面加了相同的节点。
注意:st数组的使用、判断无解的条件。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N =810,M=3000,INF=0x3f3f3f3f;
int n,p,m;
int dist[N];
bool st[N];
int h[N],e[M],ne[M],w[M],idx;
int id[N];
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa(int start)
{
    memset(dist,0x3f,sizeof dist);
    memset(st,false,sizeof false);
    dist[start]=0;
    queue<int> q;
    q.push(start);
    st[start]=true;
    while(q.size())
    {
        int t=q.front();
        q.pop();
        st[t]=false;
        for(int i=h[t];~i;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                st[j]=true;
                q.push(j);
            }
        }
    }
    int res=0;
    for(int i=0;i<n;i++)
    {
        int j=id[i];
        if(dist[j]==INF) return INF;
        res+=dist[j];
    }
    return res;
}
int main()
{
    cin>>n>>p>>m;
    memset(h,-1,sizeof h);
    for(int i=0;i<n;i++) cin>>id[i];
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }
    int res=INF;
    for(int i=1;i<=p;i++) res=min(res,spfa(i));
    cout<<res;
}
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4279946/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。4.acwing1126

建图:人 作为点,A为起点B为终点,边长是扣除手续费,dist为保留原费用的百分比,即初始化A的dist为1,走向下一个点(假设扣除z%的手续费,即dist(a)*g,,g=(100-c)/100)
那么走到最后答案就是100/dist(b)
目标是让dist(b)尽量大,则跑最长路即可
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N =2010;
int n,m,S,T;
double g[N][N];
double dist[N];
bool st[N];
void dijkstra()
{
    dist[S]=1;
    for(int i=1;i<=n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||dist[t]<dist[j]))
                t=j;
        st[t]=true;
        for(int j=1;j<=n;j++)
            dist[j]=max(dist[j],dist[t]*g[t][j]);
    }
}
int main()
{
    cin>>n>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        double z=(100.0-c)/100;
        g[a][b]=g[b][a]=max(g[a][b],z);
    }
    cin>>S>>T;
    dijkstra();
    printf("%.8lf",100/dist[T]);
}
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4280167/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。5.acwing920

答案要求输出最少换乘次数。问题根本在如何建图使得求解方便上。
假设我们乘上了j号站,则我们下一步可选择到包含j号站的所有线路,的在j号站之后的所有站下车换乘。
据此考虑,我们把j号点与这条线路上之后的所有点加一条边,长度为1。这样就能使得建出来的图能够使用最短路算法求解。
因为长度为1,因此我们使用bfs算法即可求解最短路。
直接bfs求解的实际上是“坐车的次数”,答案应该是不换乘——0 和坐车的次数-1.
注意坐车的次数可以是0,因此答案表达式为
#include<iostream>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
using namespace std;
const int N =510;
int m,n;
int dist[N];
bool g[N][N];
bool st[N];
int stop[N];
void bfs()
{
    queue<int> q;
    memset(dist,0x3f,sizeof dist);
    q.push(1);
    dist[1]=0;
    while(q.size())
    {
        int t=q.front();
        q.pop();
        for(int i=1;i<=n;i++)
            if(g[t][i]&&dist[i]>dist[t]+1)
            {
                dist[i]=dist[t]+1;
                q.push(i);
            }
    }
}
int main()
{
    cin>>m>>n;
    string line;
    getline(cin,line);
    while(m--)
    {
        getline(cin,line);
        stringstream ssin(line);
        int cnt=0,p;
        while(ssin>>p) stop[cnt++]=p;
        for(int j=0;j<cnt;j++)//每个站点 与之后的 所有站点建一条单向边
            for(int k=j+1;k<cnt;k++)
                g[stop[j]][stop[k]]=true;
    } 
    bfs();//01bfs求最短路
    if(dist[n]==0x3f3f3f3f) puts("NO");
    else cout<<max(dist[n]-1,0);
    return 0;
}
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4327369/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。6.acwing903


题意:N=100,可以用邻接矩阵建图;物品--点,边--某个物品可以的替代品。边的意义即为,从这个替代品出发,花费代价w可以到达该物品,这样就可以用最短路。
与地位低的交易了,地位高度的不会和他交易:只能选择一个起点出发。
求解方法:我们总要选择一个起点出发,如何选择交给算法解决最好。因此我们可以设立一个虚拟原点,将它和所有物品连线,边的代价就是交易的代价。这样我们直接从虚拟源点开始跑最短路即可求得答案dist(1)。
等级差距超过了M则不能“间接交易”:因此我们需要限制间接交易的区间,区间必须要包括level1,并且为了防止遗漏最短路,必须要枚举所有包括了level1的区间。跑dijkstra受到等级区间限制,需要传入两个参数:等级最低限制和最高限制。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N =110 ,INF=0x3f3f3f3f;
int level[N];
int w[N][N];
bool st[N];
int dist[N];
int m,n;
int dijkstra(int down,int up)
{
    memset(dist,0x3f,sizeof dist);
    memset(st,false,sizeof st);
    dist[0]=0;
    for(int i=1;i<=n+1;i++)//n+1个点
    {
        int t=-1;
        for(int j=0;j<=n;j++)//注意是从0号点开始循环
            if((t==-1||dist[t]>dist[j])&&!st[j])
                t=j;
        st[t]=true;
        for(int j=1;j<=n;j++)
            if(level[j]>=down&&level[j]<=up)
                dist[j]=min(dist[j],dist[t]+w[t][j]);
    }
    return dist[1];
}
int main()
{
    cin>>m>>n;
    memset(w,0x3f,sizeof w);
    for(int i=0;i<=n;i++) w[i][i]=0;
    for(int i=1;i<=n;i++)
    {
        int price,cnt;
        cin>>price>>level[i]>>cnt;
        w[0][i]=min(price,w[0][i]);//虚拟原点 代表直接购买这件物品
        while(cnt--)
        {
            int id,cost;
            cin>>id>>cost;
            w[id][i]=min(cost,w[id][i]);
        }
    }
    int res=INF;
    for(int i=level[1]-m;i<=level[1];i++)  res=min(res,dijkstra(i,i+m));
    cout<<res;
    return 0;
}
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4327877/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。



















