首先呢,前几天学了A*算法来弄k小路。。
但是我很快就发现A*实在是太慢太慢太慢了。。
完全是可以被卡到 O(nk) 的
又由于题目的需要,这样的复杂度已经无法满足我们的需求
于是就去学了另外的方法,但由于网上的资料实在是少之又少。。
于是我就去找了一下大牛
感谢Claris大佬!百忙之间抽空给了我论文与他的模板!!非常感谢!!
论文名字是《俞鼎力-堆的可持久化与k短路》,想学的朋友可以去看看
这样的话时间复杂度就变成 O(nlogn+mlogm+klogk) 了,比之前不知道高到哪里去了。。
说实话,学了两天,其实还是有一点点迷。。
所以详细的解释就不写了吧。。
随便讲几句吧:
1.我们的可持久化堆维护的是这个点到终点所有非树边的“贡献”大小
2.我觉得啊,下文中
s[u].z-(d[s[u].y]-d[s[u].x])
这句话为什么这么写呢,那是因为,我们可以吧d[s[u].y]-d[s[u].x]等价于之前有一条x到y的边,这个你可以感性地认识,然后呢,你选了这条边,其实就相当于代价增加了这么多
3.我有一个坑。。晚点填
怕误导大家
在这里贴一个我的(Claris的)板子,至少我觉得有了板子学习会简单很多
#include
#include
#include
#include
#include
using namespace std;
typedef pair<int,int>P;
const int N=1005;
const int M=100010;
const int MAX=1<<30;
int n,m;
struct qq{int x,y,z,last;}s[M];int last[N];
void init (int x,int y,int z)
{
s[++m].x=x;s[m].y=y;s[m].z=z;
s[m].last=last[x];
last[x]=m;
}
int S,T,K;
int d[N],f[N];//从这里到终点的最短距离,其实就是在反向图中终点到这个点的距离 这个点是从哪条边过来的
void get_dis ()
{
int x;
priority_queuevector
,greater
>q;
for (int u=1;u<=n;u++) d[u]=MAX,f[u]=0;
q.push(P(d[T]=0,T));
while (!q.empty())
{
P t=q.top();q.pop();
if (t.first>d[x=t.second]) continue;
for (int u=last[x];u!=-1;u=s[u].last)
{
int y=s[u].y;
if (d[y]>d[x]+s[u].z)
{
f[y]=u;
q.push(P(d[y]=d[x]+s[u].z,y));
}
}
}
}
bool ok[M];//这条边是不是一条树边
int root[N],tot;//这个堆的根 给到的编号
struct Node
{
int l,r,d;//左偏树的东西
P v;//比较的权值
Node() {}
Node (int _l,int _r,int _d,P _v)
{
l=_l;r=_r;d=_d;v=_v;
}
}lalal[2000010];//堆
int bt (P b)
{
lalal[++tot]=Node(0,0,0,b);
return tot;
}
int Merge (int a,int b)
{
if (a==0||b==0) return a+b;
if (lalal[a].v>lalal[b].v) swap(a,b);//我要维护一个小跟堆
int x=++tot;//新开一个点,因为要多次合并
lalal[x]=lalal[a];
lalal[x].r=Merge(lalal[a].r,b);
if (lalal[lalal[x].l].d1;
return x;
}
bool vis[N];
void dfs (int x)//合并
{
if (f[x]==0||vis[x]==true) return ;
vis[x]=true;
dfs(s[f[x]].x);
root[x]=Merge(root[x],root[s[f[x]].x]);
}
int solve ()
{
int x,y,z;
int mm=m;m=0;
while (mm--) scanf("%d%d%d",&x,&y,&z),init(y,x,z); //这里建的是反向图
scanf("%d%d%d",&S,&T,&K);
if (S==T) K++;//这题坑的地方。。
get_dis();
if (d[S]==MAX) return -1;
if (K==1) return d[S];
K--;
for (int u=1;u<=m;u++) ok[u]=false;
for (int u=1;u<=n;u++) vis[u]=false,ok[f[u]]=true,root[u]=0;lalal[0].d=-1;
tot=0;for (int u=1;u<=m;u++) if (ok[u]==false&&d[s[u].x]//注意这里的x和y,这里又要反过来啦,因为之前都是反向图
for (int u=1;u<=n;u++) dfs(u);
priority_queue vector
,greater
> q;
int ans;
y=root[S];
if (y!=0) q.push(P(d[S]+lalal[y].v.first,y));
while (!q.empty()&&K)
{
K--;
P t=q.top();q.pop();
ans=t.first;
x=t.second,y=lalal[x].l;
if (y!=0) q.push(P(ans-lalal[x].v.first+lalal[y].v.first,y));
y=lalal[x].r;
if (y!=0) q.push(P(ans-lalal[x].v.first+lalal[y].v.first,y));
y=root[lalal[x].v.second];
if (y!=0) q.push(P(ans+lalal[y].v.first,y));
}
return K>0?-1:ans;
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
memset(last,-1,sizeof(last));
printf("%d\n",solve());
}
return 0;
}