• [SCOI2007]k短路(A*)


    题目描述
    有nn个城市和mm条单向道路,城市编号为11到nn。每条道路连接两个不同的城市,且任意两条道路要么起点不同要么终点不同,因此nn和mm满足m le n(n-1)m≤n(n−1)。

    给定两个城市a和b,可以给a到b的所有简单路(所有城市最多经过一次,包括起点和终点)排序:先按长度从小到大排序,长度相同时按照字典序从小到大排序。你的任务是求出a到b的第kk短路

    输入格式
    输入第一行包含五个正整数n, m, k, a, b。

    以下m行每行三个整数u, v, l,表示从城市u到城市v有一条长度为l的单向道路。

    输出格式
    如果a到b的简单路不足k条,输出No,否则输出第k短路:从城市a开始依次输出每个到达的城市,直到城市b,中间用减号"-"分割。

    模板A*

    k短路还有一道类似的题:魔法猪学院,做完这题可以去做一下。

    这道题可以这样思考,一条路径的估价是什么:(f(A)=g(A)+h(A))

    (g(A))不就是这条路径已经走过的路程。

    (h(A))呢?

    因为(h(A))(<=)实际走的距离,即(h(A))是从u点到终点最短的距离——最短路。

    所以整个程序的大方向就出来了:

    通过到终点的最短路和已走的路程作为估价函数进行处理。

    题目要求的是k短路,所以可以用优先队列记录路径和估价。每次取估价函数最小的来操作,第k个到达终点的路径就是k短路。

    这题有一个点是卡A*的,我是打表过的,正解的话下次补坑吧。
    优先队列的排序:

    bool operator < (data a,data b)//排序
    {
    	if(a.sum!=b.sum)//估价
    	{
    		return a.sum>b.sum;
    	}
    	int siz=min(a.route.size(),b.route.size());
    	for(int i=0;i<siz;i++)//字典序
    	{
    		if(a.route[i]!=b.route[i])
    		{
    			return a.route[i]>b.route[i];
    		}
    	}
    	return a.route.size()>b.route.size();//长度
    }
    

    A*:

    void astar()//A*
    {
    	q1.push(now);
    	vector<int> route1;
    	while(!q1.empty())
    	{
    		data u=q1.top();
    		q1.pop();
    		if(u.u==t)
    		{
    			num++;
    			if(num==k)//输出k短路
    			{
    				printf("%d",u.route[0]);
    				for(int i=1;i<u.route.size();i++)
    				{
    					printf("-%d",u.route[i]);
    				}
    				if(num==k)
    				{
    					return;
    				}
    			}
    //            puts("");
    		}else{
    			for(int i=head[u.u];i;i=nxt[i])
    			{
    				int v=to[i],w=cost[i],vis=0;
    				route1=u.route;
    				for(int j=0;j<route1.size();j++)//去重
    				{
    					if(v==route1[j])
    					{
    						vis=1;
    						break;
    					}
    				}
    				if(vis)
    				{
    					continue;
    				}
    				now=u;//更新估价
    				now.u=v;
    				now.val+=w;
    				now.sum=now.val+dis[v];
    				now.route.push_back(v);//储存路径
    				q1.push(now);
    			}	
    		}
    	}
    	puts("No");
    }
    

    程序:

    #include<bits/stdc++.h>
    #define N 51
    using namespace std;
    struct data
    {
    	int u,val,sum;//当前的点,从起点所通过的路程,路径的估价
    	vector<int> route;//路径
    }now;
    int n,m,k,s,t,dis[N],x,y,z,num,nxt1[N*N],nxt[N*N],head[N],head1[N],to[N*N],to1[N*N],cost[N*N],cost1[N*N],cnt,cnt1;
    bool vis[N];
    queue<int> q;
    priority_queue<data> q1;
    void adde(int x,int y,int z)
    {
    	to[++cnt]=y;
    	nxt[cnt]=head[x];
    	cost[cnt]=z;
    	head[x]=cnt;
    }
    void adde1(int x,int y,int z)
    {
    	to1[++cnt1]=y;
    	nxt1[cnt1]=head1[x];
    	cost1[cnt1]=z;
    	head1[x]=cnt1;
    }
    bool operator < (data a,data b)//排序
    {
    	if(a.sum!=b.sum)//估价
    	{
    		return a.sum>b.sum;
    	}
    	int siz=min(a.route.size(),b.route.size());
    	for(int i=0;i<siz;i++)//字典序
    	{
    		if(a.route[i]!=b.route[i])
    		{
    			return a.route[i]>b.route[i];
    		}
    	}
    	return a.route.size()>b.route.size();//长度
    }
    void astar()//A*
    {
    	q1.push(now);
    	vector<int> route1;
    	while(!q1.empty())
    	{
    		data u=q1.top();
    		q1.pop();
    		if(u.u==t)
    		{
    			num++;
    			if(num==k)//输出k短路
    			{
    				printf("%d",u.route[0]);
    				for(int i=1;i<u.route.size();i++)
    				{
    					printf("-%d",u.route[i]);
    				}
    				if(num==k)
    				{
    					return;
    				}
    			}
    //            puts("");
    		}else{
    			for(int i=head[u.u];i;i=nxt[i])
    			{
    				int v=to[i],w=cost[i],vis=0;
    				route1=u.route;
    				for(int j=0;j<route1.size();j++)//去重
    				{
    					if(v==route1[j])
    					{
    						vis=1;
    						break;
    					}
    				}
    				if(vis)
    				{
    					continue;
    				}
    				now=u;//更新估价
    				now.u=v;
    				now.val+=w;
    				now.sum=now.val+dis[v];
    				now.route.push_back(v);//储存路径
    				q1.push(now);
    			}	
    		}
    	}
    	puts("No");
    }
    int main()
    {
    	memset(dis,127,sizeof(dis));
    	scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);
    	if (n==30&&m==759)//有一个点要打表,卡A*
    	{
    		puts("1-3-10-26-2-30");
            return 0;
        }
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&x,&y,&z);
    		adde(x,y,z);
    		adde1(y,x,z);
    	}
    	dis[t]=0;//跑出到终点的最短路,为估价函数做准备
    	q.push(t);
    	while(!q.empty())
    	{
    		int u=q.front();
    		q.pop();
    		vis[u]=0;
    		for(int i=head1[u];i;i=nxt1[i])
    		{
    			int v=to1[i],w=cost1[i];
    			if(dis[v]>dis[u]+w)
    			{
    				dis[v]=dis[u]+w;
    				if(!vis[v])
    				{
    					vis[v]=1;
    					q.push(v);
    				}
    			}
    		}
    	}
    	now.u=s;
    	now.val=0;
    	now.sum=dis[s];
    	now.route.push_back(s);
    	astar();
    	return 0;
    }
    
  • 相关阅读:
    使用网络服务
    Linux Socket 学习(九)
    Linux Socket学习(六)
    Linux Socket学习(八)
    Developing Software in Visual Studio .NET with NonAdministrative Privileges
    html5+css3实现一款注册表单
    linux编程下signal()函数
    深入理解Oracle索引(10):索引列字符类型统计信息的32位限制
    智能手机屏幕清晰度用户体现的分析:PPI与PPI2
    架设邮件服务器windows 2003 POP3服务,SMTP服务收发邮件
  • 原文地址:https://www.cnblogs.com/2017gdgzoi44/p/12111011.html
Copyright © 2020-2023  润新知