PAT甲级1003

题面略。

思路:其目的是求s(source)到d(destination)的单源最短路径,比较容易想到的是dijikstra算法。

回顾dijisktra算法的流程:将图中所有顶点分为两个集合,一个N,一个U,N表示该集合中的顶点已经访问过,U表示该集合中的顶点还没有访问。

1)初始化:准备一个数组d,d[i]表示从起点到顶点i的最短路径,初始值等于起点与该顶点之间的边的代价cost[s][i],如果并未直接相邻,则令其值为无穷大。

2)遍历顶点:从目前U集合顶点中 d[i] 最小的顶点开始访问,假设这个顶点是 minV,将顶点 minV 加入到N中,再更新顶点 minV 的所有邻居顶点 w 的d[w] = min(d[w], d[minV]+cost[minV][w]).

3)重复步骤2,直到U为空为止。

题解步骤:除了dijisktra的必要初始化之外,由于需要记录最短路径的数目和救援队的数目,先初始化一个amount数组,amount[ i ]表示从s到顶点 i 的最短路径上的最大的救援队伍的数目(值得注意的是,初始化时,amount[s]=team[s], 而其他顶点amount[i] = amount[s]+team[i], 这是因为要到顶点i必然从s出发,所以要加上s中的救援队;不要在准备输出的时候才加上s中的队伍数目,因为有一个检查点是,输入的s和d相同,这样就会出错。);还有一个mind数组,mind[i] 表示从起点s 到顶点i 的最短路径的数目。

根据dijisktra的最优子结构对mind数组,amount数组和d数组进行更新。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

int t[501];
int amount[501];
int mind[501];
int cost[501][501];
int ds[501];
int ischecked[501];
int n,m,s,d;
const int inf=(1<<30)-1;

void dij(){
    amount[s]=t[s];
    mind[s]=1;
    for(int i=0;i<n;i++){
        ds[i]=cost[s][i];
        ischecked[i]=false;

        if(ds[i]!=inf&&i!=s){
            mind[i]=1;
            amount[i]=t[i]+amount[s];
        }
    }
    ischecked[s]=true;
    for(int node=0;node<n;node++){
        int minV=-1;
        int minC=inf;
        //选最短距离最小的顶点
        for(int i=0;i<n;i++){
            if(!ischecked[i]&&ds[i]<minC){
                minC=ds[i];
                minV=i;
            }
        }
        if(minV==-1){
            continue;
        }
        //访问此顶点
        ischecked[minV]=true;
        for(int i=0;i<n;i++){
            //查看此顶点的所有未访问邻居
            if(!ischecked[i]&&cost[minV][i]<inf){
                //如果两种路径相等,则更新最短路径的数目,更新队伍的数目
                if(ds[i]==ds[minV]+cost[minV][i]){
                    mind[i]+=mind[minV];
                    amount[i]=max(amount[i],amount[minV]+t[i]);
                }
                //如果比原路径小,更新为最短路径,最短路径数目为1.
                else if(ds[i]>ds[minV]+cost[minV][i]){
                    ds[i]=ds[minV]+cost[minV][i];
                    mind[i]=mind[minV];
                    amount[i]=amount[minV]+t[i];
                }
            }
        }
    }
}

int main(void){
    scanf("%d %d %d %d",&n,&m,&s,&d);
    for(int i=0;i<n;i++){
        scanf("%d",&t[i]);
    }
    for(int i=0;i<n;i++){
        for(int j=i+1;j<n;j++){
            cost[i][j]=cost[j][i]=inf;
        }
    }
    for(int i=0;i<m;i++){
        int c1,c2,c;
        scanf("%d %d %d",&c1,&c2,&c);

        if(c<cost[c1][c2]){
            cost[c1][c2]=cost[c2][c1]=c;
        }
    }
    dij();
    printf("%d %d",mind[d],amount[d]);
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值