1.题目描述:点击打开链接
2.解题思路:本题利用迭代加深搜索,也是一道典型的状态空间搜索问题,状态就是1~n的排列,初始状态是输入,终止状态是1,2,……n。由于n≤9,排列最多有9!=362880个,但由于每个状态的后继状态比较多,因此仍有TLE的危险。本题如果利用迭代加深搜索,可以发现做多只需要8步,关键在于如何有效地剪枝。考虑后继不正确的数字的个数h,可以证明每次剪切时h最多减少3(因为一次剪切最多只会改变3个数字的后继,若剪切后这3个数字的后继都正确,则h最多减少了3),因此当h>3*(maxd-d)时剪枝即可。
3.代码:
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm> #include<string> #include<sstream> #include<set> #include<vector> #include<stack> #include<map> #include<queue> #include<cstdlib> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int maxn = 12; int arr[maxn]; int maxd, n; struct node { int v[maxn]; }; int geth(int*a)//计算后继不正确的项的个数 { int cnt = 0; for (int i = 0; i < n;i++) if (a[i] + 1 != a[i + 1]) cnt++; return cnt; } void moveTo(node&s, node&t, int i, int j, int k) { int num = j - i + 1;//要移动的个数 for (int u = 0; u < num; u++)//把原来i到j之间的数移到k后面 t.v[k + u] = s.v[i + u]; num = i - k; for (int u = 0; u < num; u++) //把原来k到i之间(不包括i)的数移到k+j-i后面 t.v[j - u] = s.v[i - 1 - u]; } bool dfs(int d, node t) { if (geth(t.v)>3 * (maxd - d))return false; if (geth(t.v) == 0)return true; node next; for (int i = 0; i < n;i++) for (int j = i; j < n;j++) for (int k = 0; k < i; k++) { memcpy(next.v, t.v, sizeof(t.v)); moveTo(t, next, i, j, k); if (dfs(d + 1, next))return true; } return false; } int main() { int v = 0; while (scanf("%d", &n)&&n) { for (int i = 0; i < n; i++) scanf("%d", arr + i); arr[n] = n + 1;//辅助后面判断h的大小 int tmp = 0; tmp = geth(arr); node a1; memcpy(a1.v, arr, sizeof(arr)); if (tmp) { for (maxd = 1;;maxd++) if (dfs(0, a1))break; } printf("Case %d: ", ++v); if (!tmp) printf("0\n"); else printf("%d\n", maxd); } return 0; }