最短路径(增加有源点)

https://nanti.jisuanke.com/t/41349

题意:n个救火点,m条无向边,s救火英雄所在救火点,k个消防大队,c值。

比较:救火英雄到各个救火点距离的最大值/c,与每个消防队到各个救火点的距离最大值比较,输出距离小的距离。

解法1:两次dijkstra , 救火英雄到各个救火点最大值。可以将消防队看成一个整体(都已标记),两种方法可以实现:
1、将所有消防队之间连边且权值为0,这样可以使算法实现过程中,优先选这些点,然后更新。
2、增加一个有源点,将该点与所有消防队连边且权值为0.
注意:处理消防队的增加边的过程要在第一次dijikstra之后第二次之前。
//#include<bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
#include <stdio.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <string.h>
#include <vector>
#include <stdlib.h>
using namespace std;
typedef long long ll ;
#define int ll
#define mod 100
#define gcd(m,n) __gcd(m, n)
#define rep(i , j , n) for(int i = j ; i <= n ; i++)
#define red(i , n , j)  for(int i = n ; i >= j ; i--)
#define ME(x , y) memset(x , y , sizeof(x))
//int lcm(int a , int b){return a*b/gcd(a,b);}
//ll quickpow(ll a , ll b){ll ans=1;while(b){if(b&1)ans=ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
//int euler1(int x){int ans=x;for(int i=2;i*i<=x;i++)if(x%i==0){ans-=ans/i;while(x%i==0)x/=i;}if(x>1)ans-=ans/x;return ans;}
//const int N = 1e7+9; int vis[n],prime[n],phi[N];int euler2(int n){ME(vis,true);int len=1;rep(i,2,n){if(vis[i]){prime[len++]=i,phi[i]=i-1;}for(int j=1;j<len&&prime[j]*i<=n;j++){vis[i*prime[j]]=0;if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;}else{phi[i*prime[j]]=phi[i]*phi[prime[j]];}}}return len}
#define INF  0x3f3f3f3f
#define PI acos(-1)
#define pii pair<int,int>
#define fi first
#define se second
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define pb push_back
#define mp make_pair
#define all(v) v.begin(),v.end()
#define size(v) (int)(v.size())
#define cin(x) scanf("%lld" , &x);
const int N = 1e6+9;
const int maxn = 1e3+9;
const double esp = 1e-6;
int head[maxn] , tol;
int dis[maxn] , vis[maxn];
int ans1 , ans2;
int team[maxn];
struct node{
    int v , w , next;
}g[N<<2];
struct Edge{
    int v , w ;
    bool operator < (const Edge &e) const{
        return w > e.w ;
    }
    Edge(int _v , int _w){
        v = _v , w = _w;
    }
    Edge(){}
};
void add(int u , int v , int w){
    g[++tol] = {v , w , head[u]};
    head[u] = tol;
}

void init(){
    ME(head , 0);
    ME(vis , 0);
    tol = 0;
    ans1 = 0 ;
    ans2 = 0 ;
}

void dijkstra(int u){
    ME(vis, 0);
    fill(dis , dis+maxn , INF);
    dis[u] = 0 ;
    priority_queue<Edge>q;
    q.push(Edge(u , dis[u]));
    Edge now;
    while(!q.empty()){
        now = q.top() ; q.pop();
        if(vis[now.v]) continue;
        vis[now.v] = 1 ;
        for(int i = head[now.v] ; i ; i = g[i].next){
            int v = g[i].v , w = g[i].w;
            if(!vis[v] && dis[v] > dis[now.v] + w){
                dis[v] = dis[now.v] + w;
                q.push(Edge(v , dis[v]));
            }
        }
    }
}

void solve(){
    init();
    int n , m , s , k , c ;
    scanf("%lld%lld%lld%lld%lld" ,&n , &m , &s , &k , &c);
    rep(i , 1 , k){
        scanf("%lld" , &team[i]);
    }
    rep(i , 1 , m){
        int u , v , w ;
        scanf("%lld%lld%lld" , &u , &v , &w);
        add(u , v , w);
        add(v , u , w);
    }
    dijkstra(s);
    rep(i , 1 , n){
        ans1 = max(ans1 , dis[i]);
    }
    rep(i , 1 , k){
        rep(j , i+1 , k){
            add(team[i] , team[j] , 0);
            add(team[j] , team[i] , 0);
        }
    }
    dijkstra(team[1]);
    /*rep(i , 1 , k){增加一个点作为消防队的源点
        add(n+1 , team[i] , 0);
        add(team[i] , n+1 , 0);
    }
    dijkstra(n+1);*/
    rep(i , 1 , n){
        ans2 = max(ans2 , dis[i]);
    }
    if(ans1 <= ans2 * c){
        cout << ans1 << endl;
    }else{
        cout << ans2 << endl;
    }
}

