最短路问题

加减最短路

即为路径中的边权权值相加,用 dijstra 或 SPFA 等处理即可,代码略

乘积最短路

即为路径中的边权权值相乘求得最短路(边权均为正数)

此问题中我们发现对于乘法来说,我们一般使用的加法最短路的条件就不成立了,因为在该问题中如果直接相乘就必须涉及取模运算,否则会爆精度,但是这样一来我们就无法知道它的那一条路径的真实长度(取模之前的路径长)最短

在这个问题中,我们可以采用对数运算,好处就在这种运算可以把乘法转化为加法:\(log(NM)=log(N)+log(M)\) ,并且进行对数运算以后所得到的值很小,就可以直接使用基本的最短路求解方法解决,最后将其答案用快速幂还原即可

code

#include<iostream>
#include<cstdio>
#include<math.h>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#define ll long long

const ll mod=9987;
const ll maxn=1e5+10;
ll n,m,s;
ll vis[maxn],pre[maxn],k[maxn];
double dis[maxn];
std::priority_queue<std::pair<ll,ll> > q;
std::vector<std::pair<double,ll> > e[maxn];

inline void dij(ll x)
{
	for(int i=1;i<=n;i++) dis[i]=0x7fffffff;
	
	dis[x]=0;
	
	q.push(std::make_pair(-dis[x],x));
	
	while(q.size())
	{
		ll u=q.top().second;
		q.pop();
		
		if(vis[u]) continue;
		vis[u]=1;
		
		for(int i=0;i<e[u].size();i++)
		{
			ll v=e[u][i].first;
			double w=log(e[u][i].second);
			
			if(dis[v]>dis[u]+w)
			{
				dis[v]=dis[u]+w;
				pre[v]=u;
				k[v]=e[u][i].second;
				
				q.push(std::make_pair(-dis[v],v));
			}
		}
	}
}

int main(void)
{
	scanf("%lld %lld",&n,&m);
	
	for(int i=1;i<=m;i++)
	{
		ll x,y,z;
		scanf("%lld %lld %lld",&x,&y,&z);
		
		e[x].push_back(std::make_pair(y,z));
	}
	
	dij(1);
	
	ll sum=1;
	
	while(pre[n])
	{
		(sum=sum*k[n])%=mod;
		n=pre[n];
	}
	
	printf("%lld\n",sum);
	
	return 0;
}

同余最短路

即用同余式进行构造求解,发现问题中的解决状态可以转化为最短路问题中的点边关系进行解决

首先,我们考虑设 \(f(i)\) 表示通过操作三和操作二能够到达的楼层在模 \(x\) 意义下能到达的最小楼层

那么我们有:

\[f(i+y)=f(i)+y \]

\[f(i+z)=f(i)+z \]

显然,上式即为我们求最短路时所使用的式子,于是我们可以使用最短路操作进行优化,则将 \(f(i)\) 设为一个图的点,令 \(y\)\(z\) 代表一条边权即可

于是,我们处理出来最小能够到达的楼层,只需在 \(x\) 范围内记录答案即可,为: \(ans+=(h-f[i])/x+1\)

意思是我们在 \(x\) 的范围内能够到达的楼层 \(f\),通过第一种操作,之后 \(f+i \times x\) 的楼层均能取到,最后计数时加上自己即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#define ll long long

const ll maxn=1e5+10;
ll h,x,y,z,tot,ans;
ll head[maxn*2],vis[maxn],f[maxn];
struct node
{
	ll u,v,w,nxt;
} s[maxn*2];

inline void add(ll u,ll v,ll w)
{
	s[++tot].v=v;
	s[tot].w=w;
	s[tot].nxt=head[u];
	head[u]=tot;
}

inline void spfa(ll x)
{
	std::queue<ll> q;
	memset(f,0x3f,sizeof(f));
	memset(vis,0,sizeof(vis));
	vis[x]=1;
	f[x]=1;
	q.push(x);
	
	while(q.size())
	{
		ll u=q.front();
		q.pop();
		
		vis[u]=0;
		
		for(int i=head[u];i;i=s[i].nxt)
		{
			ll v=s[i].v;
			ll w=s[i].w;
			
			if(f[v]>f[u]+w)
			{
				f[v]=f[u]+w;
				
				if(!vis[v])
				{
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
}

int main(void)
{
	scanf("%lld %lld %lld %lld",&h,&x,&y,&z);
	
	if(x==1||y==1||z==1)
	{
		printf("%lld\n",h);
		return 0;
	}
	
	for(int i=0;i<x;i++)
	{
		add(i,(y+i)%x,y);
		add(i,(z+i)%x,z);
	}
	
	spfa(1);
	
	for(int i=0;i<x;i++)
	{
		if(f[i]<=h)
		{
			ans+=(h-f[i])/x+1;
		}
	}
	
	printf("%lld\n",ans);
	
	return 0;
}
posted @ 2021-01-08 17:00  雾隐  阅读(91)  评论(0编辑  收藏  举报