最短路+bfs

最短路
Description
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。输入保证至少存在1条商店到赛场的路线。
当输入为两个0时,输入结束。

Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间。

Sample Input
3 3
1 2 1
2 3 1
1 3 3
3 3
1 2 2
2 3 1
1 3 1
0 0
Sample Output
2
1

#include<bits/stdc++.h>
using namespace std;
#define INF 999999999
int n,m,edge[1001][1001],bj[1001],dp[1001];
void djstl();
int main()
{
    int i,j,a,b,c;
    while(2==scanf("%d%d",&n,&m))
    {
        memset(bj,false,sizeof(bj));
        if(n==0&&m==0)
        {
            break;
        }
        for(i=1;i<=1000;i++)
        {
            dp[i]=INF;
            for(j=1;j<=1000;j++)
            {
                edge[i][j]=INF;
            }
        }
        dp[1]=0;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            edge[a][b]=min(c,edge[a][b]);
            edge[b][a]=min(c,edge[b][a]);
        }
        djstl();
        cout<<dp[n]<<endl;
    }

    return 0;
}
void djstl()
{
    int i,j,jh,zx;
    for(i=1;i<=n;i++)
    {
        jh=-1;
        zx=INF;
        for(j=1;j<=n;j++)
        {
            if(!bj[j]&&dp[j]<zx)
            {
                jh=j;
                zx=dp[j];
            }
        }
        bj[jh]=true;
        for(j=1;j<=n;j++)
        {
            if(!bj[j])
            {
                dp[j]=min(dp[j],dp[jh]+edge[jh][j]);
            }
        }
    }
}

算法7-15:迪杰斯特拉最短路径算法
Description
在带权有向图G中,给定一个源点v,求从v到G中的其余各顶点的最短路径问题,叫做单源点的最短路径问题。
在常用的单源点最短路径算法中,迪杰斯特拉算法是最为常用的一种,是一种按照路径长度递增的次序产生最短路径的算法。
可将迪杰斯特拉算法描述如下:

在本题中,读入一个有向图的带权邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法求出源点至每一个其它顶点的最短路径长度。
Input
输入的第一行包含2个正整数n和s,表示图中共有n个顶点,且源点为s。其中n不超过50,s小于n。
以后的n行中每行有n个用空格隔开的整数。对于第i行的第j个整数,如果大于0,则表示第i个顶点有指向第j个顶点的有向边,且权值为对应的整数值;如果这个整数为0,则表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0。
Output
只有一行,共有n-1个整数,表示源点至其它每一个顶点的最短路径长度。如果不存在从源点至相应顶点的路径,输出-1。
请注意行尾输出换行。
Sample Input
4 1
0 3 0 1
0 0 4 0
2 0 0 0
0 0 1 0
Sample Output
6 4 7
Hint
在本题中,需要按照题目描述中的算法完成迪杰斯特拉算法,并在计算最短路径的过程中将每个顶点是否可达记录下来,直到求出每个可达顶点的最短路径之后,算法才能够结束。
迪杰斯特拉算法的特点是按照路径长度递增的顺序,依次添加下一条长度最短的边,从而不断构造出相应顶点的最短路径。
另外需要注意的是,在本题中为了更方便的表示顶点间的不可达状态,可以使用一个十分大的值作为标记。

#include<bits/stdc++.h>
using namespace std;
#define INF 999999999
int dp[101][101];
int n;
void floyd();
int main()
{
    int i,j,x,m;
    cin>>n>>m;
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
        {
            dp[i][j]=INF;
        }
    }
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
        {
            scanf("%d",&x);
            if(x!=0)
            {
                dp[i][j]=x;
            }
        }
    }
    floyd();
    for(i=0;i<n;i++)
    {
        if(i==m)
        {
            continue;
        }
        if(dp[m][i]==INF)
        {
            cout<<-1<<' ';
        }
        else
        {
            cout<<dp[m][i]<<' ';
        }
    }
    cout<<endl;
    return 0;
}
void floyd()
{
    int i,j,k;
    for(k=0;k<n;k++)
    {
        for(i=0;i<n;i++)
        {
            for(j=0;j<n;j++)
            {
                if(dp[i][k]!=INF&&dp[k][j]!=INF)
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
            }
        }
    }
}

算法7-16:弗洛伊德最短路径算法
Description
在带权有向图G中,求G中的任意一对顶点间的最短路径问题,也是十分常见的一种问题。
解决这个问题的一个方法是执行n次迪杰斯特拉算法,这样就可以求出每一对顶点间的最短路径,执行的时间复杂度为O(n3)。
而另一种算法是由弗洛伊德提出的,时间复杂度同样是O(n3),但算法的形式简单很多。
可以将弗洛伊德算法描述如下:

