a* 求次短路 《路由选择问题》

22. [HAOI2005] 路由选择问题

★★★   输入文件: route.in   输出文件: route.out    简单对比
时间限制:1 s   内存限制:128 MB

【问题描述】

    X城有一个含有N个节点的通信网络,在通信中,我们往往关心信息从一个节点I传输到节点J的最短路径。遗憾的是,由于种种原因,线路中总有一些节点会出故障,因此在传输中要避开故障节点。
任务一:在己知故障节点的情况下,求避开这些故障节点,从节点I到节点J的最短路径S0。
任务二:在不考虑故障节点的情况下,求从节点I到节点J的最短路径S1、第二最短路径S2。

【输入文件】

第1行: N I J (节点个数 起始节点 目标节点)
第2—N+1行: Sk1 Sk2…SkN (节点K到节点J的距离为SkJ K=1,2,……,N)
最后一行: P T1 T2……Tp (故障节点的个数及编号)

【输出文件】

S0 S1 S2 (S1<=S2 从节点I到节点J至少有两条不同路径)

【输入输出样例】


route.in

5 1 5
0 10 5 0 0
10 0 0 6 20
5 0 0 30 35
0 6 30 0 6
0 20 35 6 0
1 2

route.out

40 22 30

【约束条件】

(1)N<=50 N个节点的编号为1,2,…,N
(2)Skj为整数,Skj<=100,(K,J=1,2…,N 若Skj=0表示节点K到节点J没线路)
(3)P<=5  


此题是一个典型的最短路问题,s0,s1好求,基本的最短路,spfa和dijs均可顺利解决,s2解决次短路有不少方法,我是用a*解决,但此题要注意,不能有自反边,所以在a*的时候要注意不能反复走同一条边,具体解决参考代码。

#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define Mem(arr,val) memset(arr,val,sizeof(arr));
const int maxn=55;
const int maxe=maxn*maxn;
struct Edge
{
	int to,data,next;
};
struct Node
{
	int now,g,f,fa;
	Node(){};
	Node(int x,int y,int z,int w){now=x;g=y,f=z;fa=w;}
	bool operator < (const Node &a)const
	{
		if(f==a.f)return g>a.g;
		return f>a.f;
	}
};
Edge e[maxe],re[maxe];
int n,p,st,en,len,head[maxn],rehead[maxn],f[maxn];
int dis[maxn];
void Init();
void Insert(int,int,int);
void Spfa1(int);
void Spfa2(int);
int Astar(int,int);
int main()
{
	freopen("route.in","r",stdin);
	freopen("route.out","w",stdout);
	Init();
	Spfa2(st);
	Spfa1(en);
	//for(int i=1;i<=n;i++)printf("%d ",dis[i]);printf("\n");
	printf("%d %d\n",dis[st],Astar(st,en));
	return 0;
}
void Init()
{
	Mem(head,-1);Mem(rehead,-1);
	Mem(e,0);Mem(re,0);Mem(f,0);
	scanf("%d%d%d",&n,&st,&en);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			int x;scanf("%d",&x);
			if(x) Insert(i,j,x);//把临接矩阵转成邻接表存储			
		}
	scanf("%d",&p);
	for(int i=1;i<=p;i++)
	{
		int x;
		scanf("%d",&x);f[x]=1;
	}
}
void Insert(int x,int y,int z)
{
	len++;
	e[len].to=y;e[len].data=z;e[len].next=head[x];head[x]=len;
	re[len].to=x;re[len].data=z;re[len].next=rehead[y];rehead[y]=len;//如果是有向边需存储你像边,此题不需要 
}
void Spfa1(int st)//求无坏点的最短路 
{
	queue<int> q;bool flag[maxn]={0};
	Mem(dis,0x7f);
	flag[st]=0;dis[st]=0;q.push(st);
	while(!q.empty())
	{
		int k=q.front();q.pop();flag[k]=0;
		for(int i=head[k];i!=-1;i=e[i].next)
		{
			int j=e[i].to;
			if(dis[j]>dis[k]+e[i].data)
			{
				dis[j]=dis[k]+e[i].data;
				if(!flag[j])
				{
					flag[j]=1;
					q.push(j);
				}
			}
		}
	}
	//for(int i=1;i<=n;i++)printf("%d ",dis[i]);
}
void Spfa2(int x)//求有坏点的最短路 
{
	bool flag[maxn]={0};
	int dis[maxn];Mem(dis,0x7f);
	queue<int>q;flag[x]=1;dis[x]=0;q.push(x);
	while(!q.empty())
	{
		int k=q.front();q.pop();flag[k]=0;
		for(int i=head[k];i!=-1;i=e[i].next)
		{
			int j=e[i].to;//如果此点为坏点直接跳过 
			if(f[j])continue;
			if(dis[j]>dis[k]+e[i].data)
			{
				dis[j]=dis[k]+e[i].data;
				if(!flag[j])
				{
					q.push(j);flag[j]=1;
				}
			}
		}
	}
	printf("%d ",dis[en]);
}
int Astar(int s,int t)
{
	priority_queue<Node> q;
	int fa[maxn];Mem(fa,-1);
	int tot=0;q.push(Node(s,0,dis[s],fa[s]));//正常需要传递3个参数即可,但为了处理
	//自返边传递了当前点的父节点。第一个参数是当前节点,第二个参数是当前点到源点的距离
	// 第三个参数是当前节点到终点的最短距离,第四个参数是当前节点的父亲节点 
	while(!q.empty())
	{		
		Node x=q.top();q.pop();
		if(x.now==t)//如果当前节点是终点,说明找到了一条第tot+1短的边 
		{
			tot++;
			if(tot==2)return x.g;
		}
		for(int i=head[x.now];i!=-1;i=e[i].next)
		{
			if(x.fa==e[i].to)continue;//如果当前边指向了其父节点说明自反了,跳过 
			int now=e[i].to,g=x.g+e[i].data,f=now+g;
			fa[now]=x.now;
			q.push(Node(now,g,f,fa[now]));
		}
	}
	return -1;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值