2018多校第五场hdu 6357 Hills And Valleys(动态规划)

版权声明:欢迎大佬指正! https://blog.csdn.net/sinat_36215255/article/details/81478684

题目网址http://acm.hdu.edu.cn/showproblem.php?pid=6357

题意: 将一个数字序列中的一段区间翻转,求翻转后的最长非递减序的子串长度,和翻转区间的左右边界。

 自己思考了一下,结合了一个简单一点的cf 934C题 题解https://blog.csdn.net/mitsuha_/article/details/79326891

再加上dls的讲解和题解,终于搞懂了这个题目。

题解是这样给的:

加上dls讲解,

对于一个数组,例如 1  2   4  3  3  2   2  5  1  如果我们想得到一个最长序列,其序列其实是一个  1 2 【4 3 2 】5 类似于这种的序列(此处将重复数字去掉)将【】区间翻转即可,但是由于 n = 1e5我们没办法枚举 l r,但是我们可以转换一下思路。对于所有的序列,其实去掉重复部分,其最终形态,是类似于题解中 0 -x-y -9那一长串的的样子。

那我们可以不再枚举l r,进而去枚举所有的x y 值域。

那么我举的例子的答案序列,他所对应的答案x =2  y = 4,序列为 1  2 4 3 2 (4 ) 5 ,(4)是原串中不存在的,我们不统计,但是我们可以由此看出规律:

那就是找到枚举所有的可取 [x ,y] ,然后生成一个匹配串PAT,  0 ,1 , 2, ... ,x  ,[  y, y-1, y-2, ...  ,x] , y,y+1,..., 9 我们从数组An中去匹配这个串,最大匹配长度就是 最长非递减序的子串长度,[ ]位置对应翻转位置。

在匹配时,dp【i】【j】对应到A数组的 i 位,PAT 的 j 位时当前的最大长度。

所有我们可以看出 dp【i】【j】 = max(dp【i】【j-1】,dp【i-1】【j】+(A[i]==pat[j]? 1 : 0));

更多的细节看代码:

       ps:标程的代码真真是骨骼清奇

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = (int)1e5 + 1, maxv = 13;
int t, n, tot, g[maxn][maxv], *f = g[0];
char buf[maxn], pat[maxv];

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%s", &n, buf);
        char vL = '9', vR = '0'; //最大数 和 最小数
        for(int i = 0; i < n; ++i)
        {
            vL = min(vL, buf[i]);
            vR = max(vR, buf[i]);
        }
        if(vL == vR)
        {
            printf("%d %d %d\n", n, 1, 1);
            continue;
        }
        int m = 0;
        char pL, pR;
        for(char low = vL; low < vR; ++low) //枚举数值,0~9
            for(char upp = low + 1; upp <= vR; ++upp)
            {
                tot = 0;
                for(char ch = vL; ch <= low; ++ch) // vl —— low
                    pat[tot++] = ch;
                for(char ch = upp; ch >= low; --ch) // up - low
                    pat[tot++] = ch;
                for(char ch = upp; ch <= vR; ++ch) // up =  vr
                    pat[tot++] = ch;
                memset(f, 0, tot * sizeof(int)); // pat 对应一个 vl - low  , up - low  ,up - vr 的一个序列,显然,up-low是翻转区域
                for(int i = 0; i < n; ++i)
                    for(int j = 0; j < tot; ++j)  //dp[i][j] = max(dp[i-1][j]+ buf[i]==pat[j],dp[i-1][j-1]) ,
                    {
                        f[j] += buf[i] == pat[j];
                        if(j && f[j] <f[j-1])
                            f[j] = f[j-1];
                    }
                if(m < f[tot - 1]) //更新最长区域对应的翻转字符,
                {
                    m = f[tot - 1];
                    pL = low;
                    pR = upp;
                }
            }
        tot = 0;
        //ans 对应的pat
        for(char ch = vL; ch <= pL; ++ch)
            pat[tot++] = ch;
        for(char ch = pR; ch >= pL; --ch)
            pat[tot++] = ch;
        for(char ch = pR; ch <= vR; ++ch)
            pat[tot++] = ch;
        memset(g[0], 0, tot * sizeof(int));//找到最长区域对应的g
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j < tot; ++j)
            {
                g[i+1][j] = g[i][j] + (buf[i] == pat[j]);
                if(j && g[i+1][j] <g[i+1][j-1])   g[i+1][j] =g[i+1][j-1];

            }
        }
        //寻找翻转区间
        int L = 0, R = 0;
        for(int i = n - 1, j = tot - 1; i >= 0; --i)
        {
            int lft,rht;
            lft = g[i][j] + (buf[i] == pat[j]);
            if(j!=0)  rht = g[i+1][j-1] ;
            else    rht = -1;
            //当前g[i+1][j]  的值,要么从lft转移来,要么从rft转移而来
            //如果是从g[i+1][j-1]转移而来的话,需要循环改变j的值
            while(lft < rht)
            {
                // 最长区域为 vl - low [up - low] up - vr 由此可见翻转区域[]的出现位置为low up 相接处
                //第一次出现时,i为R,第二次出现时i 为 L-1,因为j对应的是i+1
                if(pat[j - 1] == pL && pat[j] == pR)
                {
                    if(!R)
                        R = i;
                    else
                        L = i + 1;
                }
                --j;
                lft = g[i][j] + (buf[i] == pat[j]);
                if(j!=0) rht = g[i+1][j-1] ;
                else    rht = -1;
            }
        }
        printf("%d %d %d\n", m, L + 1, R + 1);
    }
    return 0;
}

好饿啊,,,

猜你喜欢

转载自blog.csdn.net/sinat_36215255/article/details/81478684