signed main()
{
    int t ;
    cin >> t ;
    while(t--){
        solve();
    }
}

解法2:spfa

//#include<bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
#include <stdio.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <string.h>
#include <vector>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll ;
#define int ll
#define mod 10000
#define gcd __gcd
#define rep(i , j , n) for(int i = j ; i <= n ; i++)
#define red(i , n , j)  for(int i = n ; i >= j ; i--)
#define ME(x , y) memset(x , y , sizeof(x))
int lcm(int a , int b){return a*b/gcd(a,b);}
//ll quickpow(ll a , ll b){ll ans=1;while(b){if(b&1)ans=ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
//int euler1(int x){int ans=x;for(int i=2;i*i<=x;i++)if(x%i==0){ans-=ans/i;while(x%i==0)x/=i;}if(x>1)ans-=ans/x;return ans;}
//const int N = 1e7+9; int vis[n],prime[n],phi[N];int euler2(int n){ME(vis,true);int len=1;rep(i,2,n){if(vis[i]){prime[len++]=i,phi[i]=i-1;}for(int j=1;j<len&&prime[j]*i<=n;j++){vis[i*prime[j]]=0;if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;}else{phi[i*prime[j]]=phi[i]*phi[prime[j]];}}}return len}
#define INF  0x3f3f3f3f
#define PI acos(-1)
#define pii pair<int,int>
#define fi first
#define se second
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define pb push_back
#define mp make_pair
#define all(v) v.begin(),v.end()
#define size(v) (int)(v.size())
#define cin(x) scanf("%lld" , &x);
const int N = 1e5+9;
const int maxn = 1e3+9;
const double esp = 1e-6;
int head[maxn] ,tol;
int vis[maxn] , dis[maxn];
struct node{
    int v , w ,next;
}g[N<<1];
int n , m , s , k , c;
void add(int u , int v , int w){
    g[++tol] = {v , w , head[u]};
    head[u] = tol;
}

void spfa(int u){
    queue<int>q;
    rep(i , 1 , n+1){
        dis[i] = INF; vis[i] = 0 ;
    }
    dis[u] = 0 ; vis[u] = 1 ;
    q.push(u);
    while(!q.empty()){
        int a = q.front() ; q.pop();
        vis[a] = 0 ;
        for(int i = head[a] ; i ; i = g[i].next){
            int v = g[i].v;
            int w = g[i].w;
            if(dis[v] > dis[a] + w){
                dis[v] = dis[a] + w ;
                if(!vis[v]){
                    vis[v] = 1 ;
                    q.push(v);
                }
            }
        }
    }
}
void init(){
    ME(head , 0);
    tol = 0 ;
}
void solve(){
    init();
    scanf("%lld%lld%lld%lld%lld" , &n , &m , &s , &k , &c);
    rep(i , 1 , k){
        int x ;
        scanf("%lld" , &x);
        add(n+1 , x , 0);//超级源点一定要建单向边,使得消防队成为一个整体,否则会出错。
    }
    rep(i , 1 , m){
        int u , v , w ;
        scanf("%lld%lld%lld" , &u , &v , &w);
        add(u , v , w);
        add(v , u , w);
    }
    spfa(s);
    int ans1 = -INF , ans2 = -INF;
    rep(i , 1 , n){
        ans1 = max(ans1 , dis[i]);
    }
    spfa(n+1);
    rep(i , 1 , n){
        ans2 = max(ans2 , dis[i]);
    }
    if(ans1 <= ans2*c){
        cout << ans1 << endl;
    }else{
        cout << ans2 << endl;
    }
}

signed main()
{
    int t ;
    scanf("%lld" , &t);
    while(t--)
        solve();
}

解法3:朴素dijistra,建图使消防队之间联通且边权为0

//#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <stdio.h>
#include <queue>
#include <stack>;
#include <map>
#include <set>
#include <string.h>
#include <vector>
#define ME(x , y) memset(x , y , sizeof(x))
#define SF(n) scanf("%d" , &n)
#define rep(i , n) for(int i = 0 ; i < n ; i ++)
#define INF  0x3f3f3f3f
#define mod 998244353
#define PI acos(-1)
using namespace std;
typedef long long ll ;

int ma[1020][1020];
int v , e , s , k , c ;
int dis[1020];
int vis[1020];
int a[1020];

