POJ_1847_Tram(单源最短路径的三种算法实现)

题型:图论


题意

       有n个点构成的图(有向图),边代表铁轨,电车经过这个点到另一个点时,若铁轨为起始的铁轨方向则不用扳轨道,否则需要扳轨道后才能走,问从a点到b点最少要扳几次轨道,不可到达输出-1。

       首先输入n,a,b,下接n行,每一行第一个为Ki,代表从i点出发的铁轨数,然后输入Ki个数,代表到达的点。


分析

       转换一下,对于边<x,y>,假设从x点到y点需要扳一次轨道,那么就可以看成<x,y>的权值为1,若不需要扳轨道,那么权值为0。这样就把问题转换成了求从a到b的单源最短路径问题.

       为了扎实基本功,我分别手写Dijkstra,SPFA,Floyd三种算法过了此题。


代码

Dijkstra实现

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define MAXN 105
#define INF 0x3f3f3f3f
using namespace std;

int path[MAXN][MAXN];
int dis[MAXN],n,a,b,pre[MAXN];
bool vis[MAXN];

void init(){
    for(int i=0;i<=n;i++){
        for(int j=0;j<=n;j++){
            path[i][j]=path[j][i]=INF;
        }
    }
}
void Dijkstra(int start){
    int min,k;
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=n;i++){
        if(i!=start){
            dis[i]=path[start][i];
            pre[i]=start;
        }
    }
    dis[start]=0;
    vis[start]=true;
    for(int i=1;i<=n;i++){
        min=INF;
        k=0;
        for(int j=1;j<=n;j++){
            if(!vis[j] && dis[j]<min){
                min=dis[j];
                k=j;
            }
        }
        if(k==0) return;
        vis[k]=true;
        for(int j=1;j<=n;j++){
            if(!vis[j] && path[k][j]!=INF && dis[j]>dis[k]+path[k][j]){
                dis[j]=dis[k]+path[k][j];
                pre[j]=k;
            }
        }
    }
}

int main(){
    while(~scanf("%d%d%d",&n,&a,&b)){
        init();
        int num,x;
        for(int i=1;i<=n;i++){
            scanf("%d",&num);
            for(int j=0;j<num;j++){
                scanf("%d",&x);
                if(j==0) path[i][x]=0;
                else path[i][x]=1;
            }
        }
        Dijkstra(a);
        if(dis[b]==INF) printf("-1\n");
        else printf("%d\n",dis[b]);
    }
    return 0;
}


SPFA实现

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#define MAXN 123456
#define INF 0x3f3f3f3f
using namespace std;
int n,a,b,nc;
int head[MAXN];

struct Edge{
    int to,w,next;
}edge[MAXN];

void add(int i,int j){
    edge[nc].to=j;
    edge[nc].next=head[i];
    head[i]=nc++;
}

int dis[MAXN];
bool vis[MAXN];
bool SPFA(int start){
    int k,queue[MAXN],iq,top,outque[MAXN];
    for(int i=0;i<=n;i++)
        dis[i]=INF;
    memset(vis,false,sizeof(vis));
    memset(outque,0,sizeof(outque));
    iq=0;
    queue[iq++]=start;
    vis[start]=true;
    dis[start]=0;
    int i=0;
    while(i!=iq){
        top=queue[i];
        vis[top]=false;
        outque[top]++;
        if(outque[top]>n) return false;
        k=head[top];
        while(k>=0){
            if(dis[edge[k].to]-edge[k].w>dis[top]){
                dis[edge[k].to]=edge[k].w+dis[top];
                if(!vis[edge[k].to]){
                    vis[edge[k].to]=true;
                    queue[iq]=edge[k].to;
                    iq++;
                }
            }
            k=edge[k].next;
        }
        i++;
    }
    return true;
}

int main(){
    while(~scanf("%d%d%d",&n,&a,&b)){
        memset(head,-1,sizeof(head));
        nc=0;
        int num,x;
        for(int i=1;i<=n;i++){
            scanf("%d",&num);
            for(int j=0;j<num;j++){
                scanf("%d",&x);
                if(j==0) edge[nc].w=0;
                else edge[nc].w=1;
                add(i,x);
            }
        }
        if(SPFA(a)){
            if(dis[b]==INF) printf("-1\n");
            else printf("%d\n",dis[b]);
        }
        else printf("-1\n");
    }
    return 0;
}


Floyd实现

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#define INF 0x3f3f3f3f
using namespace std;
int path[105][105];
int main() {
    int n,a,b;
    while(~scanf("%d%d%d",&n,&a,&b)) {
        memset(path,INF,sizeof(path));
        int num,x;
        for(int i=1; i<=n; i++) {
            scanf("%d",&num);
            for(int j=1; j<=num; j++) {
                scanf("%d",&x);
                if(j==1)
                    path[i][x]=0;
                else
                    path[i][x]=1;
            }
        }
        for(int k=1; k<=n; k++) {
            for(int i=1; i<=n; i++) {
                for(int j=1; j<=n; j++) {
                    if(path[i][k]+path[k][j]<path[i][j])
                        path[i][j]=path[i][k]+path[k][j];
                }
            }
        }
        if(path[a][b]==INF) printf("-1\n");
        else printf("%d\n",path[a][b]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值