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

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

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

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <set>
  5 #include <algorithm>
  6 #include <ctime>
  7 using namespace std;
  8
  9 #define print_time_ printf("time : %f\n",double(clock())/CLOCKS_PER_SEC)
 10 const int maxn=9;
 11 set<int> v;//存储状态
 12 int next_[maxn+2];//用链表存储当前序列
 13 int n;
 14 int maxd;
 15
 16 inline int Atoi(int *next){ //将当前序列转换成十进制数
 17     int ans=0;
 18     for(int i=next[0],j=0;j<n;j++,i=next[i])
 19         ans=ans*10+i;
 20     return ans;
 21 }
 22 inline bool isvisited(int *A){//判重
 23     return v.count(Atoi(A));
 24 }
 25 inline void push_v(int *A){
 26     v.insert(Atoi(A));
 27 }
 28 int h(int *next){//获得当前状态下的错数
 29     int h=0;
 30     for(int i=next[0],j=1;j<=n;j++,i=next[i]){
 31         if(j<n){
 32             if(i==n||(i!=n&&next[i]!=i+1))
 33                 h++;
 34         }
 35         else if(i!=n)
 36             h++;
 37     }
 38     return h;
 39 }
 40 int get_r(int& l,int& len){ //获得被剪切段的最右端
 41     int r=l;
 42     for(int i=0;i<len-1;i++)
 43         r=next_[r];
 44     return r;
 45 }
 46 bool IDA(int d){
 47     if(d==maxd){
 48         if(h(next_)==0)
 49             return true;
 50         else return false;
 51     }
 52     int h_=h(next_);
 53     if(h_>3*(maxd-d))
 54         return false;
 55     for(int len=1;len<n;len++){
 56         for(int last=0,l=next_[0],j=1;j+len-1<=n;j++,last=l,l=next_[l]){
 57
 58             int r=get_r(l, len);
 59
 60             for(int ptr=next_[r],i=j+len;i<=n;i++,ptr=next_[ptr]){
 61
 62                 next_[last]=next_[r];
 63                 next_[r]=next_[ptr];
 64                 next_[ptr]=l;
 65
 66                 if(!isvisited(next_)){
 67
 68                     push_v(next_);//被访问
 69                     if(IDA(d+1))
 70                         return true;
 71                     v.erase(Atoi(next_));//不要漏掉这一句!!
 72                 }
 73                 next_[ptr]=next_[r];
 74                 next_[r]=next_[last];
 75                 next_[last]=l;
 76
 77
 78             }
 79         }
 80     }
 81     return false;
 82 }
 83 void init(){
 84     memset(next_, 0, sizeof next_);
 85     v.clear();
 86 }
 87 int main() {
 88     int T=0;
 89     while(scanf("%d",&n)&&n){
 90         T++;
 91         init();
 92         for(int i=0,j=0;j<n;i=next_[i],j++){
 93             scanf("%d",&next_[i]);
 94         }
 95
 96         for(maxd=0;;maxd++){
 97             v.clear();
 98             push_v(next_);
 99             if(IDA(0)){
100                 printf("Case %d: %d\n",T,maxd);
101                 break;
102             }
103         }
104     }
105     //print_time_;
106     return 0;
107 }

  

时间: 2024-10-12 09:57:24

UVA 11212 Editing a Book [迭代加深搜索IDA*]的相关文章

uva 11212 - Editing a Book(迭代加深搜索 IDA*) 迭代加深搜索

迭代加深搜索 自己看的时候第一遍更本就看不懂..是很水,但智商捉急也是没有办法的事情. 好在有几个同学已经是做过了这道题并且对迭代加深搜索的思路有了一定的了解,所以在某些不理解的地方询问了一下他们的见解, 真的是很有帮助,也许自己想要想很久才能想明白,还会很痛苦,稍微问一下别人的想法,点上一个方向,剩下的自己就能想得明白了. 迭代加深. 把answer(需要的步数或其他)在主函数里面从零往上递加,此之谓 "层数",亦可谓之"深度".用书上的话就是: 从小到大枚举深度

Uva 11212 编辑书稿(迭代加深搜索)

题意: 给定N个数的序列, 希望将它排列成1~N, 可以用剪切.粘贴来完成任务, 每次可以剪切一段连续的自然段, 粘贴时按照顺序粘贴. #include <bits/stdc++.h> #define rep(i,a,b) for(int i = a; i < b; i++) #define _rep(i,a,b) for(int i = a; i <= b; i++) using namespace std; const int maxn = 12; int n, maxd; i