在本题中,读入一个有向图的带权邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法求出每一对顶点间的最短路径长度。

Input
输入的第一行包含1个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数。对于第i行的第j个整数,如果大于0,则表示第i个顶点有指向第j个顶点的有向边,且权值为对应的整数值;如果这个整数为0,则表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0。
Output
共有n行,每行有n个整数,表示源点至每一个顶点的最短路径长度。如果不存在从源点至相应顶点的路径,输出-1。对于某个顶点到其本身的最短路径长度,输出0。
请在每个整数后输出一个空格,并请注意行尾输出换行。
Sample Input
4
0 3 0 1
0 0 4 0
2 0 0 0
0 0 1 0
Sample Output
0 3 2 1
6 0 4 7
2 5 0 3
3 6 1 0
Hint
在本题中,需要按照题目描述中的算法完成弗洛伊德算法,并在计算最短路径的过程中将每个顶点是否可达记录下来,直到求出每一对顶点的最短路径之后,算法才能够结束。
相对于迪杰斯特拉算法,弗洛伊德算法的形式更为简单。通过一个三重循环,弗洛伊德算法可以方便的求出每一对顶点间的最短距离。
另外需要注意的是,为了更方便的表示顶点间的不可达状态,可以使用一个十分大的值作为标记。而在题目描述中的算法示例使用了另外一个三维数组对其进行表示,这使原本的O(n3)时间复杂度增长到了O(n4),这也是需要自行修改的部分。

#include<bits/stdc++.h>
using namespace std;
#define INF 999999999
int dp[101][101];
int n;
void floyd();
int main()
{
    int i,j,x;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            dp[i][j]=INF;
        }
    }
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            scanf("%d",&x);
            if(x!=0)
            {
                dp[i][j]=x;
            }
        }
    }
    floyd();
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            if(i==j)
            {
                dp[i][j]=0;
                continue;
            }
            if(dp[i][j]==INF)
            {
                dp[i][j]=-1;
                continue;
            }
        }
    }
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            printf("%d ",dp[i][j]);
        }
        printf("\n");
    }
    return 0;
}
void floyd()
{
    int i,j,k;
    for(k=1;k<=n;k++)
    {
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                if(dp[i][k]!=INF&&dp[k][j]!=INF)
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
            }
        }
    }
}

最短路上的统计
Description
一个无向图上,没有自环,所有边的权值均为1,对于一个点对(a,b),我们要把所有a与b之间所有最短路上的点的总个数输出

Input
第一行n,m,表示n个点,m条边
接下来m行,每行两个数ai,bi,表示ai,bi之间有条边
在下来一个数p,表示问题的个数
接下来p行,每行两个数aj,bj,表示询问aj,bj(若两点不连通输出0)
Output
对于每个询问,输出一个数c,表示最短路上点的总个数

Sample Input
5 6
1 2
1 3
2 3
2 4
3 5
4 5
3
2 5
5 1
2 4
Sample Output
4
3
2
Hint
2<=n<=100,p<=5000
1<=ai,bi,aj,bj<=n
ai != bi

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3fffffff
int dp[101][101],n,m;
void floyd();
bool pd(int x,int y,int z);
int main()
{
    int u,v,i,q,j,gs;
    gs=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            dp[i][j]=INF;
        }
    }
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        dp[u][v]=1;
        dp[v][u]=1;
    }
    floyd();
    scanf("%d",&q);
    for(i=1;i<=q;i++)
    {
        gs=0;
        scanf("%d%d",&u,&v);
        for(j=1;j<=n;j++)
        {
            if(pd(u,v,j))
            {
                gs++;
            }
        }

        if(dp[u][v]!=INF)
        {
            gs+=2;
        }
        if(u==v)
        {
            gs=1;
        }
        cout<<gs<<endl;
    }
    return 0;
}
void floyd()
{
    int i,j,k;
    for(k=1;k<=n;k++)
    {
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                if(dp[i][k]!=INF&&dp[k][j]!=INF)
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
            }
        }
    }
}
bool pd(int x,int y,int z)
{
    if(x==z||y==z||dp[x][z]+dp[z][y]!=dp[x][y])
    {
        return false;
    }
    return true;
}

最短路
Description
一个无向图有N个顶点,M条边,每条边都有边长,求1点到N点的最短距离。

Input
一行是两个整数N、M(N<=1000,M<=100000)。接下来M行,每行三个整数u, v,w(1<=u,v<=N,1<=w<=1000),表示u点和v点之间有一条边长为w的边。不存在重边或自环,保证1点到N点有路径。单样例输入。

