zoukankan      html  css  js  c++  java
  • UVa 11212 Editing a Book (IDA* && 状态空间搜索)

    题意:你有一篇n(2n9)个自然段组成的文章,希望将它们排列成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
    ", 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
    ", t++, solve());
        }
        return 0;
    }
    View Code

    瞎理解的IDA*:

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

  • 相关阅读:
    Casting
    hdu 1164 Eddy's research I
    hdu 1212 Big Number
    CF271 C. Secret
    hdu 1065 I Think I Need a Houseboat
    单档——状态码显示设置,状态码更改链表更新
    单身金额统计,更新显示到单头
    开窗设计器——条件,参数,返回值接收显示
    单档——单头内容新增、修改后同步更新到其他相关数据表
    TIPTOP之分割split函数方法、getIndexOf、subString、replace、临时表创建;
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/7199629.html
Copyright © 2011-2022 走看看