一二三四五 上山打老虎

PAT(A)1003 Emergency

题目链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805523835109376

题意:计算最短路和最短路的线路数目已经最短路中点权值和的最大值

思路: 多条最短路的变式题,只需要多定义个数组来维护线路数目和点权值和,当松弛操作进行更新时对于d[y]==d[x]+len 情况也进行讨论和两个数组的数据更新。

代码:
dijkstra

#include<bits/stdc++.h>

using namespace std;
int n,m,s,t;
int a[505];
struct node{
    int ne,v;
    node(int _n,int _v):ne(_n),v(_v){}
};
vector<struct node> edge[1005];
int vis[1005]={0};
int d[1005];
int lennum[1005]={0};//线数目
int sum[1005]={0};//最大点权值
priority_queue<pair<int,int>> q;
void dijk(){
    memset(d,0x3f,sizeof(d));
    q.push(make_pair(0,s));
    d[s]=0;
    lennum[s]=1;
    sum[s]=a[s];
    while(q.size()){
        int x=q.top().second;
        q.pop();
        if(vis[x])continue;
        vis[x]=1;//注意dij中v数组的意思是是否已经确定为最小值,所以需要在弹出最小根堆时赋值为1
        for(int i=0;i<edge[x].size();i++){
            int len=edge[x][i].v;
            int y=edge[x][i].ne;
            if(d[y]>d[x]+len){
                d[y]=d[x]+len;
                lennum[y]=lennum[x];
                sum[y]=sum[x]+a[y];
                q.push(make_pair(-d[y],y));
            }
            else if(d[y]==d[x]+len){//多条最短路变式题 需要对本情况进行讨论和数据更新
                lennum[y]+=lennum[x];
                sum[y]=max(sum[x]+a[y],sum[y]);
            }
        }
    }
    cout<<lennum[t]<<" "<<sum[t];
}
int main (){
    cin>>n>>m>>s>>t;
    for(int i=0;i<n;i++)cin>>a[i];
    int x,y,z;
    for(int i=0;i<m;i++){
        cin>>x>>y>>z;
        edge[x].push_back(node(y,z));
        edge[y].push_back(node(x,z));
    }
    dijk();
    return 0;
}

SPFA
通过SPFA进行计算多条最短路径变式题时应该注意,统计最短路径条数时应该加一个set来维护,如果路径条数发生变化也应该加到队列中,对于其他统计则不需要。

#include<bits/stdc++.h>

using namespace std;
int n,m,s,t;
struct node{
    int ne,w;
    node(int _ne,int _w):ne(_ne),w(_w){}
};
vector<struct node>edge[1005];
int d[1005];
int a[1005];
int vis[1005]={0};
queue<int>q;
int lensum[1005]={0};
int wsum[1005]={0};
set<int> pre[1005];
const int INF=0x3fffffff;
void spf(){
    fill(d,d+1005,INF);
    d[s]=0;
    q.push(s);
    vis[s]=1;
    lensum[s]=1;
    wsum[s]=a[s];
    set<int> ::iterator it;
    while(!q.empty()){
        int x=q.front();
        vis[x]=0;//SPFA中vis数组定义为 当前是否在队列中
        q.pop();
        for(int i=0;i<edge[x].size();i++){
            int y=edge[x][i].ne;
            int len=edge[x][i].w;
            if(d[y]>d[x]+len){
                d[y]=d[x]+len;
                if(!vis[y])
                    q.push(y);
                vis[y]=1;
                wsum[y]=wsum[x]+a[y];
                lensum[y]=lensum[x];
                pre[y].clear();
                pre[y].insert(x);
            }
            else if(d[y]==d[x]+len){
                if(wsum[x]+a[y]>wsum[y])wsum[y]=wsum[x]+a[y];
                pre[y].insert(x);
                lensum[y]=0;
                for(it=pre[y].begin();it!=pre[y].end();it++){//由于SPFA是根据有所更新的点进行更新,所以很多点可能重复更新多次,则计算最短路径条数时需要定义一个set来保存连接到当前点的其他点,当出现等于情况时进行重新计算。
                    lensum[y]+=lensum[*it];
                }
                if(!vis[y]){ //注意这里 在SPFA中队列内为有更新的点 ,其中更新不光是d,最短路径条数进行更新了也要加到队列中,
                    q.push(y);
                }
                vis[y]=1;
            }
        }
    }
    cout<<lensum[t]<<" "<<wsum[t];
  }
int main (){
    cin>>n>>m>>s>>t;
    for(int i=0;i<n;i++)cin>>a[i];
    int x,y,z;
    for(int i=0;i<m;i++){
        cin>>x>>y>>z;
        edge[x].push_back(node(y,z));
        edge[y].push_back(node(x,z));
    }
    spf();
    
    return 0;
}
posted @ 2021-03-02 10:25  黒川川  阅读(52)  评论(0)    收藏  举报