最短路(hdu4725)(建点巧妙)

传送门

题目大意是:

有一些点,分为几层,然后层与层之间可花费代价C到达,还有一些特殊边,使得某个点和某个点之间花费某代价可以到达,求从1到n的最小代价。


一开始的想法是,既然层与层之间可以到达,那么对于第i层和第i+1层,把第i层的所有点与第i+1层的所有点连边,权值为C,特殊的边该怎么连怎么连,然后跑最短路。

然后我看了一下数据范围——N, M (0 <= N, M <= 105)  笑不出来

好吧,那让我们考虑一下如何优化。

我们知道,同一层内的点是可以相互到达的且无花费。也就是说,如果我想去到第i层,我到达第i层的某个点后就相当于我到了这一层中所有的点

那么对于每一层,我可以建一个入点和一个出点(或者说就建一个点也行,反正连进和连出的边的性质是一样的)。层与层之间就有建的点相连(代价就为C),层内的点全部连向建的点(代价为0)。

连变数:2*n+m   时间O(e loge)能过

注意:1.建的虚拟点的编号一定不要和原来的点重复了。2.图中的点不再是n个,而应该建成3n个

#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#define N 100003
#define M 100003
using namespace std;
int read()
{
    int f=1,x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
const int INF=0x3f3f3f3f;
struct qnode
{
    int v;
    int c;
    qnode(int _v=0,int _c=0):v(_v),c(_c){}
    bool operator <(const qnode &r)const
    {
        return c>r.c;
    }
};
struct Edge
{
    int v,cost;
    Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge> E[N*2+M];
bool vis[N*3];
int dist[N*3];
 
void dij(int n,int start)//点的编号从1开始
{
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=n;i++) dist[i]=INF;
    priority_queue<qnode> que;
    while(!que.empty()) que.pop();
    dist[start]=0;
    que.push(qnode(start,0));
    qnode tmp;
    while(!que.empty())
    {
        tmp=que.top();
        que.pop();
        int u=tmp.v;
        if(vis[u]) continue;
        vis[u]=true;
        for(int i=0;i<E[u].size();i++)
        {
            int v=E[tmp.v][i].v;
            int cost=E[u][i].cost;
            if(!vis[v]&&dist[v]>dist[u]+cost)
            {
                dist[v]=dist[u]+cost;
                que.push(qnode(v,dist[v]));
            }
        }
    }
}
 
void add(int u,int v,int w)
{
    E[u].push_back(Edge(v,w));
}
int main()
{
    int T=read();
    for(int k=1;k<=T;++k)
    {
        for(int i=0;i<=N*2+M;i++)
          if(!E[i].empty())
            E[i].clear();
        
        int n=read(),m=read(),c=read();
        for(int i=1;i<=n;++i)
        {
            int x=read();
            add(x*2+n-1,i,0);
            add(i,x*2+n,0);
        }
        for(int i=1;i<=m;++i)
        {
            int u=read(),v=read(),w=read();
            add(u,v,w);add(v,u,w);
        }
        for(int i=1;i<=n;++i)
        {
            int u=2*i+n;
            if(i>1)add(u,u-3,c);
            if(i<n)add(u,u+1,c);
            
        }
        dij(3*n,1);
        printf("Case #%d: %d
",k,(dist[n]==INF?-1:dist[n]));
    }
} 
/*
*/
最短路(hdu4725)(建点巧妙)
原文地址:https://www.cnblogs.com/yyys-/p/11172799.html