HDU-6357Hills And Valleys(用最长可重复公共子序列求最长非递减子序列)

传送门

思路:

对于一个串,如果我们知道了它翻转后最长的非递减子序列,那它去重之后一定是a1<a2<a3……<an 这种形式,ai只能取0~9

。所以我们可以求出原序列最大值, 最小值,构造一个pat匹配串,VL,VL+1,……VH,然后枚举翻转每一个区间(一共是C(10,2)个),和原串做最长公共子序列。在翻转区间的时候要多加上端点值,比如说, 122432445, pat串为12345,如果你直接翻转234,得到pat串为14325,这样匹配的话有两个2,两个4都不能匹配到,多以pat串应该为1243245;

其实求长度是好求的,但是区间就比较繁琐,可以开一个跟dp数组一样大的两个数组L,R,分别记录每个状态区间值,具体方法看一下代码注释比较好理解。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n;
char buf[N], pat[20], VL, VH;
int dp[N][15], cnt, L[N][15], R[N][15], ansl, ansr, ans;

void init(){
    ans=-1;
    VL='9', VH='0';
}

void _reverse(char l, char r, int& ll, int& rr){
    cnt=0;
    for(char k=VL; k<=l; k++)    pat[++cnt]=k;
    ll=cnt+1;
    for(char k=r; k>=l; k--)   pat[++cnt]=k;
    rr=cnt;
    for(char k=r; k<=VH; k++) pat[++cnt]=k;
}

void solve(int l, int r){

    for(int i=0; i<=cnt; i++) dp[0][i]=0;

    for(int i=0; i<=n; i++) for(int j=0; j<=cnt; j++) L[i][j]=R[i][j]=-1;

    for(int i=1; i<=n; i++){
        for(int j=1; j<=cnt; j++){
            dp[i][j]=max(dp[i-1][j]+(buf[i]==pat[j]), dp[i][j-1]);
            /******************************************///找翻转区间
            if(dp[i-1][j]+(buf[i]==pat[j])>=dp[i][j-1]){//如果dp[i][j]由前一项转移而来
                L[i][j]=L[i-1][j]; R[i][j]=R[i-1][j];//它首先应该继承前一个状态的区间,这样能使L尽量的小
                if(buf[i]==pat[j]){
                    if(l==j&&L[i][j]==-1) L[i][j]=i;//如果前一个状态没有L,则L可以赋值
                    if(r==j) R[i][j]=i;//R只要能更新就一直更新,尽量的大
                }
                if(L[i][j]==-1&&L[i][j-1]!=-1){
                    L[i][j]=L[i][j-1]; R[i][j]=R[i][j-1];//这是前后两个值相等的情况如果前面的赋值没有使得L获得一个区间值,可以从(i, j-1)获得
                }
            }
            else if(dp[i-1][j]+(buf[i]==pat[j])<dp[i][j-1]){//如果后者大,直接从后者获得
                L[i][j]=L[i][j-1]; R[i][j]=R[i][j-1];
            }
            /******************************************/
        }
    }

    if(ans<dp[n][cnt] && L[n][cnt]!=-1 && R[n][cnt]!=-1){
        ans=dp[n][cnt];
        ansl=L[n][cnt];
        ansr=R[n][cnt];
    }
}

int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        init();
        scanf("%d", &n);
        scanf("%s", buf+1);

        for(int i=1; i<=n; i++)
            VL=min(buf[i], VL), VH=max(buf[i], VH);
        if(VL==VH){
            printf("%d 1 1\n", n);
            continue;
        }

        for(char i=VL; i<=VH; i++){
            for(char j=i; j<=VH; j++){
                int ll, rr;
                _reverse(i, j, ll, rr);

                solve(ll, rr);
            }
        }
        printf("%d %d %d\n", ans, ansl, ansr);
    }

    return 0;
}

/*
9
122265489
2
12
*/

猜你喜欢

转载自blog.csdn.net/du_lun/article/details/81533355
今日推荐