您的位置:首页 > 其它

UVA 11212 Editing a Book [迭代加深搜索IDA*]

2016-03-20 12:10 447 查看

 

                        11212 Editing a Book
  You have n equal-length paragraphs numbered 1 to n. Now you want to arrange them in the order of
1, 2, . . . , n. With the help of a clipboard, you can easily do this: Ctrl-X (cut) and Ctrl-V (paste) several
times. You cannot cut twice before pasting, but you can cut several contiguous paragraphs at the same
time - they’ll be pasted in order.
For example, in order to make {2, 4, 1, 5, 3, 6}, you can cut 1 and paste before 2, then cut 3 and
paste before 4. As another example, one copy and paste is enough for {3, 4, 5, 1, 2}. There are two
ways to do so: cut {3, 4, 5} and paste after {1, 2}, or cut {1, 2} and paste before {3, 4, 5}.
Input
  The input consists of at most 20 test cases. Each case begins with a line containing a single integer n
(1 < n < 10), thenumber of paragraphs. The next line contains a permutation of 1, 2, 3, . . . , n. The
last case is followed by a single zero, which should not be processed.
Output
  For each test case, print the case number and the minimal number of cut/paste operations.
Sample Input
6
2 4 1 5 3 6
5
3 4 5 1 2
0
Sample Output
Case 1: 2
Case 2: 1

解题思路:

  1.简单分析我们可以发现,当n=9时,最多只需要剪切八次即可完成排序。并且全排列数量9!=362880不算很大,所以我们可以将当前排列作为状态,转化成十进制数存入set以便判重。然后逐渐增加解答树的深度(搜索最大深度)进行迭代加深搜索。

  2.构造启发函数。本题可以定义一个后继错数:当前状态中,后继元素不正确的元素个数。可以证明,每一次剪切粘贴最多改变3个数的后继数,那么错数最多减少3.比如  1 2 4 3,错数是3,1 2 3 4,错数是0. 假设当前搜索到第d层,最大搜索深度为maxd,那么如果当前状态的错数 h>3*(maxd-d),则说明这个状态无解,剪枝;

  3.状态转移:以长度递增的顺序,依次从每个元素开始剪切相应长度的一段,然后依次插入后继元素之后(用链表存储序列更方便剪切和插入操作)。

代码如下(关键内容有注释):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <algorithm>
#include <ctime>
using namespace std;

#define print_time_ printf("time : %f\n",double(clock())/CLOCKS_PER_SEC)
const int maxn=9;
set<int> v;//存储状态
int next_[maxn+2];//用链表存储当前序列
int n;
int maxd;

inline int Atoi(int *next){ //将当前序列转换成十进制数
int ans=0;
for(int i=next[0],j=0;j<n;j++,i=next[i])
ans=ans*10+i;
return ans;
}
inline bool isvisited(int *A){//判重
return v.count(Atoi(A));
}
inline void push_v(int *A){
v.insert(Atoi(A));
}
int h(int *next){//获得当前状态下的错数
int h=0;
for(int i=next[0],j=1;j<=n;j++,i=next[i]){
if(j<n){
if(i==n||(i!=n&&next[i]!=i+1))
h++;
}
else if(i!=n)
h++;
}
return h;
}
int get_r(int& l,int& len){ //获得被剪切段的最右端
int r=l;
for(int i=0;i<len-1;i++)
r=next_[r];
return r;
}
bool IDA(int d){
if(d==maxd){
if(h(next_)==0)
return true;
else return false;
}
int h_=h(next_);
if(h_>3*(maxd-d))
return false;
for(int len=1;len<n;len++){
for(int last=0,l=next_[0],j=1;j+len-1<=n;j++,last=l,l=next_[l]){

int r=get_r(l, len);

for(int ptr=next_[r],i=j+len;i<=n;i++,ptr=next_[ptr]){

next_[last]=next_[r];
next_[r]=next_[ptr];
next_[ptr]=l;

if(!isvisited(next_)){

push_v(next_);//被访问
if(IDA(d+1))
return true;
v.erase(Atoi(next_));//不要漏掉这一句!!
}
next_[ptr]=next_[r];
next_[r]=next_[last];
next_[last]=l;

}
}
}
return false;
}
void init(){
memset(next_, 0, sizeof next_);
v.clear();
}
int main() {
int T=0;
while(scanf("%d",&n)&&n){
T++;
init();
for(int i=0,j=0;j<n;i=next_[i],j++){
scanf("%d",&next_[i]);
}

for(maxd=0;;maxd++){
v.clear();
push_v(next_);
if(IDA(0)){
printf("Case %d: %d\n",T,maxd);
break;
}
}
}
//print_time_;
return 0;
}

 

  

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: