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;
}