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

题意:你有一篇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}前。

分析: 需要搜索的状态都很直接,就是一个数字序列搜索从初始到有序的最短路,加深搜索的关键就是h函数和模拟操作了,h函数定义很厉害,考虑每一个数字的后继,将后继不正确的数量记起来,然后每一次加深搜索(即剪切黏贴操作)不正确后继变正确最多只有3,因为每一次操作,最多只有三个数的后继发生改变,那h函数考虑最优的情况就是记录不正确的后继数,那就最少还需要h/3次才能有序,则h/3 > max_depth - cur_depth则剪枝,至于模拟操作,用两层for循环依次枚举可以剪切的段,然后枚举未剪切的元素,将剪切的部分依次放到这些元素后面。

#include<bits/stdc++.h>
using namespace std;
int n, a[10];
bool Is_sorted()
{
    for(int i=0; i<n-1; i++)
        if(a[i]>a[i+1])//why have =
            return false;
    return true;
}
int h()
{
    int ret = 0;
    for(int i=0; i<n-1; i++){//!
        if(a[i] + 1 != a[i+1]) ret++;
    }
    if(a[n-1]!=n) ret++;//!
    return ret;
}
int DFS(int depth, int max_depth)
{
    if(depth*3 + h() > 3*max_depth) return false;
    if(Is_sorted()) return true;
    int Old_a[10], Cut_segment[10];
    memcpy(Old_a, a, sizeof(a));
    //!not in here int cnt = 0;
    for(int i=0; i<n; i++){
        for(int j=i; j<n; j++){
            int cnt = 0;
            for(int k=0; k<n; k++)
                if(k<i || k>j) Cut_segment[cnt++] = a[k];//将未剪切部分拼接起来,存到Cut_segment中
            //!not in here int cnt2 = 0;
            for(int k=0; k<=cnt/*!why have = symbol*/; k++){//枚举将剪切下来的部分放到未被剪切的每一个字符的后面
                int cnt2 = 0;
                for(int p=0; p<k; p++) a[cnt2++] = Cut_segment[p];//未剪切部分
                for(int p=i; p<=j; p++) a[cnt2++] = Old_a[p];//剪切部分放入
                for(int p=k; p<cnt/*!*/; p++) a[cnt2++] = Cut_segment[p];//剩下未剪切部分
                if(DFS(depth+1, max_depth)) return true;
                memcpy(a, Old_a, sizeof(a));
            }
        }
    }
    return false;
}
inline int solve()
{
    if(Is_sorted()) return 0;
    int max_depth = 8;
    for(int i=1; i<max_depth; i++){//Is begin 0 or 1? 应该是1,因为代表多少次就能sorted,0的情况就是一开始就是sorted了·
        //Debug printf("%d\n", i);
        if(DFS(0, i)) return i;
    }
    return max_depth;
}
int main(void)
{
    int t = 1;
    while(~scanf("%d", &n) && n){
        for(int i=0; i<n; i++) scanf("%d", &a[i]);
        printf("Case %d: %d\n", t++, solve());
    }
    return 0;
}

瞎理解的IDA*:

这道题的状态很明显,就是自然段的编号,初始即输入,终止即有序,要求最少步数即最短路,但是这里直接使用之间的BFS进行状态空间搜索会爆炸,因为每一次拓展会发现剪切和黏贴的方式有太多,在规定时间内可能连第一层拓展出来的状态都搜索不完,面对这种情况,是时候使用IDA*了,即避免了BFS的空间状态爆炸,也避免了DFS的盲目性,首先IDA*是一种迭代加深搜索,在我的理解里面就是面对每一次搜索,给定一个深度上限,如果超过这个上限便退出搜索增大上限再搜索一次,这里就避免了太多状态的拓展,然后便是关键的h函数,h函数起到的是在搜索过程中,每一次搜索加深,对于局面都能产生影响,我们利用一个h函数来估计从当前深度到最大深度,如果最优的局面都不能解决问题,则剪枝,因此避免了盲目性,但是如何根据深度的加深去估值,这也是一个难点,不同的估值都会产生不同的效率,所以时间复杂度是个O(迷),听说因此在竞赛中出现的频率不高。

时间: 2024-10-29 12:56:36

UVa 11212 Editing a Book (IDA* && 状态空间搜索)的相关文章

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

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

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

[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 11212 Editing a Book

题意: 有一篇由n个自然段组成的文章.希望将他们排成递增序列.只能剪贴和粘贴交替进行,剪贴时可以剪贴一段连续的自然段. 分析: 用IDA*算法求解.当3*d+h>maxd时剪枝. 代码: #include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;string goal;int n;bool dfs(int d,int max

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

Editing a Book UVA - 11212 IDA*

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,

UVa 1343 The Rotation Game (状态空间搜索 &amp;&amp; IDA*)

题意:有个#字型的棋盘,2行2列,一共24个格. 如图:每个格子是1或2或3,一共8个1,8个2,8个3. 有A~H一共8种合法操作,比如A代表把A这一列向上移动一个,最上面的格会补到最下面. 求:使中心8个格子数字一致的最少步骤,要输出具体的操作步骤及最终中心区域的数字.如果有多个解,输出字典序最小的操作步骤. 分析 : 还是状态空间的搜索,对象就是一个数字序列,判断中心位置是否一样,可以看出如果使用BFS,每一层还是爆炸,所以使用IDA*,关键还是模拟操作和h函数,这里的h函数是这样定义的,

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

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

UVa 1252 - Twenty Questions(记忆化搜索,状态压缩dp)

题目链接:uva 1252 题意: 有n个长度为m的二进制串,每个都是不同的. 为了把所有字符串区分开,你可以询问,每次可以问某位上是0还是1. 问最少提问次数,可以把所有字符串区分开来. 思路来源于:点击打开链接 思路: m很小,可以考虑状态压缩. dp[s1][s2]表示询问的状态为s1时,此时能猜到状态包含s2时最小需要的步数. 当询问的几位=s2的二进制串小于2时就能区分出来了,dp[s1][s2]=0: 不能区分则再询问一次,s1|=(1<<k),如果问某位为0,则s2不变,问某位为