zoukankan      html  css  js  c++  java
  • 洛谷P1415 拆分数列(dp)

    题目链接:传送门

    题目:

    题目背景
    
    【为了响应党中央勤节俭、反铺张的精神,题目背景描述故事部分略去^-^】
    题目描述
    
    给出一列数字,需要你添加任意多个逗号将其拆成若干个严格递增的数。如果有多组解,则输出使得最后一个数最小的同时,字典序最大的解(即先要满足最后一个数最小;如果有多组解,则使得第一个数尽量大;如果仍有多组解,则使得第二个数尽量大,依次类推……)。
    输入输出格式
    输入格式:
    
    共一行,为初始的数字。
    
    输出格式:
    
    共一行,为拆分之后的数列。每个数之间用逗号分隔。行尾无逗号。
    
    输入输出样例
    输入样例#1: 
    
    [1]
    3456
    [2]
    3546
    [3]
    3526
    [4]
    0001
    [5]
    100000101
    
    输出样例#1: 
    
    [1]
    3,4,5,6
    [2]
    35,46
    [3]
    3,5,26
    [4]
    0001
    [5]
    100,000101
    
    说明
    
    【题目来源】
    
    lzn改编
    
    【数据范围】
    
    对于10%的数据,输入长度<=5
    
    对于30%的数据,输入长度<=15
    
    对于50%的数据,输入长度<=50
    
    对于100%的数据,输入长度<=500
    View Code

      dp练得还是太少了呀。其实复杂dp和大模拟很像,要从每个细节考虑,逐个击破,囫囵吞枣使不得。刚开始我乍一看贪心可用,强行贪心搜索,实际上时间复杂度没考虑,很多细节也没考虑到。。。而且搜索写得还很麻烦,写完了还很多bug,加了一通乱七八糟的特判,最后还是以WA告终。。。

    思路:

    ·第一步:

      根据题意,当然要先找最小的最后一个数。本来想贪心地从后开始搜索,第一个满足条件的就是最小,但是发现遇到0的时候状况千变万化,很难控制:比如:

      100 000 104 123

      搜索过程中会出现以下的情况:

      ① 100000 > 104,不满足

      ② 10000 > 0104,不满足

      ③ 1000 > 00104,不满足

      像这样,连续出现的0会导致每次搜索失败都要回到之前的位置101,而不能直接回到最后一个数123,这就导致这样搜索的时间复杂度是指数级的,不可用。

      但是从后往前找又想不到什么办法记录状态,不能用dp来做,僵硬。。。

      于是想到考虑一个子问题:

      子串被分割成若干个严格递增的数后,使得最后一个数最小时,找到最小的最后一个数

      然后可以惊奇地发现,如果之前的所有子串都计算完了,长度+1之后可以利用之前的结果:

      假设当前位置是i,如果[0, j-1]的子串的最小的最后一个数是a,那么只要a < [j, i]的子串对应的数,那么到位置i为止的最后一个数就可以是[j, i],只要从后往前枚举j,找到的第一个满足条件a < [j, i]的j,对应的[j, i]就是要找的最小的数了,时间复杂度大概是O(n2)。

      不过这还没完。“如果有多组解,使得第一个数尽量大”。。。

    ·第二步:

      我们想要找到最大第一个数,首先就要使第二个数尽量大,然后在这之前要使第三个数尽量大,再在之前就是第四个数尽量大。。。。所以考虑从后往前找,但是直接跑一遍还是会遇到刚开始的搜索的问题(多个零导致指数级时间),比如:

      1234765

      最后一个数最小是765,然后往前贪心的话找到的会是最大的234,但是234被用掉之后第一个只剩下1。而我们可以发现12,34,765也是满足题意的,12显然比1大,不妥QWQ。

      对于每个位置的决策,不仅仅取决于右边相邻的一个位置,还取决于右边的右边,以及右边的右边的右边……的位置。于是还是要回到动态规划上面来。重复利用之前的状态:

      对于每个位置j,子串[j, N]的第一个数会有一个最大值(当然是在第一步割出最小的最后一个数之后),这个最大值可以用于更新在位置j之前的所有位置i的对应子串[i, N]的第一个数的最大值。

    状态&状态转移方程见代码

    时间复杂度是O(n2)

    #include <bits/stdc++.h>
    
    using namespace std;
    const int MAX_N = 505;
    
    string s;
    int a[MAX_N], len;
    int st[MAX_N], ed[MAX_N];
    //st 开始坐标, ed 结束坐标
    
    bool cmp(int st1, int ed1, int st2, int ed2)
    {
        while (st1 <= ed1 && a[st1] == 0)
            st1++;
        while (st2 <= ed2 && a[st2] == 0)
            st2++;
        int len1 = ed1 - st1 + 1;
        int len2 = ed2 - st2 + 1;
        if (len1 < len2)
            return true;
        if (len1 > len2)
            return false;
        for (int i = 0; i < len1; i++) {
            if (a[st1+i] < a[st2+i])
                return true;
            if (a[st1+i] > a[st2+i])
                return false;
        }
        return false;
    }
    
    void dp2()
    {
        memset(ed, 0, sizeof ed);
        ed[st[len]] = len;
        int ind = st[len]-1;
        while (ind >= 1 && !a[ind])
            ed[ind--] = len;
        for (int i = st[len]-1; i >= 1; i--) {
            ed[i] = max(ed[i], i);
            for (int j = st[len]-1; j > i; j--)
                if (cmp(i, j, j+1, ed[j+1])) {
                    ed[i] = max(ed[i], j);
                    break;
                }
        }
    }
    
    void dp()
    {
        st[0] = 0;
        for (int i = 1; i <= len; i++) {
            st[i] = 1;
            for (int j = i; j >= 2; j--)
                if (cmp(st[j-1], j-1, j, i)) {
                    st[i] = j;
                    break;
                }
        }
    }
    
    int main()
    {
        cin >> s;
        len = s.size();
        for (int i = 0; i < len; i++)
            a[i+1] = s[i] - '0';
        dp();
        dp2();
        bool firstprint = true;
        for (int i = 1; i <= len; i++) {
            if (firstprint)
                firstprint = false;
            else
                putchar(',');
            for (int j = i; j <= ed[i]; j++) {
                putchar(s[j-1]);
            }
            i = ed[i];
        }
        cout << endl;
        return 0;
    }
    View Code
  • 相关阅读:
    c#冒泡排序
    C# 虚方法(virtual)覆盖(override) 隐藏(new) 重载
    Javascript 大括号
    C# const.static.readonly.
    热点链接(img map area)
    WeiBo返回错误码的二种方式
    Cookie跨域操作
    synchronized(this)与synchronized(class)
    线程安全场景备忘
    git新建一个分支setupstream
  • 原文地址:https://www.cnblogs.com/Lubixiaosi-Zhaocao/p/9876222.html
Copyright © 2011-2022 走看看