Output
输出一个整数,表示1点到N点之间的最短路长度。

Sample Input
2 1
1 2 3

3 3
1 2 5
2 3 5
3 1 2
Sample Output
3

2

#include<bits/stdc++.h>
using namespace std;
#define INF 999999999
int n,m,edge[1001][1001],bj[1001],dp[1001];
void djstl();
int main()
{
    int i,j,a,b,c;
    scanf("%d%d",&n,&m);
    for(i=1;i<=1000;i++)
    {
        dp[i]=INF;
        for(j=1;j<=1000;j++)
        {
            edge[i][j]=INF;
        }
    }
    dp[1]=0;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        edge[a][b]=c;
        edge[b][a]=c;
    }
    djstl();
    cout<<dp[n]<<endl;
    return 0;
}
void djstl()
{
    int i,j,jh,zx;
    for(i=1;i<=n;i++)
    {
        jh=-1;
        zx=INF;
        for(j=1;j<=n;j++)
        {
            if(!bj[j]&&dp[j]<zx)
            {
                jh=j;
                zx=dp[j];
            }
        }
        bj[jh]=true;
        for(j=1;j<=n;j++)
        {
            if(!bj[j])
            {
                dp[j]=min(dp[j],dp[jh]+edge[jh][j]);
            }
        }
    }
}

Floyd
Description
一个无向图上,没有自环,对于一个点对(u,v),要求得它们之间的最短路长度。

Input
第一行n,m,表示n个点,m条边
接下来m行,每行三个数ui,vi,wi,表示ui,vi之间有一条长为wi的边相连
在下来一个数q,表示问题的个数
接下来q行,每行两个数uj,vj,表示询问uj,vj(若两点不连通输出No Path)
Output
输出最短路径长度

Sample Input
5 6
1 2 1
1 3 1
2 3 1
2 4 1
3 5 1
4 5 1
3
2 5
5 1
2 4
Sample Output
2
2
1
Hint
2<=n<=100,q<=5000
1<=ui,vi,uj,vj<=n
ui != vi

#include<bits/stdc++.h>
using namespace std;
#define INF 999999999
int dp[101][101];
int n,m;
void floyd();
int main()
{
    int i,j,a,b,c,q,u,v;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            dp[i][j]=INF;
        }
    }
    for(i=1;i<=m;i++)
    {
       scanf("%d%d%d",&a,&b,&c);
       dp[a][b]=c;
       dp[b][a]=c;
    }
    floyd();
    cin>>q;
    for(i=1;i<=q;i++)
    {
        scanf("%d%d",&u,&v);
        if(u==v)
        {
            cout<<0<<endl;
            continue;
        }
        if(dp[u][v]==INF)
        {
            cout<<"No Path"<<endl;
        }
        else
        {
            cout<<dp[u][v]<<endl;
        }
    }
    return 0;
}
void floyd()
{
    int i,j,k;
    for(k=1;k<=n;k++)
    {
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                if(dp[i][k]!=INF&&dp[k][j]!=INF)
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
            }
        }
    }
}

传送阵
Description
你的周围有若干个传送阵,每个传送阵都有一个得分,第一次传送到这个传送阵,你就可以得到这个分数。还有若干对传送阵是互相连通的,你从一个传送阵到另外一个传送阵需要一些时间。游戏规定了你的起点和终点传送阵,你首要目标是从起点尽快到达终点,在满足首要目标的前提下,使得你的得分总和尽可能大。你能计算在尽快抵达终点的前提下,你的最大得分是多少么?

Input
第一行4个整数n (<=500), m, start, end。n表示传送门的个数,传送门编号从0到(n - 1),m表示连通传送门对数,任意两个传送门之间最多只有一条道路,start和end表示起点和终点传送门的编号。
第二行包含n个空格分隔的正整数(不超过600),表示进入每个传送门你的得分。
再接下来m行,每行3个空格分隔的整数x, y, z (0<z<=200),表示从传送门x到传送门y(双向)你需要的时间为z。
输入保证从start到end至少有一条路径。

Output
一行,两个空格分隔的整数,第一个表示你最少需要的时间,第二个表示你在最少时间前提下可以获得的最大得分。

Sample Input
3 2 0 2
1 2 3
0 1 10
1 2 11
Sample Output
21 6

