【习题·搜索】Bookcase(IDA*)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/88977918

题目描述

有N(1 <=N <= 15)本书,每本与每本的高度都不一样。现在可以按照以下的办法整理书:抽出一摞书,再保持原来的顺序插进一个位置。这样的话我们称之为“一次操作”。现在你需要求出至少需要经过几次操作才能让书变成按高度升序的状态。如果需要5次或者多于5次,只需要输出“5 or more”。

题解

如果暴力考虑搜索:我们将 L L - R R 的书放到 K K 后面,此时一定满足 K &gt; R K&gt;R ,否则与先前重复枚举;此时这些书的序列就会有:序列 L L - R R 和序列 R + 1 R+1 - K K 进行交换,我们使用临时数组存储即可。

因为输出不超过五部,我们需要使用迭代加深搜索。

然后考虑能否设计出一个适当的估价函数利用IDA*算法解决;显然每次进行移动操作的时候,会有三个点的后继发生改变,即 L 1 L-1 R R K K 的后继;如果满足 a [ i ] + 1 = a [ i + 1 ] a[i]+1=a[i+1] ,则 i + 1 i+1 i i 的正确后继,如若每一个序列的错误后继为 t o t tot ,则可以令函数值 = t o t =\lceil tot \rceil ,即每一次最多修改三个后继使其变正确。

代码

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

int n;
int a[20];

inline int read()
{
	int s=0;char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9') s=s*10+c-48, c=getchar();
	return s; 	
}

inline int func(void)
{
	int tot=0;
	for (int i=1;i<=n;++i) 
	    if (a[i]+1!=a[i+1]) tot ++;
	return tot;
}

void change(int L,int R,int K)
{
	int t[20] = {}, c=0;
	for (int i=R+1;i<=K;++i) t[++c]=a[i];
	for (int i=L;i<=R;++i) t[++c]=a[i];
	for (int i=L;i<=K;++i) a[i]=t[i-L+1]; 
}

bool dfs(int deep,int MAXD)
{
	int b[20]={}; 
	if (deep==MAXD)
	{
		if (!func()) return 1;
		return 0;
	}
	int G=func();
	G=G%3 ? G/3+1 : G/3;
	if (deep+G>=5) return 0; //IDA*剪枝
	memcpy(b,a,sizeof(b));
	for (int L=1;L<=n;++L) 
	    for (int R=L;R<=n;++R) 
		    for (int K=R+1;K<=n;++K) 
			{
				//交换 L - R 和  R+1 - K
				change(L,R,K);
				if (dfs(deep+1,MAXD)) return 1;
				memcpy(a,b,sizeof(a));
			} 
	return 0;
}
int main(void)
{
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	int T;
	T=read();
	Again:
		bool flag=0;
		n=read();
		a[n+1]=n+1;
		for (int i=1;i<=n;++i) a[i]=read();
		for (int i=0;i<=4;++i)
			if (dfs(0,i)) 
			{
				printf("%d\n",i);
				flag=1;
				break;
			}
		if (!flag) puts("5 or more");
	if (--T) goto Again;
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/Ronaldo7_ZYB/article/details/88977918
今日推荐