题意:有一本有n段的书,段的编号为1~n,段的顺序是乱的。有“剪切”和“粘贴”的两种操作,问至少要用多少组剪切和粘贴才能把顺序调整正确。
思路:
IDA*。
估价函数为:h>3*(maxd-d)。h代表前后不匹配 (如果a[i]-a[i-1]==1,那么i与i-1匹配) 的段的个数。由于每次操作h最多加大3 (如书上图,假设a的最后一个数和b的前一个数不连续,b的最后一个数和c的前一个数不连续,c的最后一个数和后面空白的前一个数不连续,调度后它们都变得连续,那么匹配数为1,这是最优情况) ,而剩下只有maxd-d次操作的机会,所以最多只能让3*(maxd-d)个不匹配的段匹配。如果此时不匹配的段更多,那么搜完前一定不能完全匹配,可以剪枝。
还有不能拆开已经变成连续了的序列,如书中【策略3】。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#include<sstream>
#include<queue>
using namespace std;
int n;
string a;
int maxd;
int find(string y) {
int sum=0;
for(int i=0; i<y.size()-1; i++) {
if(y[i+1]-y[i]!=1) ++sum;
}
return sum;
}
bool dfs(int d,string a) {
if(d==maxd) {
for(int i=0; i<a.size(); i++) {
if(a[i]-'0'!=i+1) return false;
}
return true;
}
for(int i=0; i<a.size(); i++) {
if(i!=0&&a[i]-a[i-1]==1) continue;
for(int j=i; j<a.size(); j++) {
string x=a.substr(i,j-i+1);
if(j!=a.size()-1&&a[j+1]-a[j]==1) continue;
string after=a.substr(j+1,a.size()-j-1);
string before=a.substr(0,a.size()-x.size()-after.size());
for(int k=0; k<after.size(); k++) {
string y=before+after.substr(0,k+1)+x+after.substr(k+1,after.size()-k-1);
int h=find(y);
if(h>3*(maxd-1-d)) continue;
if(dfs(1+d,y)) return true;
}
}
}
return false;
}
int main() {
int T=0;
while(scanf("%d",&n)==1&&n!=0) {
a.clear();
for(int i=0; i<n; i++) {
int x;
scanf("%d",&x);
a+=(char)(x+'0');
}
for(maxd=0; maxd<n; maxd++) {
if(dfs(0,a)) {
printf("Case %d: %d\n",++T,maxd);
break;
}
}
}
return 0;
}