这道题让我先是超时再是WA,真是无语,hdu的讨论里说dp也能做(好久没碰dp了),既然是练搜索嘛,那就用搜索做,dfs玩的就是回溯,这句话是在一个博客里看的,现在感觉颇有道理。
本题需要对dfs剪枝,虽然我的代码在没有剪枝的情况下也过了(我觉得是数据水了),大家可以看以下的提交记录;
这是未剪枝的情况:
这是剪枝的情况:
大家可以看到仅仅一句代码(见dfs函数里的剪枝代码——已注释),时间从904ms缩减到140ms,在此我为大家解释下剪枝代码的含义,if (times >= ans) return ; dfs函数中的times参数记录的是移动的总距离,我们追求的答案是移动的最小距离,如果你此时找的times比你已经找到的最终答案ans'还要大,那么你将没有必要继续进行搜索,这将省去很多无用的判断,当然还可能有更多的剪枝方法,我这里列举的只是其一,欢迎大家在评论里指出。
在这里我在给大家一组测试数据,如下代码注释中的数据,正确答案为11。
#include <bits/stdc++.h>
#define min(x, y) x < y ? x : y
#define INF 0x3f3f3f3f
using namespace std;
int a[11];
int ans;
bool vis[11];
/*
1
3 4 1 2 5 6 7 8 9 10
*/
int findNext(int inx) //寻找下标为inx的元素应该移动到的正确位置
{
for (int i = 1; i < 11; i++) {
if (a[i] == a[inx] + 1) {
if (vis[i] == false) return i;
else return findNext(i);
}
}
}
int Move(int inx) //计算移动的距离
{
return abs(inx-findNext(inx));
}
void dfs(int times, int num)
{
if (times >= ans) return ; //dfs剪枝
if(num == 9) {
ans = min(ans, times);
return ;
}
for (int i = 1; i < 11; i++) {
if (a[i] == 10) continue;
if (vis[i] == false) {
vis[i] = true;
dfs(times+Move(i), num+1);
vis[i] = false;
}
}
}
int main()
{
int t;
cin >> t;
while (t--) {
for (int i = 1; i < 11; i++) cin >> a[i];
memset(vis, false, sizeof(vis));
ans = INF;
for (int i = 1; i < 11; i++) {
if (a[i] == 10) continue;
vis[i] = true;
dfs(Move(i), 1);
vis[i] = false;
}
cout << ans <<endl;
}
return 0;
}