P5183 [COCI 2009] POSLOZI
先看数据范围
首先,个人认为这题广搜应该不太行。毕竟一个队列中的节点还需要记录许多信息,对于我而言,我认为十分麻烦。不过听说本题可以用 A 过。 下面我介绍一种比较方便的方法 IDA。
搜索算法的本身框架十分简单,这里便不多说了。主要讨论搜索的优化
剪枝一:不妨设刚刚使用的操作编号为 虽然这个剪枝可能优化效果不好,但是乱剪枝总比不剪好。
剪枝二:估价函数。我们不妨设当前排列与全排列对不上的个数为
#include <bits/stdc++.h>
using namespace std;
int n, m, a[13], b[170][2], depth, d[13][13];
vector<int> p, _p;
int h() {
int cnt = 0;
for (register int i = 1; i <= n; ++i) cnt += d[i][a[i]];
return cnt;
}
bool dfs(int dep, int pre) {
int k = h();
if (!k) {
_p = p;
return 1;
}
if (2 * dep + k > 2 * depth) return 0;
for (register int i = 1; i <= m; ++i)
if (pre != i) {
swap(a[b[i][0]], a[b[i][1]]);
p.push_back(i);
if (dfs(dep + 1, i)) return 1;
p.pop_back();
swap(a[b[i][0]], a[b[i][1]]);
}
return 0;
}
int main() {
scanf("%d%d", &n, &m);
memset(d, 0x3f, sizeof d);
for (register int i = 1; i <= n; ++i) scanf("%d", a + i), d[i][i] = 0;
for (register int i = 1; i <= m; ++i) {
scanf("%d%d", b[i], b[i] + 1);
d[b[i][0]][b[i][1]] = d[b[i][1]][b[i][0]] = min(d[b[i][1]][b[i][0]], 1);
}
for (register int k = 1; k <= n; ++k)
for (register int i = 1; i <= n; ++i)
for (register int j = 1; j <= n; ++j)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
depth = 0;
while (!dfs(0, -1) && depth <= 30) ++depth;
if (depth == 31) puts("NEMA");
else {
printf("%d\n", depth);
for (register int i = 0; i < _p.size(); ++i) printf("%d\n", _p[i]);
}
}