zoukankan      html  css  js  c++  java
  • Til the Cows Come Home 最短路Dijkstra+bellman(普通+优化)

    Til the Cows Come Home 最短路Dijkstra+bellman(普通+优化)

    贝西在田里,想在农夫约翰叫醒她早上挤奶之前回到谷仓尽可能多地睡一觉。贝西需要她的美梦,所以她想尽快回来。

    农场主约翰的田里有n(2<=n<=1000)个地标,唯一编号为1..n。地标1是谷仓;贝西整天站在其中的苹果树林是地标n。奶牛在田里行走时使用地标间不同长度的T(1<=t<=2000)双向牛道。贝西对自己的导航能力没有信心,所以一旦开始,她总是沿着一条从开始到结束的路线行进。

    根据地标之间的轨迹,确定贝西返回谷仓必须走的最小距离。这样的路线一定存在。

    解题思路

    这个题是很典型的最短路问题,并且给了起点和终点,所以使用Dijkstra算法来解决单源最短路问题。

    Dijkstra算法我这有两种形式,一种是普通的邻接矩阵法,复杂度是(O(n^2)),n是顶点的个数

    然而使用邻接表和优先队列的形式,可以将复杂度优化到(O(m*logn)),m是边的个数,但是这种一般适用于稀疏图,对于稠密图,这种优化算法可能比原来没有优化的复杂度还要高。参考《算法竞赛入门经典(第二版)》360页

    这个题也可以用Bellman算法来进行实现。

    代码实现

    //普通的临界矩阵算法 dijkstra算法
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int inf=0x3f3f3f3f;
    const int maxn=1e3+7;
    int mp[maxn][maxn];
    int dis[maxn];
    int vis[maxn];
    int t, n;
    
    void dij()
    {
    	for(int i=1; i<=n; i++)
    	{
    		dis[i]=mp[1][i];
    	}
    	vis[1]=1;
    	for(int i=1; i<n; i++)
    	{
    		int tmp=inf, k;
    		for(int j=1; j<=n; j++)
    		{
    			if(!vis[j] && dis[j]<tmp)
    			{
    				tmp=dis[j];
    				k=j;
    			}
    		}
    		vis[k]=1;
    		for(int j=1; j<=n; j++)
    		{
    			if(!vis[j] && dis[j] > dis[k]+mp[k][j])
    				dis[j]=dis[k] + mp[k][j];
    		}
    	}
    }
    
    void init()
    {
    	for(int i=1; i<=n; i++)
    	{
    		for(int j=1; j<=n; j++)
    		{
    			if(i!=j) mp[i][j]=inf;
    			else mp[i][j]=0;	
    		}		
    	}
    	fill(vis+1, vis+n+1, 0);
    	fill(dis+1, dis+n+1, inf);
    }
    int main()
    {
    	while(scanf("%d%d", &t, &n)!=EOF)
    	{
    		int a, b, c;
    		init();
    		for(int i=1; i<=t; i++)
    		{
    			scanf("%d%d%d", &a, &b, &c);
    			if(c < mp[a][b])
    			{
    				mp[a][b]=c;
    				mp[b][a]=c;
    			}
    		}
    		dij();
    		printf("%d
    ", dis[n]);
    	}
    	return 0;
     } 
    

    优化的Dijkstra算法

    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int maxn=1e3+7;
    const int maxe=2e3+7;
    const int inf=0x3f3f3f3f;
    struct edge{
    	int to, cost;
    };
    struct headnode{
    	int d, u;
    	bool friend operator < (const headnode a, const headnode b)
    	{
    		return a.d > b.d; //使用大于号是因为在优先队列中默认是从大到小的,这里需要反过来,从小到大。
    	}
    };
    int dis[maxn];
    int vis[maxn];
    vector<edge> g[maxn];
    priority_queue<headnode> que;
    int t, n;
    void init()
    {
    	for(int i=1; i<=n; i++)
    	{
    		g[i].clear();
    		vis[i]=0;
    		dis[i]=inf;
    	}
    	while(!que.empty()) que.pop();
    }
    void dij(int s)
    {
    	int u;
    	edge e;
    	dis[s]=0;
    	headnode tmp={0, s};
    	headnode next;
    	que.push(tmp);
    	while(!que.empty())
    	{
    		tmp=que.top();
    		que.pop();
    		u=tmp.u;
    		if(vis[u]==1) continue;
    		vis[u]=1;
    		for(int i=0; i<g[u].size(); i++)
    		{
    			e=g[u][i];
    			if(dis[e.to] > dis[u]+e.cost)
    			{
    				dis[e.to]=dis[u]+e.cost;
    				next.d=dis[e.to];
    				next.u=e.to;
    				que.push(next);
    			}
    		}
    	}
    }
    int main()
    {
    	while(scanf("%d%d", &t, &n)!=EOF)
    	{
    		init();
    		int a, b, c;
    		edge e;
    		for(int i=1; i<=t; i++)
    		{
    			scanf("%d%d%d", &a, &b, &c);
    			e.to=b;
    			e.cost=c;
    			g[a].push_back(e);
    			e.to=a;
    			e.cost=c;
    			g[b].push_back(e);
    		}
    		dij(1);
    		printf("%d
    ", dis[n]);
    	}
    	return 0;
    } 
    

    Bellman算法

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int maxn=1e3+7;
    const int maxe=4e3+7; //这里一定要注意,t是路的条数,但是存图时每条路需要存储两遍。
    const int inf=0x3f3f3f3f;
    
    struct edge{
    	int a, b, c;
    }e[maxe];
    
    int dis[maxn];
    int t, n, cnt; //
    
    bool bellman(int s)
    {
    	for(int i=1 ;i<=n; i++)
    		dis[i]=inf;
    	dis[s]=0;
    	bool flag;
    	int x, y, z;
    	for(int i=1; i<n; i++)
    	{
    		flag=false;
    		for(int j=1; j<cnt; j++)//cnt-1是边的条数
    		{
    			x=e[j].a;
    			y=e[j].b;
    			z=e[j].c;
    			if(dis[y] >= dis[x] + z)
    			{
    				dis[y]=dis[x]+z;
    				flag=true;
    			}
    		}
    		if(!flag)
    			break;
    		if(flag && i==n)
    			return false;
    	}
    	return true;
    }
    
    int main()
    {
    	while(scanf("%d%d", &t, &n)!=EOF)
    	{
    		int a, b, c;
    		cnt=1;
    		for(int i=1; i<=t; i++)
    		{
    			scanf("%d%d%d", &a, &b, &c);
    			e[cnt].a=a; e[cnt].b=b; e[cnt++].c=c;
    			e[cnt].a=b; e[cnt].b=a; e[cnt++].c=c;
    		}
    		bellman(1);
    		printf("%d
    ", dis[n]);
    	}
    	return 0;
    }
    

    优化的Bellman算法,也就是鼎鼎大名的SPFA。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<queue>
    using namespace std;
    const int maxn=1e3+7;
    const int inf=0x3f3f3f3f;
    struct edge 
    {
    	int to, cost;
    	edge(){}
    	edge(int a, int b)
    	{
    		to=a;
    		cost=b;
    	}
    };
    int dis[maxn];
    bool inq[maxn];
    int cnt[maxn];
    vector<edge> g[maxn];
    queue<int>que;
    int t, n;
    bool spfa(int s)
    {
    	for(int i=1; i<=n; i++)
    	{
    		dis[i]=inf;
    		inq[i]=false;
    		cnt[maxn]=0;
    	}
    	while(!que.empty()) que.pop();
    	
    	edge e;
    	dis[s]=0;
    	inq[s]=true;
    	que.push(s);
    	while(!que.empty())
    	{
    		int u=que.front();
    		que.pop();
    		inq[u]=false;
    		for(int i=0; i<g[u].size() ; i++)
    		{
    			e=g[u][i];
    			if(dis[e.to] > dis[u]+e.cost)
    			{
    				dis[e.to] = dis[u]+e.cost;
    				if(inq[e.to]==false)
    				{
    					inq[e.to]=true;
    					que.push(e.to);
    					cnt[e.to]++;
    					if(cnt[e.to]>=n) return false; //这里是记录松弛的次数,如果达到n次说明有负环
    				}
    			}
    		}
    	}
    	return true; 
    }
    int main()
    {
    	while(scanf("%d%d", &t, &n)!=EOF)
    	{
    		int a, b, c;
    		for(int i=1; i<=n; i++)
    			g[i].clear();
    		for(int i=1; i<=t; i++)
    		{
    			scanf("%d%d%d", &a, &b, &c);
    			g[a].push_back(edge(b, c));
    			g[b].push_back(edge(a, c));  		
    		}	
    		spfa(1);
    		printf("%d
    ", dis[n]);
    	}
    	return 0;
     } 
    
    欢迎评论交流!
  • 相关阅读:
    linux进程调度(zz)
    为什么vfork的子进程里用return,整个程序会挂掉,而且exit不会(zz)
    ubuntu安装samba
    【前端知乎系列】ArrayBuffer 和 Blob 对象
    【Web技术】442- 关于图片懒加载的几种方案
    【Web技术】441- 蚂蚁前端研发最佳实践
    【面试题】440- 10 道 Nodejs EventLoop 和事件相关面试题
    【面试题】439- 这是一道网红面试题
    【Web技术】438- 移动端体验优化经验总结与实践
    记 · 寒风依旧 · 虎跑路和人生路
  • 原文地址:https://www.cnblogs.com/alking1001/p/11268063.html
Copyright © 2011-2022 走看看