引入
博博在做数据结构课设的时候,一开始用一个正常的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;
}