最短路的变形(hdu 4725)

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=4725

题意:有N个点和N层,一层有X个点(0<=X<=N),两邻层间有一条路花费C,即该层到邻层上的任意点距离为C,同层间的点距离不是0,除非存在后面讲的小路贯通。还有M条小路在两个点之间。问从第一个点走到第N个点最短路是多少。输入说明:先输入测试数据的数量,在输入N M C,接下来一行输入N个点所在的层数,再接下来输入M行,表示M行小路(无方向)。

题解:将i层拆成两个点,一个点(N+2×i-1)对于本层的点来说只进不出,并且同层点到该点距离为0,但对于邻层来说只出不进,另一个点(N+2×i)对于本层的点来说只出不进,并且到本层任意点距离为0,但对邻层来说只进不出,这样保证了同层无法通过这两个点到达本层的点,而邻层点可以通过该两点到达邻层任意点,并且花费只有C,对于输入的N个点所在层数,每次建立i-->(N+2×i-1)的距离为0的一条边和(N+2×i)---->i的距离为0的一条边,

建完之后建立两邻层的关系,即建立(N+2×i-1)---->(N+2×(i+1))距离为C和(N+2×(i+1)-1)---->(N+2×(i))距离为C;这样做保证了每次循环时i层的点都可以到达N+2×i-1,并可以通过此点到达邻层的N+2×(i+1)或N+2×(i-1),再通过这些点的0中转到达该层的任意点,注意时间复杂度和内存,最少保证有30w的点可以存下

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#define MAX 300050
#define M 0x3f3f3f3f
using namespace std;
 struct Edge
{
    int e;
    int w;
    Edge(int _e=0,int _w=0):e(_e),w(_w){}
};
vector<Edge> head[MAX];
bool vis[MAX];
int dist[MAX];
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;
    }
};
//o(eloge)
void Dijkstra(int n,int start)//点的编号从1开始
{
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=n;i++)dist[i]=M;
    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<head[u].size();i++)
        {
            int v=head[u][i].e;
            int cost=head[u][i].w;
            if(!vis[v]&&dist[v]>dist[u]+cost)
            {
                dist[v]=dist[u]+cost;
                que.push(qnode(v,dist[v]));
            }
        }
    }
}
//o(e) 超时
void spfa(int start,int n)
{
    queue<int> q;
    q.push(start);
    for(int i=1;i<=n;i++){dist[i]=M;vis[i]=false;}
    dist[start]=0;
    while(!q.empty())
    {
        int h=q.front();q.pop();
        for(int i=0;i<head[h].size();i++)
        {
            Edge& p=head[h][i];
            if(p.w+dist[h]<dist[p.e])
            {
                dist[p.e]=p.w+dist[h];
                if(!vis[p.e]){q.push(p.e);vis[p.e]=true;}
            }
        }
        vis[h]=false;
    }
}
void addEdge(int s,int ed,int w)
{
    head[s].push_back(Edge(ed,w));
}
int main()
{
    int op=1;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m,c;
        scanf("%d %d %d",&n,&m,&c);
        int now;
        for(int i=1;i<=n;i++)  //输入第i点所在的层数,
        {
            scanf("%d",&now);
            addEdge(i,n+now*2-1,0);
            addEdge(n+now*2,i,0);
        }
        for(int i=1;i<n;i++)
        {
            addEdge(n+i*2-1,n+2*(i+1),c);
            addEdge(n+2*(i+1)-1,n+2*i,c);
        }
        for(int i=1;i<=m;i++)
        {
            int s,ed,w;
            scanf("%d %d %d",&s,&ed,&w);
            addEdge(s,ed,w);
            addEdge(ed,s,w);
        }
        //spfa(1,n*3);
         Dijkstra(n*3,1);
        if(dist[n]>=M)printf("Case #%d: -1\n",op++);
        else printf("Case #%d: %d\n",op++,dist[n]);
        for(int i=1;i<=3*n;i++) {head[i].clear();}
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值