Hills And Valleys 杭电多校第五场

 

题意:长度为n的序列,有一次翻转区间的机会,问最长不减序列

 

题解:如果没有翻转区间的机会,有两个做法。

          一是dp[i]表示以i结尾的最长序列 dp[i]=max(dp[i],dp[j]+1)  (j<=i)。

          二是那个抽牌替换的解法。

          这道题可以翻转但是值域很小,所以考虑最长子序列和值域的关系。

          选择第一种解法改进。

          显然不翻转的话是序列A与 序列B ={0123456789} 来匹配,B中的元素可以被匹配到多次。

          现在要求翻转一次后的最长子序列,直接翻转A的复杂度是C(n,2)*n*10。

          考虑有效翻转的意义,一定是将(只有)一个递减的序列变为递增。

          这就相当于在被匹配的B序列中插入一个递减序列来被A匹配。

          比如A是12345564678,直接匹配的对应的B'序列是12345(64)678,也就是B中多加了一个递减序列。

          所以可以不翻转A,翻转B,这样复杂度就将为C(10,2)*n*20。

 

          实现问题的话,可以在第二位数值域上多加10个来记录要添加的递减序列长度。

          关于记录位置,因为只需考虑值域,所以只需开两个L[20],R[20]数组来记录以数字i结尾的(每个数分递减递增)左边和右边翻转区域即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+7;
int dp[N][22],n,T,len[22],L[22],R[22];
int ans,l,r;
char s[N];
void solve(int ll,int rr){
    int tar;
    for(int i=1;i<=n;++i) for(int j=0;j<20;++j) dp[i][j]=0;
    for(int i=0;i<20;++i) len[i]=0;
    for(int i=0;i<20;++i) L[i]=R[i]=0;
    for(int i=1;i<=n;++i) {
        tar=s[i]-'0';
        for(int j=tar;j>=0;--j) {
            if(dp[i][tar]<len[j]+1) {
                dp[i][tar]=len[j]+1;
                if(L[j]) L[tar]=L[j];else L[tar]=i;
                if(R[j]) R[tar]=R[j];else R[tar]=i;
            }
        }
        if(ll<=tar&&tar<=rr) {
            for(int j=tar+10;j<=10+rr;++j) {
                if(dp[i][tar+10]<len[j]+1) {
                     dp[i][tar+10]=len[j]+1;
                     if(!L[j]) L[tar+10]=i;else L[tar+10]=L[j];
                     R[tar+10]=i;
                }
            }
            for(int j=0;j<=ll;++j) if(dp[i][tar+10]<len[j]+1){
                dp[i][tar+10]=len[j]+1;
                L[tar+10]=R[tar+10]=i;
            }
        }
        if(tar>=rr) {
            for(int j=10+ll;j<=10+rr;++j) {
                if(dp[i][tar]<len[j]+1) {
                    dp[i][tar]=len[j]+1;
                    L[tar]=L[j],R[tar]=R[j];
                }
            } 
        }
        if(dp[i][tar]>ans) ans=dp[i][tar],l=L[tar],r=R[tar];
        if(ll<=tar&&tar<=rr&&ans<dp[i][tar+10]) ans=dp[i][tar+10],l=L[tar+10],r=R[tar+10];
        for(int j=0;j<20;++j) len[j]=max(len[j],dp[i][j]);
    }
}
int main(){
    for(scanf("%d",&T);T--;){
        scanf("%d",&n);
        scanf("%s",s+1);
        ans=0;
        l=r=1;
        for(int i=0;i<9;++i) for(int j=i+1;j<10;++j) solve(i,j);
        printf("%d %d %d\n",ans,l,r);
    }
}

 

posted @ 2018-08-07 17:11  Billyshuai  阅读(261)  评论(0编辑  收藏  举报