蓝桥杯:糖果【玄学A*算法】

本文探讨了如何使用A*算法解决糖果配对问题,通过压缩糖果状态并引入评估函数,实现高效搜索。作者以丢失物品找寻为例,解释了评估函数如何指导搜索过程。代码示例展示了如何结合优先队列和启发式策略求解最优路径。

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

先看题:

如果不考虑时间复杂度,那么这道题最好写的那么肯定是bfs;

但是肯定会超时,这时本菜发现,一种味道的糖果有两种状态,一种是被尝过,一种是没尝过。

那么我们可以将M种糖果的状态压缩到一个int型二进制中,然后我们又知道最终我们要找的状态的二进制数对应的十进制数为(1<<m)-1,那么一个算法就对应上了,即A*算法,又称启发性搜索;【至于为什么叫这个算法为玄学算法,dddd】

在这种我简单说一下A*算法吧【之前有写过一篇更加详细的】

A*算法在本菜眼里呢等于优先队列+评估函数+朴素bfs,其实很好理解这个函数的;

我们给每个搜索的状态应该新的元素,即代价,然后我们用优先队列挑出代价最小的那个状态,然后对这个状态就行扩展;

打个比方,如果你在家里丢了东西,那么你一定会去有可能存在失物的地方去找,而不是去鸟不拉屎的杂物间找;

我们的这个评估函数就是给每个状态一个评估值,评估值越小,说明越靠近答案,有以下公式:

f[x]=g[x]+h[x]

f[x]为一个状态的代价,g[x]为从起始状态到此状态用了多少步,h[x]为从目前状态到目标状态预计要付出的代价;

那么有以下代码:

#include<iostream>
#include<algorithm>
#include<queue>
#include<time.h>
#include<math.h>
using namespace std;

const int M = 1 << 20, N = 100;

int n, m, k;
clock_t a, b;

struct p {
	bool st1[N] = {};//当前状态用到的袋状态,st1[i]=1表示第i包糖果被选择,=0表示没被选择
	int id = 0;//当前状态的id
	double cost = 0;//一个状态的代价
	int num = 0;//用到的袋数,对应位从起始状态到此状态付出的代价
}st[N + 10];

int ha[M];//记录每个状态是否出现过,防止爆内存

double get_cost(p a) {//这个函数的作用是给出一个状态a,然后返回这个状态的代价
	double sum = 0;

	for (int i = 0; i < m; i++) {
		if (!((a.id >> i) & 1)) sum++;
	}
	sum /=2.255;
	sum += a.num;
	return sum;
}

struct cmp {
	bool operator() (p a, p b) {
		return b.cost < a.cost;
	}
};

p make(p a, int x) {//这个函数的作用是给出一个状态a,然后在这个状态的基础上选择第x包糖果,然后返回改变后的状态
	a.num++;
	a.id = a.id | st[x].id;

	a.st1[x] = 1;
	a.cost = get_cost(a);
	return a;
}

int bfs() {

	priority_queue<p, vector<p>, cmp> que;

	for (int i = 1; i <= n; i++) {
		que.push(st[i]);
	}

	while (que.size()) {//一个朴素的bfs

		p t = que.top();
		que.pop();

		ha[t.id] = 1;

		if (t.id == ((1 << m) - 1))
			return t.num;

		p mid;
		for (int i = 1; i <= n; i++) {

			if (!t.st1[i]) {
				mid = make(t, i);
				if (!ha[mid.id])
					que.push(mid), ha[mid.id] = 1;
			}
		}

	}
	
	return -1;

}

int main() {

	cin >> n >> m >> k;

	for (int i = 1; i <= n; i++) {
		for (int j = 0; j < k; j++) {

			int x; cin >> x;
			st[i].id = st[i].id | (1 << (x - 1));
		}
		st[i].cost = get_cost(st[i]);
		st[i].num = 1;
	}

	cout << bfs();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值