void Dijia(ll r)
{
    for(int i = 1 ; i <= v ; i++)
    {
        vis[i] = 0 ;
        dis[i] = ma[r][i];
    }
    vis[r] = 1 ;
    for(int i = 1 ; i < v ; i++)
    {
        int min1 = INF;
        int pos ;
        for(int j = 1 ; j <= v ; j++)
        {
            if(!vis[j] && min1 > dis[j])
            {
                min1 = dis[j];
                pos = j ;
            }
        }
        vis[pos] = 1 ;
        for(int j = 1 ; j <= v ; j++)
        {
            dis[j] = min(dis[j] , dis[pos] + ma[pos][j]);
        }
    }
}

int main()
{
    int t ;
    scanf("%d" , &t);
    while(t--)
    {
        int u , vv ,  w ;
        int h = - INF , cc = -INF;

        scanf("%d%d%d%d%d" , &v , &e , &s ,&k , &c);

        for(int i = 1 ; i <= v ; i++)
        {
            for(int j = 1 ; j <= v ; j++)
            {
                if(i == j) ma[i][j] = 0 ;
                else ma[i][j] = INF ;
            }
        }


        for(int i = 0 ; i < k ; i++)
        {
            scanf("%d" , &a[i]);
        }



        for(int i = 0 ; i < e ; i++)
        {
            scanf("%d%d%d" , &u , &vv , &w);
            ma[u][vv] = ma[vv][u] = min(ma[u][vv] , w);
        }

        Dijia(s);

        for(int i = 1 ; i <= v ; i++)
        {
            
            h = max(h , dis[i]);
        }
        
        for(int i = 0 ; i < k ; i++)
        {
            for(int j = i + 1 ; j < k ; j++)
            {
                ma[a[i]][a[j]] = ma[a[j]][a[i]] = 0 ;
            }
        }
        Dijia(a[0]);
        for(int i = 1 ; i <= v ; i++)
        {
            
            cc = max(cc , dis[i]);
        }
        

        if(h <= cc * c)
        {
            printf("%d
" , h);
        }
        else
            printf("%d
" , cc);
    }


    return 0 ;
}

解法4:第二种方法是增加一个点,将与每一个救援对之间的距离赋值为0。该点位源点;

//#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <stdio.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <string.h>
#include <vector>
#define ME(x , y) memset(x , y , sizeof(x))
#define SF(n) scanf("%d" , &n)
#define rep(i , n) for(int i = 0 ; i < n ; i ++)
#define INF  0x3f3f3f3f
#define mod 998244353
#define PI acos(-1)
using namespace std;
typedef long long ll ;
ll v , e , s , k , c ;
ll ma[1020][1020];
ll vis[1020];
ll dis[1020];


void Dijia(ll r)
{
    for(int i = 1 ; i <= v ; i++)
    {
        vis[i] = 0 ;
        dis[i] = ma[r][i] ;
    }
    vis[r] = 1 ;

    for(int i = 1 ; i < v ; i++)
    {

        ll min1 = INF ;
        ll pos ;

        for(int j = 1 ; j <= v ; j++)
        {
            if(!vis[j] && min1 > dis[j])
            {
                min1 = dis[j];
                pos = j ;
            }
        }
        vis[pos] = 1 ;
        for(int j = 1 ; j <= v ; j++)
        {
            dis[j] = min(dis[j] , dis[pos] + ma[pos][j]);
        }
    }
}


int main()
{
    int t ;
    scanf("%d" , &t);
    while(t--)
    {
        ll h = -INF , cc = -INF ;
        scanf("%lld%lld%lld%lld%lld" , &v , &e , &s , &k , &c);
        ll tea ;
        for(int i = 1 ; i <= v+1 ; i++)
        {
            for(int j = 1 ; j <= v+1 ; j++)
            {
                if(i == j) ma[i][j] = 0 ;
                else ma[i][j] = INF;
            }
        }

        for(int i = 0 ; i < k ; i++)
        {
            scanf("%lld" , &tea);
            ma[v+1][tea] = ma[tea][v+1] = 0 ;
        }

        for(int i = 0 ; i < e ; i++)
        {
            ll f , to , w ;
            scanf("%lld%lld%lld" , &f , &to , &w);
            ma[f][to] = ma[to][f] = min(ma[f][to] , w);
        }

        Dijia(s);

        for(int i = 1 ; i <= v ; i++)
        {
            h = max(dis[i] , h);
        }

        Dijia(v+1);
        for(int i = 1 ; i <= v ; i++)
        {
            cc = max(dis[i] , cc);
        }
        if(h <= cc * c)
        {
            printf("%lld
" , h);
        }
        else
        {
            printf("%lld
" , cc);
        }
    }


    return 0 ;
}
原文地址:https://www.cnblogs.com/nonames/p/11489149.html