主要思路来自于紫书,利用迭代加深的算法,一层一层往下搜,注意迭代加深的主体是bfs。所以每次的最大搜索层数maxd,都代表着0到maxd-1层都没有找到答案,所以,迭代加深相当于在maxd-1层的基础上又搜了一层(maxd层),这是典型的bfs,用来求最短路等问题(本题中求的是最少的剪切/粘贴次数)。
本题中,n的范围是1到9,所以搜索层数的范围就是1到8。
估价函数h()是这样确定的:每次剪切/粘贴最多改变三个数的后继(读者可以自己验证一下),也就是说,每搜一层,最多使三个数字的后继由错误变正确,所以,当(maxd - 当前层数)*3 < 当前错误后继数时,就应该剪枝。
#include <cstdio>
#include <cstring>
int a[30];
int kase = 1;
int n;
int hh()
{
int cur = 0;
for(int i = 1; i < n; i++)
if(a[i+1] != a[i] + 1) cur++;
if(a[n] != n) cur++;
return cur;
}
bool dfs(int cur, int maxd)
{
int h = hh();
if(cur == maxd) return h == 0;
if(cur < maxd) if(3*cur + h > 3*maxd + 1) return false;
int olda[30];
int b[30];
memcpy(olda, a, sizeof(a));
for(int i = 1; i <= n; i++) // 剪切的左边界
for(int j = i; j <= n; j++) // 剪切的右边界
{
int now = 1;
for(int k = 1; k <= n; k++)
if(k < i || k > j) b[now++] = a[k];
for(int d = 1; d <= now; d++) //剪下来的部分是a[i..j],剩下的部分是b[1..now-1],要把a[i..j]这个整体依次插入到b[1],b[2],..,b[now]的位置。
{
int now2 = 1;
int ii = i;
for(int p = d; p < d+j-i+1; p++) a[p] = olda[ii++];
for(int p = 1; p < d; p++) a[p] = b[now2++];
for(int p = d+j-i+1; ; p++)
{
//printf("p = %d i = %d j = %d n = %d\n",p,i,j,n);
a[p] = b[now2++];
if(now2 > n-(j-i+1)) break;
}
if(dfs(cur+1, maxd)) return true; //搜下一层
memcpy(a, olda, sizeof(a)); //还原a数组
}
}
return false;
}
int solve()
{
int maxd_max = 8;
for(int maxd = 0; maxd < 8; maxd++)
if(dfs(0, maxd)) return maxd;
return maxd_max;
}
int main()
{
//freopen("ztest.txt","r",stdin);
//freopen("zans.txt","w",stdout);
while(scanf("%d",&n) && n)
{
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
printf("Case %d: %d\n", kase++, solve());
}
return 0;
}