例题7-10 编辑书稿 UVa11212

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

你可能感兴趣的:(算法竞赛入门经典(第二版),常用技巧——剪枝,搜索——广度优先搜索(BFS),搜索——迭代加深搜索或IDA*)