随笔 - 46  文章 - 0  评论 - 0  阅读 - 503 

k短路

1. a* 算法

用优先队列维护目前路径的最小值进行bfs
一个路径的权值定义为已走长度+剩余最短路长度
即带估价函数的bfs
O(nklogk)

2.可持久化可并堆

我们先建出最短路树(即每个节点的父节点为其一条最短路上的后继)
disu为u到终点t的距离
对于一条边,定义其权值为ew
ew=disev+elendiseu
即走他而非最短路增加的代价

发现对于路径P,lenP=diss+pw,p为所有非树边

考虑有一条路径向外扩展,对一条路径,有两种操作可以使其不重不漏地扩展为新路径

1. 替换非树边集中的最后一条边为一条权更大的边

2. 在后边拼上一条边,此边为最后一条边终点在最短路树上祖先所直接连接的非树边

发现任意点备选集合可以由其父亲并上有自己出发的非树边

可持久化可并堆维护

求值时,每次取出最短的,扩展新边
重复k次即可

例: P2483 [SDOI2010] 魔法猪学院

#include<bits/stdc++.h>
using namespace std;

const int N = 5010,M = 200010;

struct que
{
	int u;
	double w;
	
	bool operator < (const que &rhs) const
	{
		return w>rhs.w;
	} 
}seq[N]; 

struct heap
{
	int ls,rs,ed,d;
	double w;
}hp[20*M];
int tot,n,m,vis[N],h1[N],h2[N];
int fa[N],rt[N],cnte1,cnte2;
double E,dis[N];

int clone(int u)
{
	hp[++tot].ls=hp[u].ls;
	hp[tot].rs=hp[u].rs;
	hp[tot].ed=hp[u].ed;
	hp[tot].d=hp[u].d;
	hp[tot].w=hp[u].w;
	return tot;
}

int init(int ed,double w)
{
	hp[++tot]={0,0,ed,0,w};
	return tot;
}

int merge(int x,int y)
{
	if(!x||!y) return x+y;
	if(hp[x].w>hp[y].w) swap(x,y);
	int p=clone(x);
	hp[p].rs=merge(hp[x].rs,y);
	if(hp[hp[p].ls].d<hp[hp[p].rs].d)
		swap(hp[p].ls,hp[p].rs);
	hp[p].d=hp[hp[p].rs].d;
	return p;
}

int cnt;

struct edge
{
	int u,v,nxt;
	double w;
}e1[M],e2[M];
priority_queue<que>q;

void dijstra()
{
	for(int i=1;i<=n;i++) dis[i]=10000000000000;
	dis[n]=0;
	q.push({n,0});
	while(!q.empty())
	{
		cnt++;
		que x=q.top();
		q.pop();
		
		if(vis[x.u]) continue;
		vis[x.u]=1;
		
		for(int i=h2[x.u];i;i=e2[i].nxt)
		{
			int v=e2[i].v;
			double w=e2[i].w;
			if(dis[v]>dis[x.u]+w)
			{
				dis[v]=dis[x.u]+w;
				q.push({v,dis[v]});
			}
		}
	}
	return ;
}

int ontr[M],tf[N];

void dfs(int u)
{
	cnt++;
	tf[u]=1;
	for(int i=h2[u];i;i=e2[i].nxt)
	{
		int v=e2[i].v;
		if(!tf[v]&&dis[v]==dis[u]+e2[i].w)
		{
			fa[v]=u;
			ontr[i]=1;
			dfs(v);
		}
	}
	return ;
}

void dfs2(int u)
{
	cnt++;
	tf[u]=1;
	if(fa[u]) rt[u]=merge(rt[u],rt[fa[u]]);
	for(int i=h2[u];i;i=e2[i].nxt)
		if(fa[e2[i].v]==u&&!tf[e2[i].v])
			dfs2(e2[i].v);
	return ;
}

void add1(int u,int v,double w)
{
	e1[++cnte1].u=u;
	e1[cnte1].v=v;
	e1[cnte1].w=w;
	e1[cnte1].nxt=h1[u];
	h1[u]=cnte1;
}

int rev[M];

void add2(int u,int v,double w)
{
	e2[++cnte2].u=u;
	e2[cnte2].v=v;
	e2[cnte2].w=w;
	e2[cnte2].nxt=h2[u];
	h2[u]=cnte2;
}

void input()
{
	scanf("%d%d%lf",&n,&m,&E);
	for(int i=1;i<=m;i++)
	{
		int u,v;double w;
		scanf("%d%d%lf",&u,&v,&w);
		if(u==n) continue;
		add1(u,v,w);
		add2(v,u,w);
		rev[cnte1]=cnte2;
	}
	return ;
}

int main()
{
//	freopen("P2483_1.in","r",stdin);
	input();
	dijstra();
	dfs(n);
	for(int i=1;i<=n;i++)
		if(vis[i])
			for(int j=h1[i];j;j=e1[j].nxt)
				if(!ontr[rev[j]])
					rt[i]=merge(rt[i],init(e1[j].v,dis[e1[j].v]+e1[j].w-dis[i]));
	
	memset(tf,0,sizeof tf);
	dfs2(n);
	
//	for(int i=1;i<=n;i++) cout<<dis[i]<<endl;
	
	int ans=0;
	q.push({rt[1],hp[rt[1]].w});
	E-=dis[1],ans++;
	while(E>0)
	{
		cnt++;
		if(q.empty()) break;
		que x=q.top();
		q.pop();
		if(E-dis[1]-x.w<0) break;
		else E-=dis[1]+x.w,ans++;
		
		if(hp[x.u].ls) q.push({hp[x.u].ls,x.w-hp[x.u].w+hp[hp[x.u].ls].w});
		if(hp[x.u].rs) q.push({hp[x.u].rs,x.w-hp[x.u].w+hp[hp[x.u].rs].w});
		if(rt[hp[x.u].ed]) q.push({rt[hp[x.u].ed],x.w+hp[rt[hp[x.u].ed]].w});
	}
	cout<<cnt<<endl;
	cout<<ans;
//	cout<<1.00*tot/M<<" "<<cnte1<<" "<<cnte2<<endl;
	return 0;
}
posted on   Grylls_117  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示