zoukankan      html  css  js  c++  java
  • 图论3-分层图最短路

    这是我写的第三篇图论题解,这次要介绍的是分层图最短路。要求:熟练的打出最短路spfa/dijkstra并理解其含义。

    还是照例用一道题来引入:6B02 升降梯上

    题意简述:有一个电梯原先在1层,有一个控制杆可以控制电梯升降,一共有m个档(满足C1<=Ci<=Ci+1<=Cm),

    将控制杆挂到相邻的档需要1秒,控制杆原本在Ci=0的那个档,在每个档都可以按一个按钮使电梯上升Ci层(当然也可

    以不按并继续挂到下一档),(如果Ci<0就是下降|Ci|层),电梯到相邻的层需要2秒,求从1层到n层最少要多少秒。

    思路解析:这道题有两个量,分别是挂的档以及电梯,我们要同时维护这两个量的最小,所以普通的最短路算法就没法

    解决这个问题。那我们要怎么做呢?如果在dp里出现要维护两个最小时我们该怎么办?当然是把这两个量都记录到转态

    里啦。也就是dp就会变成二维的,所以举一反三,在图论里我们也可以这么做。我们将这个图分成m“层”,然后再在

    这m层图中跑最短路。但是由于我不想在队列里维护结构体,所以我用一个(x-1)*n+y代表第x层的y号节点,其中x层y号

    节点也就是(x-1)*n+y,表示电梯停到了y层并且控制杆停到了x档的最小时间。所以最后答案就是min(dis[i*n]){1<=i<=m} 

    建边是这样的:

    1.如果这个点对应的电梯层数+Ci>0且<=n就建一条从这个节点到这个节点+Ci,权值为abs(Ci)*2的单向边

    2.对于每个控制杆档数不为m的点都向档数加一(节点编号加n)建一条权值为1的单向边,反之同理。

    建边代码:

    for(int i=1;i<=m;i++)//枚举层数 
    { for(int j=1;j<=n;j++)//枚举节点编号 { if(a[i]==0) s=(i-1)*n+1;//找到起始点s if(j+a[i]>0&&j+a[i]<=n) add((i-1)*n+j,(i-1)*n+j+a[i],abs(a[i])*2);//建边条件1 x->x+a[i]权值为abs(a[i])*2 if(i!=1) add((i-1)*n+j,(i-2)*n+j,1); if(i!=m) add((i-1)*n+j,i*n+j,1);//建边条件2,需要正反建边,x->x+n和x->x-n权值为1 } }

    注意:

    1.起始点s不能默认是1

    2.这也是我容易犯的错误,在spfa中,新的节点用y=to[i],之后不管什么操作千万别把i和y写反了啊

    3.如果用一维分层图的话数组要开够

    4.别忘记输出-1

    #include<bits/stdc++.h>
    using namespace std;
    const int NR=1e3+10;
    const int MR=25;
    const int INF=0x3f3f3f3f;
    int n,m,s;
    int a[MR];
    int to[NR*MR<<3],nxt[NR*MR<<3],val[NR*MR<<3];
    int head[NR*MR];//邻接表数组开够 
    int tot=1;
    void add(int x,int y,int z)
    {
        to[tot]=y;
        val[tot]=z;
        nxt[tot]=head[x];
        head[x]=tot++;
    }
    bool vis[NR*MR];
    int dis[NR*MR];//数组开够 
    void spfa(int s)//spfa基本操作 
    {
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        queue<int> q;q.push(s);dis[s]=0;vis[s]=1;
        while(!q.empty())
        {
            int x=q.front();q.pop();vis[x]=0;
            for(int i=head[x];i;i=nxt[i])
            {
                int y=to[i];
                if(dis[y]>dis[x]+val[i])
                {
                    dis[y]=dis[x]+val[i];
                    if(!vis[y])//注意一定是y不是i,这个错我调了很久才调出来 
                    {
                        q.push(y);
                        vis[y]=1;
                    }
                }
            }
        }
    }
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
        while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
        return x*f;
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
    //    freopen("1.out","w",stdout);
        n=read(),m=read();
        for(int i=1;i<=m;i++) a[i]=read();//我这里面a[i]就是题目中的Ci 
        for(int i=1;i<=m;i++)//枚举层数 
        {
            for(int j=1;j<=n;j++)//枚举节点编号 
            {
                if(a[i]==0) s=(i-1)*n+1;//找到起始点s 
                if(j+a[i]>0&&j+a[i]<=n) add((i-1)*n+j,(i-1)*n+j+a[i],abs(a[i])*2);//建边条件1 x->x+a[i]权值为abs(a[i])*2
                if(i!=1) add((i-1)*n+j,(i-2)*n+j,1);
                if(i!=m) add((i-1)*n+j,i*n+j,1);//建边条件2,需要正反建边,x->x+n和x->x-n权值为1 
            }
        }
        spfa(s);//以求出来的起点s做最短路 
        int ans=INF;
        for(int i=1;i<=m;i++)
        {
            ans=min(ans,dis[i*n]);//统计答案要记得取min 
        }
        if(ans==INF) puts("-1");
        else printf("%d
    ",ans);//输出答案 
        return 0;
    }
  • 相关阅读:
    《DSP using MATLAB》Problem 6.17
    一些老物件
    《DSP using MATLAB》Problem 6.16
    《DSP using MATLAB》Problem 6.15
    《DSP using MATLAB》Problem 6.14
    《DSP using MATLAB》Problem 6.13
    《DSP using MATLAB》Problem 6.12
    《DSP using MATLAB》Problem 6.11
    P1414 又是毕业季II
    Trie树
  • 原文地址:https://www.cnblogs.com/chen-1/p/12567099.html
Copyright © 2011-2022 走看看