#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
#define inf 0x3FFFFFFF
int n,m,ks,js,dp[501][501],mi[501],mx[501],z[501];
bool bj[501];
void djstl();
int main()
{
    int i,j,a,b,c,d;
    while(cin>>n>>m>>ks>>js)
    {
        memset(bj,0,sizeof(bj));
        for(i=0;i<=500;i++)
        {
            mi[i]=inf;
            mx[i]=0;
            for(j=0;j<=500;j++)
            {
                if(i==j)
                {
                    dp[i][j]=0;
                }
                else
                {
                    dp[i][j]=inf;
                }
            }
        }
        for(i=0;i<n;i++)
        {
            scanf("%d",&z[i]);
        }
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            dp[a][b]=c;
            dp[b][a]=c;
        }
        mi[ks]=0;
        mx[ks]=z[ks];
        djstl();
        cout<<mi[js]<<" "<<mx[js]<<endl;
    }
    return 0;
}
void djstl()
{
    int i,j,jh,zx;
    for(i=0;i<n;i++)
    {
        jh=-1;
        zx=inf;
        for(j=0;j<n;j++)
        {
            if(!bj[j]&&mi[j]<zx)
            {
                jh=j;
                zx=mi[j];
            }
        }
        bj[jh]=true;
        for(j=0;j<n;j++)
        {
            if(!bj[j])
            {
                if(mi[jh]+dp[jh][j]<mi[j])
                {
                    mi[j]=mi[jh]+dp[jh][j];
                    mx[j]=mx[jh]+z[j];
                }
                else
                {
                    if(mi[jh]+dp[jh][j]==mi[j])
                    {
                        mx[j]=max(mx[j],mx[jh]+z[j]);
                    }
                }
            }
        }
    }
}

利比亚行动
Description
2011年3月16日以来,利比亚爆发的骚乱不断升级,已严重危及到普通民众和各国在利比亚工作的人员的安全。为了尽快救出在利比亚的同胞,根据利比亚的形势,我国政府告诉每个在利比亚的公民,如何行动才能最快地到达安全的地方,然后由我国派出的飞机、轮船、汽车接回国。

   假设利比亚的地图可以描述为一个n行m列的长方形,待拯救的同胞小A在1行1列处,安全的目标位置在n行m列处。小A每次只能向相邻的上、下、左、右四个方向移动,即如果小A现在的位置是i行j列,小A的下一步位置将到达i-1行j列、i+1行j列、i行j-1列、i行j+1列这四个位置之一,当然小A不能移出n行m列的长方形。

  利比亚是一个多沙漠且地形复杂的国家,某些位置是很危险的,人不能去。

  给出利比亚的地图,请告诉小A从起点(1,1)走到终点(n,m)最快需要多少步呢?。

Input
第一行有2个正整数n,m (1≤n≤2000,1≤m≤2000),它们之间以一个空格分隔,表示利比亚的地形可以分为n行m列。

接下来n行,每行m个字符,分别表示地图中该位置的信息。其中:

字符“”表示这个位置是建筑物、河流、有地雷等人无法走到的位置(保证起点终点不是“”);

小数点“.”表示人可以走到该位置。

Output
输出只有一行,该行只有一个正整数。表示为小A从起点到终点,最快需要多少步,如果不能到达输出-1。

Sample Input
3 5
.
.
.
Sample Output
8
Hint
60%的数据中,1≤n≤100,1≤m≤100;

80%的数据中,1≤n≤250,1≤m≤250;

90%的数据中,1≤n≤500,1≤m≤500;

100%的数据中,1≤n≤2000,1≤m≤2000。

#include<bits/stdc++.h>
using namespace std;
struct jg
{
    int x,y,t;
};
string a[2001];
int bfs();
jg dq,hm;
int n,m,dir[4][2]={0,-1,0,1,1,0,-1,0};
bool bj[2001][2001];
int main()
{
    int i,j;
    cin>>n>>m;
    for(i=0;i<n;i++)
    {
        cin>>a[i];
    }
    cout<<bfs()<<endl;
    return 0;
}
int bfs()
{
    int i,dx,dy;
    queue<jg>q;
    dq.x=0;
    dq.y=0;
    dq.t=0;
    bj[0][0]=true;
    q.push(dq);
    while(!q.empty())
    {
        dq=q.front();
        q.pop();
        if(dq.x==n-1&&dq.y==m-1)
        {
            return dq.t;
        }
        for(i=0;i<4;i++)
        {
            dx=dq.x+dir[i][0];
            dy=dq.y+dir[i][1];
            if(dx<0||dy<0||dx>=n||dy>=m||a[dx][dy]=='*')
            {
                continue;
            }
            if(bj[dx][dy])
            {
                continue;
            }
            hm.x=dx;
            hm.y=dy;
            hm.t=dq.t+1;
            bj[dx][dy]=true;
            q.push(hm);
        }
    }
    return -1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值