zoukankan      html  css  js  c++  java
  • poj 1239 increasing sequence (zoj 1499) 分步动态规划

    /*
     * 给定一个长度不大于80的数字序列如 01000102
     * 3456
     * 添加适当的逗号,使得分割的数字严格升序
     * 要求给出使得最后数字最小的分割方法允许001,002,即0开头
     * 如果两个不同分割使得最后的数字大小相同,取第一个数字较大的序列,如果
     * 再相同取第二个数字较大的,如此...
     * 3546
     * 35,46 而不是 3,5,46  3,5,46 的分割index 对应 -1 0 1 4
     * 100000101
     * 100,000101
     *
     * 思路
     * 1.首先不考虑不同序列,相同的最小的最后数字情况,仅仅求出一条使得最后数字最小的分割路径
     *  
     *   动态规划
     *   一条符合条件的序列,如果去掉最后的数字,仍然是符合条件的序列,即子问题仍然符合条件
     *   从前到后依次计算到当前位置的最小序列。每次计算的时候用计算所有可能的最后数字,并利用前面
     *   已经计算好的前面位置的最小序列。
     *
     * 2.处理35,46  3,5,46问题
     *   开始考虑上面求出的最小序列
     *   43432111678
     *   4,34,321,11678
     *   应该是 43,4321,1168
     *   43444543378
     *   4,34,44,54,3378    43, 44, 454, 3378
     *  
     *   本质上思路1只是解决了最后的数字是什么,对于去掉最后数字x的序列,现在要将其划分使得前面的数字尽可能的大,
     *   同时符合递增条件,而且最后数字小于x。
     * 第二步符合逆向动态规划,可对剩下序列从右到左动态规划,因为去掉首数字后的字串仍需要符合最优要求,符合最优子问题。但是需要特别注意的是,步骤1,虽然求得最小的最后数字但是最后一个划分位置不一定确定,因为0的存在,使得083 = 83,这是本题最大的陷阱,考虑第一次dp,后x00,083的划分,需要考虑 x00,083和x0,0083,x,00083 这3种可能的划分情况,尾数分别取083,0083,00083。当前采用的方法是简单的对于所有的可能序列x00,  x0,  x都计算去除尾数的最优分割并比较前面序列取其中最优的,也许有更好的方法。
     * 3.处理特殊情况 000013   00001,3  而不是 0000,1,3 注意000 不划分
     *   即输入数据开头是0的特殊情况, 这个0很别扭,注意这里处理的时候都把开头0的影响去掉如
     *   0003按3处理,它的长度为1
     *4. 实现细节问题,不要试图将字符转换为整型如int,long long,因为80个字符的输入规模,涉及大整数处理了,直接按字符数组处理,写大小比较函数即可。                                                                                                                                                                                              5.   另外,本题目也可以用递归搜索解决,但是代价较大尤其1步骤有很多重复子问题,不过1步骤动态规划找到最小尾数后,第二步可以用递归搜索因为有大量情况可以剪枝,所以时间代价虽然比dp大但是也可以接受。
     *   */

     #include <iostream>

    using namespace std;


    char a[90];         //the initial number string

    //对于 12,34   b[3] = 2  index_last = 1 b[1] = 0

    int index_last;     //求得的最后数字的大小,和分割点标记 index_last = b[strlen(a) - 1] - 1,最后一个数字a[index_last + 1:len_a - 1]

    int path_now[90];

    int path_best[90];

    int cur = 1;

    int cur_best = 1;

    int b[90];     //b[i]存放到i位置为止的序列,按照题目要求最后一个数字的最小值(存前一坐标如2则值为a[2:i])

    int len_a;  //length of char array a



    //return true only when a[s1,e1] < a[s2,e2]

    bool IsLess(int s1, int e1, int s2, int e2)

    {  

        if (s1 < 0)

            return true;

        while(s1 < e1 && a[s1] == '0')  //delete the beginning 0

            s1++;

        while(s2 < e2 && a[s2] == '0')

            s2++;


        int len1 = e1 - s1 + 1;

        int len2 = e2 - s2 + 1;

        if (len1 < len2)

            return true;    //a[s1,e1] < a[s2,e2]

        if (len1 > len2)

            return false;   //a[s1,e1] > a[s2,e2]

        // len1 == len2

        for (int i = 0; i < len1; i++) {

            if (a[s1 + i] < a[s2 + i])  //a[s1,e1] < a[s2,e2]

                return true;

            if (a[s1 + i] > a[s2 + i]) //a[s1,e1] > a[s2,e2]

                return false;

        }

        return false;   //a[s1,e1] == a[s2,e2]

    }


    //return 1  a[s1,e1] < a[s2,e2]

    //return 0  a[s1,e1] > a[s2,e2]

    //return -1 a[s1,e1] == a[s2,e2]

    int cmp(int s1, int e1, int s2, int e2)

    {

        if (s1 == s2 && e1 == e2)

            return -1;


        while(s1 < e1 && a[s1] == '0')  //delete the beginning 0

            s1++;

        while(s2 < e2 && a[s2] == '0')

            s2++;


        int len1 = e1 - s1 + 1;

        int len2 = e2 - s2 + 1;

        if (len1 < len2)

            return 1;    //a[s1,e1] < a[s2,e2]

        if (len1 > len2)

            return 0;   //a[s1,e1] > a[s2,e2]


        for (int i = 0; i < len1; i++) {

            if (a[s1 + i] < a[s2 + i])  //a[s1,e1] < a[s2,e2]

                return 1;

            if (a[s1 + i] > a[s2 + i]) //a[s1,e1] > a[s2,e2]

                return 0;

        }

        return -1;   //a[s1,e1] == a[s2,e2]

    }


    //找到最后一个数字,其数字大小存储在b[strlen(a) - 1]中

    //what we store is b[] and index_last

    void FindLast() 

    {

        int start = 0;

        while (a[start] == '0')     //处理起始数字开头为0的情况,deal with 00001 treat as 1 ignore the starting 0 

            start++;                

        

        for (int i = start; i < len_a; i++) {

            int j;

            for (j = i; j > start; j--) {   //对于每个位置i,求它的可能的最小的最后数字

                if (IsLess(b[j - 1], j - 1, j, i)) { // if a[ b[j - 1] : j - 1]  < a[j: i]

                    b[i] = j;

                    break;

                }

            }

            if (j == start) {

                b[i] = start;

            }


            if (i == (len_a - 1)) {

                if (start != 0 && j == start)   //00001

                    index_last = -1;

                else

                    index_last = j - 1;

                return;

            }

        }

        index_last = -1;    //like 0000 do not go into for loop

    }


    void PrintData(int start, int end);

    //执行后将会得到前面的分割点坐标,并且cur指向下一个分割位置

    //在已知最后最小尾数的情况下,递归搜索解法求解前面的分割,结合剪枝

    bool FindSequence(int start, int end, int lstart, int lend, int path[])

    {

        while (start < end && a[start] == '0')     //这个很重要不能少,有了这个预处理则长度大的就大,否则如0003与4影响判断

            start++;

        

        if (IsLess(start, end, index_last + 1, len_a - 1) && 

            IsLess(lstart, lend, start, end)) {

            return true;

        }

        

        for (int j = (start + (end + 1)) / 2; j > start; j--) {

        //for (int j = end; j > start; j--) {

            if (!IsLess(start, j - 1, index_last + 1, len_a - 1)) 

                continue;

            path[cur++] = j - 1;

            if (IsLess(lstart, lend, start, j - 1) && FindSequence(j , end, start, j - 1, path))

                return true;

            --cur;

        }

        return false;   //will always find so will never go here

    }



    //作用同上,反向dp解法,求去掉最小尾数的前面串的最优分割

    //返回path的尾座标

    int FindSequence(int start, int end, int path[]) 

    {

        int c[90];      //反向dp时记录值,存从右向左到当前的首数字最大值,记录例如c[5] = 7  意味者从5-end的最优序列的首元素为a[5:7]

        

        for (int i = end; i >= start; i--) {    //对于i:end找一个可能的最大首数字

            if (a[i] == '0' && i != end) {      //0可以与前面的合并

                c[i] = c[i + 1];

                continue;

            } 

            if (IsLess(i, end, index_last + 1, len_a -1)) {     //结束点,如果当前串整个长度数字仍然小于原串尾数

                c[i] = end;

                continue;

            } 

            for (int j = end - 1; j >= i; j--) {             //动态规划,试探找最大的首数字,子问题也是最优被利用 

                if (IsLess(i, j, j + 1, c[j + 1])) {

                    c[i] = j;

                    break;      //ok we have find it ,need to break ,do not forget

                }

            }

        }


        //now we will finsh the path 1,23,45,67

        //path -1,0,2,4

        path[0] = -1;

        int i = start;  //actually start will always be 0 here

        int j = 1;

        while (i <= end) {

            path[j++] = c[i];

            i = c[i] + 1;

        }

        return j - 1;  //since j++ need to - 1

    }


    bool IsBetterPath() {

        for (int i = 0; i < cur && i < cur_best; i++) {

            int status = cmp(path_now[i] + 1, path_now[i + 1], path_best[i] + 1, path_best[i + 1]);

            if (status == 0)     // >

                return true;

            else if (status == 1) // <

                return false;

        }

        return false; //will not go to here since two path will be different

    }


    void CopyPath() {

        for (int i = 0; i <= cur; i++) {

            path_best[i] = path_now[i];

        }

    }


    /* print result */

    void PrintData(int start, int end) 

    {

        for (int i = start; i<= end; i++)

            cout << a[i];

    }

    void PrintResult() 

    {

        if (index_last == -1) {   //也就是说只有一个数字如 001  或者 21

            cout << a << endl;

            return;

        }


        for (int i = 0; i < cur_best; i++) {

            PrintData(path_best[i] + 1, path_best[i + 1]);

            cout << ",";

        }

        PrintData(index_last + 1, len_a - 1);  //index_last + 1 , len_a - 1

        cout << endl;

    }




    void FinishPath(int path[], int index) {

        path[0] = -1;

        path[cur] = index;

    }




    void CalcPath( ) {

        

        len_a = strlen(a);


        index_last = -1;

        cur = 1;    //由于是全局变量,在多次使用时要小心前面的影响 记录path的尾座标

        cur_best = 1;

        //anlysis string to find last minimal num

        FindLast();

        

        //find the sequnece before the last minimal num

        FinishPath(path_best, index_last);  //1,21 need this

        if (index_last >= 1) {

            //FindSequence(0, index_last, -1, -1, path_best);   //find by rec

            //cur_best = cur;

            //FinishPath(path_best, index_last);

            cur_best = FindSequence(0, index_last, path_best);     //find by dp

            int tmp_index = index_last;

            //由于最后一个数字有可能前面带0,如取83但是前面有0,要考虑到最后数字改取083的情况

            //当前的做法是考虑最后数字的所有可能,再此基础上求得前面的path取最优的path,同时

            //也就最终确定了最后数字的分割位置index_last可能会向前移动

            while (a[tmp_index] == '0') {

                if (IsLess(b[tmp_index - 1], tmp_index - 1, index_last + 1, len_a - 1)) {

                    //cur = 1;

                    //FindSequence(0, tmp_index - 1, -1, -1, path_now);   

                    //FinishPath(path_now, tmp_index - 1);           

                    cur = FindSequence(0, tmp_index - 1, path_now);

                    if (IsBetterPath()) {

                        CopyPath();

                        cur_best = cur;

                        index_last = tmp_index - 1;

                    }

                }

                tmp_index--;

            }

        }

    }


    int main(int argc, char *argv[])

    {

        while (1) {

            cin >> a;

            

            if (a[0] == '0' && a[1] == '\0')

               break; 

            

            CalcPath();

            

            PrintResult();


        } 

        return 0;

    }

  • 相关阅读:
    网页中嵌入百度地图
    第一篇博客, 感谢博客园
    Flask-Migrate插件
    基于airtest-selenium的UI自动化测试
    自动化测试中如何解决图片验证码问题
    Linux下搭建自动化测试环境
    python3中使用subprocess模块执行外部命令
    微信小程序自动化实战(一)
    python3中使用objectpath模块处理Json对象
    Pytest中如何解决测试用例的依赖执行问题
  • 原文地址:https://www.cnblogs.com/rocketfan/p/1519555.html
Copyright © 2011-2022 走看看