最短路(建图)

                                                                          虫洞

题目描述

N个虫洞,M条单向跃迁路径。从一个虫洞沿跃迁路径到另一个虫洞需要消耗一定量的燃料和1单位时间。虫洞有白洞和黑洞之分。设一条跃迁路径两端的虫洞质量差为delta。

从白洞跃迁到黑洞,消耗的燃料值减少delta,若该条路径消耗的燃料值变为负数的话,取为0。

从黑洞跃迁到白洞,消耗的燃料值增加delta。

路径两端均为黑洞或白洞,消耗的燃料值不变化。

作为压轴题,自然不会是如此简单的最短路问题,所以每过1单位时间黑洞变为白洞,白洞变为黑洞。在飞行过程中,可以选择在一个虫洞停留1个单位时间,如果当前为白洞,则不消耗燃料,否则消耗s[i] 的燃料。现在请你求出从虫洞1到N最少的燃料消耗,保证一定存在1到N的路线。

输入格式

第1行:2个正整数N, M

第2行:N个整数,第i个为0表示虫洞i开始时为白洞,1 表示黑洞。

第3行:N个整数,第i个数表示虫洞i的质量w[i]。

第4行:N个整数,第i个数表示在虫洞i停留消耗的燃料s[i]。

第5..M+4行:每行3个整数,u,v,k,表示在没有影响的情况下,从虫洞u到虫洞v需要消耗燃料k。

输出格式

一个整数,表示最少的燃料消耗。

样例

样例输入

4 5
1 0 1 0
10 10 100 10
5 20 15 10
1 2 30
2 3 40
1 3 20
1 4 200
3 4 200

样例输出

130

样例解释

按照1->3->4的路线。

数据范围与提示

对于30%的数据: 1<=N<=100,1<=M<=500

对于60%的数据: 1<=N<=1000,1<=M<=5000

对于100%的数据: 1<=N<=5000,1<=M<=30000

其中20%的数据为1<=N<=3000的链

1<=u,v<=N, 1<=k,w[i],s[i]<=200

思路:分层图,将白洞作为一层(结点1-n),黑洞作为一层(节点n+1-2*n)

第一种:u与v颜色相同,因为走一条边花费一个单位时间,所以从u走到v时v以变色,Insert(u(u为白点),v+n(v为黑点),k(边权))(若开始u为白v为白)反之同上

第二种:u与v颜色不同,因为点会按时换颜色所以即使u开始为黑v开始为白一定得建一条u到v(u到v后v变成白点)的边,但u可变成黑,同时v变白,再建一条u+n到v的边,反之,同上

第三种:每一点可在原地停留,所以建u到u+n和u+n到u的边

之后跑最短路,注意起始位置与1的颜色有关,最后注意比较两种情况

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10000+10,maxm=30000+10,inf=0x3f3f3f3f;
int color[maxn],w[maxn],s[maxn];
struct Edge{
    int to,next,w;
}e[maxm<<2];
struct Node{
    int num,w;
    Node(){};
    Node(int x,int y){
        num=x;
        w=y;
    }
    bool operator <(const Node &a)const{
         return w>a.w;
    }
};
int head[maxm<<2],tot=0;
void Insert(int a,int b,int c){
     e[++tot].to=b;
     e[tot].w=c;
     e[tot].next=head[a];
     head[a]=tot;
}
priority_queue<Node> q;
int d[maxn];
void Dij(int x){
     bool vis[maxn];
     memset(vis,0,sizeof(vis));
     memset(d,0x3f,sizeof(d));
     d[x]=0;
     q.push(Node(x,0));
     while(!q.empty()){
         Node t=q.top();
         int u=t.num;
         q.pop();
         if(vis[u]) continue;
         vis[u]=1;
         for(int i=head[u];i;i=e[i].next){
             int v=e[i].to;
             if(d[v]>d[u]+e[i].w){
                 d[v]=d[u]+e[i].w;
                 q.push(Node(v,d[v]));
             }
         }
     }
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&color[i]);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<=n;i++) scanf("%d",&s[i]);
    for(int i=1;i<=m;i++){
        int u,v,k;
        scanf("%d%d%d",&u,&v,&k);
        if(color[u]==color[v]){
             Insert(u,v+n,k);
             Insert(u+n,v,k);//当u与v颜色相同时,都得见两条边
        }     
        else{
              Insert(u,v,max(0,k-abs(w[u]-w[v])));
              Insert(u+n,v+n,k+abs(w[u]-w[v]));//u与v颜色不同
        }
    }
    for(int i=1;i<=n;i++){
        Insert(i,i+n,0);
        Insert(i+n,i,s[i]);   //在此处停留
    }
    if(color[1]==0) Dij(1);
    else Dij(1+n);//注意起始位置
    printf("%d
",min(d[n],d[2*n]));//最后要比较两种可能结果
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/HZOIDJ123/p/13263260.html