UVA 1343 - The Rotation Game-[IDA*迭代加深搜索]

解题思路: 这是紫书上的一道题,一开始笔者按照书上的思路采用状态空间搜索,想了很多办法优化可是仍然超时,时间消耗大的原因是主要是: 1)状态转移代价很大,一次需要向八个方向寻找: 2)哈希表更新频繁: 3)采用广度优先搜索结点数越来越多,耗时过大: 经过简单计算,最长大概10次左右的变换就能出解,于是笔者就尝试采用IDA*,迭代加深搜索的好处是: 1)无需存储状态,节约时间和空间: 2)深度优先搜索查找的结点数少: 3)递归方便剪枝: 代码如下: 1 #include <iostream> 2

UVa 1374 - Power Calculus——[迭代加深搜索、快速幂]

解题思路: 这是一道以快速幂计算为原理的题,实际上也属于求最短路径的题目类型.那么我们可以以当前求出的幂的集合为状态,采用IDA*方法即可求解.问题的关键在于如何剪枝效率更高.笔者采用的剪枝方法是: 1)如果当前状态幂集合中的最大元素max满足 max*2^(maxd-cur_d)<n,则剪枝.原因是:在每一次状态转移后,max最多增大一倍.(maxd-cur_d)次转移之后,max最多变成原来的2^(maxd-cur_d)倍,然而如果当前状态的极限情况下仍有max<n,则当前状态结点一定无法

UVa 11212 Editing a Book (IDA* &amp;&amp; 状态空间搜索)

题意:你有一篇n(2≤n≤9)个自然段组成的文章,希望将它们排列成1,2,-,n.可以用Ctrl+X(剪切)和Ctrl+V(粘贴)快捷键来完成任务.每次可以剪切一段连续的自然段,粘贴时按照顺序粘贴.注意,剪贴板只有一个,所以不能连续剪切两次,只能剪切和粘贴交替.例如,为了将{2,4,1,5,3,6}变为升序,可以剪切1将其放到2前,然后剪切3将其放到4前.再如,排列{3,4,5,1,2},只需一次剪切和一次粘贴即可--将{3,4,5}放在{1,2}后,或者将{1,2}放在{3,4,5}前. 分析

Power Calculus UVA - 1374 迭代加深搜索

迭代加深搜索经典题目,好久不做迭代加深搜索题目,拿来复习了,我们直接对当前深度进行搜索,注意剪枝,还有数组要适当开大,因为2^maxd可能很大 题目:题目链接 AC代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cmath> 5 #include <algorithm> 6 #include <cstring> 7 #incl

[2016-02-27][UVA][11212][Editing a Book]

[2016-02-27][UVA][11212][Editing a Book] 时间:2016-02-26 19:38:44 星期五 题目编号:UVA 11212 题目大意:给定长度为n(值为1~n)的序列,求把该序列 复制/粘贴 成1~n 的排列最少步数 分析: 状态空间搜索,但是每次状态转移的方式有多种,可能会T, 发现,最多 操作 的次数:n~1的序列,每次操作 长度为1的连续段,那么需要 n-1 次操作 可以用IDA*算法 剪枝,每次操作之后,每个数字的后继数字不正确的数目h,最多减少

UVA-11214 Guarding the Chessboard (迭代加深搜索)

题目大意:在一个国际象棋盘上放置皇后,使得目标全部被占领,求最少的皇后个数. 题目分析:迭代加深搜索,否则超时. 小技巧:用vis[0][r].vis[1][c].vis[2][r+c].vis[c-r+N]分别标志(r,c)位置相对应的行.列.主.副对角线有没有被占领(详见<入门经典(第2版)>P193),其中N表示任意一个比行数和列数都大(大于等于)的数. 代码如下: # include<iostream> # include<cstdio> # include&l

USACO/fence8 迭代加深搜索+剪枝

题目链接 迭代加深搜索思想. 枚举答案K,考虑到能否切出K个木头,那么我们当然选最小的K个来切. 1.对于原材料,我们是首选最大的还是最小的?显然,首选大的能够更容易切出,也更容易得到答案. 2.对于目标木头,我们是优先得到最大的还是最小的?显然,由于K个木头我们都要得到,那么当然先把最大的(最难得到的)先得到,这种搜索策略更优. 3.假设总原材料为all,前K个木头总和为sum,那么all-sum就是这一次切割过程中能[浪费]的最大数目.对于一个切剩下的原材料,若它比最小的目标木头还要小,则它