最短路(基础板子)

1.朴素版Dijkstra

题目:A - Til the Cows Come Home

#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int mp[1010][1010],dist[1010],vis[1010];
void Dij()
{
    for(int i=1;i<=n;i++)dist[i]=mp[n][i];
    vis[n]=1;
    for(int i=1;i<=n;i++)
    {
        int f=-1;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&(f==-1||dist[f]>dist[j]))f=j;
        }
        vis[f]=1;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j])dist[j]=min(dist[j],dist[f]+mp[f][j]);
        }
    }
    printf("%d\n",dist[1]);
}
int main()
{
    while(~scanf("%d%d",&m,&n))
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(i==j)mp[i][j]=0;
                else mp[i][j]=INF;
            }
        }
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(mp[x][y]>z)mp[x][y]=mp[y][x]=z;
        }
        memset(vis,0,sizeof(vis));
        Dij();
    }
    return 0;
}

2.堆优化Dijkstra

题目:
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。

请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入格式
第一行包含整数 n 和 m。

接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。

如果路径不存在,则输出 −1。

数据范围
1≤n,m≤1.5×105,
图中涉及边长均不小于 0,且不超过 10000。

输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3

const int N=150010;
int n,m;
int h[N],w[N],e[N],ne[N],idx;
typedef pair<int,int>PII;
int dist[N],vis[N];
void add(int a,int b,int c)
{
    e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
void Dij()
{
    priority_queue<PII,vector<PII>,greater<PII> >heap;
    dist[1]=0;
    heap.push({0,1});
    while(!heap.empty())
    {
        PII t=heap.top();heap.pop();
        int ver=t.second,dis=t.first;
        if(vis[ver])continue;
        vis[ver]=1;
        for(int i=h[ver];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dis+w[i])
            {
                dist[j]=dis+w[i];
                heap.push({dist[j],j});
            }
        }
    }
    if(dist[n]==INF)printf("-1\n");
    else printf("%d\n",dist[n]);
}
int main()
{
    memset(h,-1,sizeof(h));
    memset(vis,0,sizeof(vis));
    memset(dist,INF,sizeof(dist));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    Dij();
    return 0;
}

3.Bellman-Ford算法

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。

请你求出从 1 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 impossible。

注意:图中可能 存在负权回路 。

输入格式
第一行包含三个整数 n,m,k。

接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式
输出一个整数,表示从 1 号点到 n 号点的最多经过 k 条边的最短距离。

如果不存在满足条件的路径,则输出 impossible。

数据范围
1≤n,k≤500,
1≤m≤10000,
任意边长的绝对值不超过 10000。

输入样例:
3 3 1
1 2 1
2 3 1
1 3 3
输出样例:
3

int n,m,k;
struct edges
{
    int a,b,w;
}edge[10010];
int dist[510],backup[510];
void bellman()
{
    memset(dist,INF,sizeof(dist));
    dist[1]=0;
    for(int i=0;i<k;i++)
    {
        memcpy(backup,dist,sizeof(dist));
        for(int j=1;j<=m;j++)
        {
            int a=edge[j].a,b=edge[j].b,w=edge[j].w;
            dist[b]=min(dist[b],w+backup[a]);
        }
    }
    if(dist[n]>INF/2)printf("impossible\n");
    else printf("%d\n",dist[n]);
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].w);
    }
    bellman();
    return 0;
}

4.SPFA算法

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。

请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 impossible。

数据保证不存在负权回路。

输入格式
第一行包含整数 n 和 m。

接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。

如果路径不存在,则输出 impossible。

数据范围
1≤n,m≤105,
图中涉及边长绝对值均不超过 10000。

输入样例:
3 3
1 2 5
2 3 -3
1 3 4
输出样例:
2

const int N=100010;
int h[N],w[N],e[N],ne[N],idx;
int dist[N],vis[N];
int n,m;
void add(int a,int b,int c)
{
    e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
void SPFA()
{
    memset(dist,INF,sizeof(dist));
    memset(vis,0,sizeof(vis));
    queue<int>q;
    dist[1]=0;
    q.push(1);
    vis[1]=1;
    while(!q.empty())
    {
        int t=q.front();
        q.pop();
        vis[t]=0;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>w[i]+dist[t])
            {
                dist[j]=w[i]+dist[t];
                if(!vis[j])
                {
                    q.push(j);
                    vis[j]=1;
                }
            }
        }
    }
    if(dist[n]==INF)printf("impossible\n");
    else printf("%d\n",dist[n]);
}
int main()
{
    memset(h,-1,sizeof(h));
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    SPFA();
    return 0;
}

5.Floyd算法

某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多。这让行人很困扰。

现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。
Input
本题目包含多组数据,请处理到文件结束。
每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。
接下来是M行道路信息。每一行有三个整数A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路。
再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。
Output
对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1.
Sample Input
3 3
0 1 1
0 2 3
1 2 1
0 2
3 1
0 1 1
1 2
Sample Output
2
-1

int mp[210][210];
int n,m;
int s,t;
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(i==j)mp[i][j]=0;
                else mp[i][j]=INF;
            }
        }
        for(int i=1;i<=m;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            if(mp[a][b]>c)mp[a][b]=mp[b][a]=c;
        }
        scanf("%d%d",&s,&t);
        for(int k=0;k<n;k++)
        {
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<n;j++)
                {
                    if(mp[i][j]>mp[i][k]+mp[k][j])mp[i][j]=mp[i][k]+mp[k][j];
                }
            }
        }
        if(mp[s][t]==INF)printf("-1\n");
        else printf("%d\n",mp[s][t]);
    }
    return 0;
}

6.bellmon判断是否存在正环

题目:E - Currency Exchange

int n,m,s;
double v;
struct cur
{
    int a,b;
    double r,c;
}cc[210];
double dist[110];
void bellmon()
{
    memset(dist,0,sizeof(dist));
    dist[s]=v;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=2*m;j++)
        {
            if((dist[cc[j].a]-cc[j].c)*cc[j].r>dist[cc[j].b])
                dist[cc[j].b]=(dist[cc[j].a]-cc[j].c)*cc[j].r;
        }
    }
    int flag=0;
    for(int j=1;j<=2*m;j++)
    {
        if((dist[cc[j].a]-cc[j].c)*cc[j].r>dist[cc[j].b])
        {
            flag=1;
            break;
        }
    }
    if(flag==1)printf("YES\n");
    else printf("NO\n");
}
int main()
{
    while(~scanf("%d%d%d%lf",&n,&m,&s,&v))
    {
        for(int i=1;i<=m;i++)
        {
            int a,b;
            double rab,cab,rba,cba;
            scanf("%d%d%lf%lf%lf%lf",&a,&b,&rab,&cab,&rba,&cba);
            cc[i].a=a;cc[i].b=b;cc[i].r=rab;cc[i].c=cab;
            cc[i+m].a=b;cc[i+m].b=a;cc[i+m].r=rba;cc[i+m].c=cba;
        }
        bellmon();
    }
    return 0;
}

7…bellmon判断是否存在负环

F - Wormholes

int t,n,m,w;
struct path
{
    int a,b,c;
}pp[5500];
int dist[510];
void bellmon()
{
    memset(dist,INF,sizeof(dist));
    dist[1]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m*2+w;j++)
        {
            if(dist[pp[j].b]>dist[pp[j].a]+pp[j].c&&dist[pp[j].a]!=INF)
                dist[pp[j].b]=dist[pp[j].a]+pp[j].c;
        }
    }
    int flag=0;
    for(int j=1;j<=m*2+w;j++)
    {
        if(dist[pp[j].b]>dist[pp[j].a]+pp[j].c)
        {
            flag=1;
            break;
        }
    }
    if(flag==1)printf("YES\n");
    else printf("NO\n");
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&w);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&pp[i].a,&pp[i].b,&pp[i].c);
            pp[i+m].a=pp[i].b;pp[i+m].b=pp[i].a;pp[i+m].c=pp[i].c;
        }
        for(int i=m*2+1;i<=m*2+w;i++)
        {
            scanf("%d%d%d",&pp[i].a,&pp[i].b,&pp[i].c);
            pp[i].c=-pp[i].c;
        }
        bellmon();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值