基本思路和算法(A*)大佬们都讲的很详细了(不会的点这里),这里就提供一个小小的但很实用优化。
离AC只差一步的可以来看看
《算法竞赛进阶指南》第124页写到“估价函数的估值不能大于未来的实际价值”,但在这题中,稍稍提高一点估值可以大大提高程序运行效
优化前后效率快了将近一倍,而优化所做的所有改变就是在估价函数上乘了一个系数
一般的估价函数:每个旋钮距离目标状态(1)的差值之和除以2(取上整)
bool operator < (const e &a) const { return s + (v >> 1) + (v & 1) > a.s + (a.v >> 1) + (a.v & 1); }
注:
1、s指当前状态已经操作的次数,v表示每个旋钮距离目标状态的差值之和(估价函数)
2、加上(v&1)是为了取上整(也许没什么用)
3、该比较函数写在结构体中,结构体名称为e
不难发现,这个估价函数是一个很美好的理想状态(每次操作都让两个旋钮向目标状态靠近),但现实中很难达到,可以让估值更准确一些,于是我在 v 上乘一个稍大于1的系数(1.1, 1.2, 1.3)均可(下面代码中写的1.3,因为这个比较快),可以自己调试
变成这样:
bool operator < (const e &a) const { return s + (v >> 1) * 1.3 + (v & 1) > a.s + (a.v >> 1) * 1.3 + (a.v & 1); }
小小的改变就上效率大大提高,快了将近400ms(数据最大的点快了300ms,虽然巨佬们总用时都不到300ms)
完整代码
#pragma GCC optimize("Ofast") #include <bits/stdc++.h> using namespace std; #define x now[a[p][now[p]]] #define y now[p] int tx, a[13][5], k[30000000], to[5] = {0, 2, 3, 4, 1}; struct e { int s, sum, v, now[13], step[20]; bool operator < (const e &a) const { return s + (v >> 1) * 1.3 + (v & 1) > a.s + (a.v >> 1) * 1.3 + (a.v & 1); } inline void change (int p) { x = to[x], tx = x, y = to[y]; v += (tx == 2) * 3 - (tx != 2) + (y == 2) * 3 - (y != 2); step[++s] = p, sum += (tx == 1) - (tx == 2) + (y == 1) - (y == 2); } inline int get () { int tmp(0); for (int i = 1; i <= 12; ++i) tmp = (tmp << 2) + now[i]; return tmp; } } top, tmp, t; void Just_Do_It () { priority_queue <e> q; q.push (top); while (!q.empty()) { tmp = q.top(); q.pop(); for (int i = 12; i; --i) { t = tmp; t.change (i); if (t.sum == 12) { printf ("%d ", t.s); for (int i = 1; i <= t.s; ++i) printf ("%d ", t.step[i]); return; } int tt (t.get()); if (k[tt]) continue; k[tt] = 1; q.push (t); } } } int main() { for (int i = 1; i <= 12; ++i) scanf ("%d %d %d %d %d", &top.now[i], &a[i][1], &a[i][2], &a[i][3], &a[i][4]); for (int i = 1; i <= 12; ++i) top.sum += (top.now[i] == 1), top.v += (top.now[i] == 1) ? 0 : 5 - i; k[top.get()] = 1; Just_Do_It (); return 0; }