CSP 201712-4 行车路线 迪杰斯特正解

引入

博博在做数据结构课设的时候,一开始用一个正常的dij去做这个题,也在CCF官网测试Accept,但在后来的思考中发现这个题并没有这么简单
PS:如果您是用的dij求解此题,可以试一组样例

4 5
1 1 2 1
1 2 3 1
1 3 4 3
0 1 2 5
0 1 3 100

正确答案:21

.

所谓正解

目前在网络上对于此题的题解大体分为两类

  • spaf
  • dij

①spfa

对于网络上的第一种spaf解法并没有什么问题,复杂度O(n*m),但常数也不小,能成功通过此题.

②dij

但对于第二种dij的题解,目前我还没有发现一篇正解(也可能是我没发现,轻喷) 包括在CCF上AC的代码,也一样有问题(CCF数据水得不行)。大多数代码是利用局部最优解去求解全局最优解,这将使上面说到的那个输入的结果为25。

.

dij写法的修改

我们前面提到,目前网络上大多数dij写法都是利用局部最优解去求解全局最优解,不合理在于对于一个点v的最短路径不一定就是其他点的最短路径延申而来,二dij算法的思想就是这样,导致出现错误。所以我们如果一定要用dij写出正解,就要使其符合dij的基本思想。所以我们要对边进行一些处理才行 因为我们到达v点的最短路的最后一条路是大路和小路中的一条,但小路的选择会影响v点出的边的权值,所以我们为了使每一条边独立,我们就把所有的小路处理为一条条独立的路。 例如edge(1,2)=1,edge(2,3)=1,那么我们就再生成一个边edge(1,3)=4,这样我们以后就不用再考虑相邻小路的权值处理问题,这里我们可以使用Floyd来完成 ,因为n最大只有500,所以把所有小路合并后最多也只会生成25000条新路,这也是不会对我们的复杂度产生太大影响, 剩下的我们只需要对旧路+新路一起求最短路,但要注意不能让两条新路相邻,因为两条新路相邻的cost应为另一条长的新路的cost,所以我们需要用一个二维的len去做一次dij即可。

100代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f3f3f3f3f;
template <typename T> class Graph
{
    public:
        struct edge
        {
            int to;
            T data;
            edge *next;
        };
        Graph(int n);
        ~Graph();
        bool addedge(int s,int t,T data);
        ll solve();
    private:
        edge **firstedge;
        int size;
};

template <typename T>
Graph<T>::Graph(int n)
{
    firstedge=new edge*[n+1];
    for(int i=1;i<=n;i++)
        firstedge[i]=NULL;
    size=n;
}

template <typename T>
Graph<T>::~Graph()
{
    for(int i=1;i<=size;i++)
    {
        edge *p=firstedge[i];
        while(p)
        {
            edge *tmp=p;
            p=p->next;
            delete tmp;
        }
    }
    delete[] firstedge;
}

template <typename T>
bool Graph<T>::addedge(int s,int t,T data)
{
    if(s<1||s>size||t<1||t>size)
        return false;
    edge *p=new edge;
    p->to=t;
    p->data=data;
    p->next=firstedge[s];
    firstedge[s]=p;
    return true;
}

template <typename T>
ll Graph<T>::solve()
{
    ll *len[2];
    len[0]=new ll[size+1];
    len[1]=new ll[size+1];
    for(int i=1;i<=size;i++)
        len[0][i]=len[1][i]=inf;
    len[0][1]=0;
    priority_queue<pair<pair<ll,int>,int>,vector<pair<pair<ll,int>,int> >,greater<pair<pair<ll,int>,int> > > que;
    que.push(make_pair(make_pair(0,0),1));
    while(!que.empty())
    {
        int c=que.top().first.first;
        int lastop=que.top().first.second;
        int v=que.top().second;
        que.pop();
        if(len[lastop][v]!=c)
            continue;
        edge *p=firstedge[v];
        while(p)
        {
            if(p->data.second==0)
            {
                if(len[0][p->to]>c+p->data.first)
                {
                    len[0][p->to]=c+p->data.first;
                    que.push(make_pair(make_pair(len[0][p->to],0),p->to));
                }
            }
            else if(lastop==0)
            {
                if(len[1][p->to]>c+p->data.first)
                {
                    len[1][p->to]=c+p->data.first;
                    que.push(make_pair(make_pair(len[1][p->to],1),p->to));
                }
            }
            p=p->next;
        }
    }
    return min(len[0][size],len[1][size]);
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    Graph<pair<ll,int> >g(n);
    int op,s,t;
    ll cost;
    ll len[n+1][n+1];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            len[i][j]=inf;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%lld",&op,&s,&t,&cost);
        if(op==0)
        {
            g.addedge(s,t,make_pair(cost,op));
            g.addedge(t,s,make_pair(cost,op));
        }
        else
        {
            len[s][t]=min(len[s][t],cost);
            len[t][s]=min(len[t][s],cost);
        }
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=i;j<=n;j++)
                len[i][j]=min(len[i][j],len[i][k]+len[k][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i!=j&&len[i][j]!=inf)
                g.addedge(i,j,make_pair(len[i][j]*len[i][j],1));
    printf("%lld\n",g.solve());
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值