zoukankan      html  css  js  c++  java
  • 最短路问题

    加减最短路

    即为路径中的边权权值相加,用 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
    ",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 imes 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
    ",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
    ",ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    shell脚本 加密备份MySQL数据库
    C#在Linux下获取文件夹信息(所在磁盘总大小,使用空间,已用空间,使用率)
    bootstrap--- 两种bootstrap multiselect组件大比拼
    C# 文件重命名
    C#中一些常用的正则表达式
    C# 文件压缩加解密
    Python 由__dict__和dir()引发的一些思考
    python3随机生成中文字符
    Django自定义过滤器中is_safe和need_autoescape两个参数的理解
    Python格式化字符串--format
  • 原文地址:https://www.cnblogs.com/jd1412/p/14242325.html
Copyright © 2011-2022 走看看