【神奇的dp】poj 6357 Hills And Valleys

【神奇的dp】poj 6357 Hills And Valleys

【题目链接】


题目

Problem Description

Tauren has an integer sequence A of length n (1-based). He wants you to invert an interval [l,r] (1≤l≤r≤n) of A (i.e. replace Al,Al+1,⋯,Ar with Ar,Ar−1,⋯,Al) to maximize the length of the longest non-decreasing subsequence of A. Find that maximal length and any inverting way to accomplish that mission.
A non-decreasing subsequence of A with length m could be represented as Ax1,Ax2,⋯,Axm with 1 ≤ x1 < x2 < ⋯ < xm ≤ n and Ax1 ≤ Ax2 ≤ ⋯ ≤ Axm.

Input
The first line contains one integer T, indicating the number of test cases.
The following lines describe all the test cases. For each test case:
The first line contains one integer n.
The second line contains n integers A1,A2,⋯,An without any space.
1≤T≤100, 1≤n≤105, 0≤Ai≤9 (i=1,2,⋯,n).
It is guaranteed that the sum of n in all test cases does not exceed 2⋅105.

Output
For each test case, print three space-separated integers m,l and r in one line, where m indicates the maximal length and [l,r] indicates the relevant interval to invert.

Sample Input

2
9
864852302
9
203258468

Sample Output

5 1 8
6 1 2

Hint

In the first example, 864852302 after inverting [1, 8] is 032584682, one of the longest non-decreasing subsequences of which is 03588.
In the second example, 203258468 after inverting [1, 2] is 023258468, one of the longest non-decreasing subsequences of which is 023588.


题目大意和解题思路

T组数据,每组给一个长为n[1,1e5]的串,串里是[0,9],问翻转其中某一段后能达到的最长不减子序列有多长,输出长度和翻转串的左右下标。

比赛的时候没多想,只看出肯定要转成几个0+几个1+几个2+……+几个9,区间可以转化为看它值域为[0,9][1,3]之类的递减序列有多长,复杂度肯定是过不了的。。。

看直播讲题的时候一直在想大佬的按值域枚举区间是什么玩意儿,还有枚举完和原串匹配,其实就是把所有连续相同的点全都看成一个以后,最后的递增子序列肯定是0123456789的子序列,这就是所谓的值域,找递增子序列的过程就相当于用这个串和原串匹配。
这个枚举就是比如翻转值域[2,5],它对应的值域是012 5432 56789,用原串和这个串匹配得到对应的最长序列,一共45种,比较一下就可以了。找翻转区间的过程类似找最长公共子序列的路径,就是路径上纵坐标对应翻转了的值域的那一部分。


AC代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=1e5+7;

int a[maxn],b[maxn];
char s[maxn];
int dp[maxn][23];
int dir[maxn][23];///0代表来自i-1,1代表来自j-1,-1代表来自i-1并且+了1
int main()
{
    int T,m,n,i,j,x,y,cnt,maxx,l,r,ans;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        scanf("%s",s+1);
        for(i=1;i<=n;i++) a[i]=s[i]-'0';
        ans=0;
        for(l=0;l<=9;l++)
        {
            for(r=l+1;r<=9;r++)
            {
                cnt=0;
                for(i=0;i<=l;i++) b[cnt++]=i;
                for(i=r;i>=l;i--) b[cnt++]=i;
                for(i=r;i<=9;i++) b[cnt++]=i;
                for(i=1;i<=n;i++)
                {
                    for(j=0;j<cnt;j++)
                    {
                        dp[i][j]=dp[i-1][j];
                        dir[i][j]=0;
                        if(a[i]==b[j])
                        {
                            dp[i][j]++;
                            dir[i][j]=-1;
                        }
                        if(dp[i][j]<dp[i][j-1])
                        {
                            dir[i][j]=1;
                            dp[i][j]=max(dp[i][j],dp[i][j-1]);

                        }

                    }
                }
                if(ans<dp[n][cnt-1])
                {
                    ans=dp[n][cnt-1];
                    i=n,j=cnt-1;
                    x=y=1;
                    cnt=1;
                    bool flag=1;
                    while(i>=1&&j>0)
                    {

                        if(j>=l+1&&dir[i][j]==-1) x=i;
                        if(j<=r+1&&flag&&dir[i][j]==-1) flag=0,y=i;
                        if(dir[i][j]==1) j--;
                        else i--;
                    }
                }
            }
        }
        printf("%d %d %d\n",ans,x,y);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值