【SDOI 2010】魔法猪学院

本文深入解析A*算法原理,介绍其在寻找最优路径问题中的应用,通过一个具体实例展示了如何利用A*算法解决复杂问题,强调了估价函数在算法效率中的核心作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目】

传送门

题目描述:

iPig 在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练。经过了一周理论知识和一周基本魔法的学习之后,iPig 对猪世界的世界本原有了很多的了解:众所周知,世界是由元素构成的;元素与元素之间可以互相转换;能量守恒……。

能量守恒……iPig 今天就在进行一个麻烦的测验。iPig 在之前的学习中已经知道了很多种元素,并学会了可以转化这些元素的魔法,每种魔法需要消耗 iPig 一定的能量。作为 PKU 的顶尖学猪,让 iPig 用最少的能量完成从一种元素转换到另一种元素……等等,iPig 的魔法导猪可没这么笨!这一次,他给 iPig 带来了很多 1 1 1 号元素的样本,要求 iPig 使用学习过的魔法将它们一个个转化为 n n n 号元素,为了增加难度,要求每份样本的转换过程都不相同。这个看似困难的任务实际上对 iPig 并没有挑战性,因为,他有坚实的后盾……现在的你呀!

注意,两个元素之间的转化可能有多种魔法,转化是单向的。转化的过程中,可以转化到一个元素(包括开始元素)多次,但是一但转化到目标元素,则一份样本的转化过程结束。iPig 的总能量是有限的,所以最多能够转换的样本数一定是一个有限数。具体请参看样例。

输入格式:

第一行三个数 n n n m m m E E E 表示 iPig 知道的元素个数(元素从 1 1 1 n n n 编号)、iPig 已经学会的魔法个数和 iPig 的总能量。

后跟 m m m 行每行三个数 s i s_i si t i t_i ti e i e_i ei 表示 iPig 知道一种魔法,消耗 e i e_i ei 的能量将元素 s i s_i si 变换到元素 t i t_i ti

输出格式:

一行一个数,表示最多可以完成的方式数。输入数据保证至少可以完成一种方式。

样例数据:

输入
4 6 14.9
1 2 1.5
2 1 1.5
1 3 3
2 3 1.5
3 4 1.5
1 4 1.5

输出
3

提示:

【样例解释】
有意义的转换方式共4种:

1->4,消耗能量 1.5
1->2->1->4,消耗能量 4.5
1->3->4,消耗能量 4.5
1->2->3->4,消耗能量 4.5

显然最多只能完成其中的 3 3 3 种转换方式(选第一种方式,后三种方式仍选两个),即最多可以转换 3 3 3 份样本。

如果将 E = 14.9 E=14.9 E=14.9 改为 E = 15 E=15 E=15,则可以完成以上全部方式,答案变为 4 4 4

【数据规模】

占总分不小于 10 % 10\% 10% 的数据满足 n n n 6 6 6 m m m 15 15 15
占总分不小于 20 % 20\% 20% 的数据满足 n n n 100 100 100 m m m 300 300 300 E E E 100 100 100 E E E 和所有的 e i e_i ei 均为整数(可以直接作为整型数字读入)。
所有数据满足 2 2 2 n n n 5000 5000 5000 1 1 1 m m m 200000 200000 200000 1 1 1 E E E 1 0 7 10^7 107 1 1 1 e i e_i ei E E E E E E 和所有的 e i e_i ei 为实数。


【分析】

题解:A*算法

第一次接触 A* 算法,觉得这个算法比较玄妙,和爆搜不一样的是,它像是一种贪心版 d f s dfs dfs

启发函数 f ( i ) = g ( i ) + h ( i ) f(i)=g(i) + h(i) f(i)=g(i)+h(i) 表示估计的该结点的好坏(一般越小代表这个点越优),其中 g ( i ) g(i) g(i) 表示从初始结点到 i i i 的路径长度, h ( i ) h(i) h(i) 表示从 i i i 到目标结点的距离的估计(估计值不能大于实际代价)。A* 算法每次选择 f ( i ) f(i) f(i) 最小的点进行拓展

我们把 h ( i ) h(i) h(i) 叫做估价函数,我觉得它是 A* 算法的核心,只要 h ( i ) h(i) h(i) 足够优秀,搜索就会很快

怎么确定估计值呢,比如说对于这道题,可以简单地看成 t t t 到各个点的最短路(这样 h ( i ) h(i) h(i) 肯定是比实际要小的)

这里摘一个证明
证明: 当 h ( i ) h(i) h(i) 小于等于真实距离时是真实解,因为通过 f ( i ) f(i) f(i) 的比较每次被临时放弃的节点都是因为自己的 f ( i ) f(i) f(i) 比别人的大,而自己的 g ( i ) g(i) g(i) 是真实距离, h ( i ) h(i) h(i) 又小于等于真实距离,所以这个被放弃的点在这一步的确不是最优解,直到它可能是最优解的时候它才会被搜索。 故得证。

A* 算法我觉得先感性理解就行了,先不要过多地深究,不然可能会把自己绕晕

P s Ps Ps:洛谷上这道题貌似卡 A* 算法,可以去其他的网站去交


【代码】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5005
#define M 200005
using namespace std;
int n,m,t1,t2,ans;double e,dist[N];
int first[N],v[M],nxt[M];double w[M];
int First[N],V[M],Nxt[M];double W[M];
struct node{int x;double cost;};
bool operator<(const node &p,const node &q)  {return dist[p.x]+p.cost>dist[q.x]+q.cost;}
void add(int x,int y,double z)  {nxt[++t1]=first[x];first[x]=t1;v[t1]=y;w[t1]=z;}
void readd(int x,int y,double z)  {Nxt[++t2]=First[x];First[x]=t2;V[t2]=y;W[t2]=z;}
priority_queue<pair<double,int> >q;
void dijkstra(int s)
{
	int x,y,i;
	memset(dist,127,sizeof(dist));
	q.push(make_pair(0,s));dist[s]=0;
	while(!q.empty())
	{
		x=q.top().second;q.pop();
		for(i=First[x];i;i=Nxt[i])
		{
			y=V[i];
			if(dist[y]>dist[x]+W[i])
			{
				dist[y]=dist[x]+W[i];
				q.push(make_pair(-dist[y],y));
			}
		}
	}
}
priority_queue<node>que;
void A_star()
{
	que.push((node){1,0});
	while(!que.empty())
	{
		node now=que.top();
		que.pop();
		if(now.x==n)
		{
			e-=now.cost;
			if(e<0)  return;
			ans++;continue;
		}
		int i,j;
		for(i=first[now.x];i;i=nxt[i])
		{
			j=v[i];
			que.push((node){j,now.cost+w[i]});
		}
	}
}
int main()
{
	int x,y,i;double z;
	scanf("%d%d%lf",&n,&m,&e);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d%lf",&x,&y,&z);
		add(x,y,z),readd(y,x,z);
	}
	dijkstra(n);
	A_star();
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值