【JZOJ5179】【NOI2017模擬6.29】哈哈

題意

給定一個長度爲n的序列,你能夠進行若干次操做:
選擇一個區間,刪掉,並得到Val[Len]的得分,Len爲這個區間的長度:
其中這個區間知足:
1.相鄰兩個數差的絕對值爲1
2.每一個數都大於相鄰兩個數的平均數
問最大得分。c++

解法

一眼過去,獲得一個多是O(n^4)的作法。
設f[i][j]表示刪除掉[i,j]這個區間的最大得分。
而後發現只有這個轉移不了,而後又多設了一個g[i][j][0/1],表示以a[i]開頭的,以a[j]結尾的上升或降低的最大得分。
而後發現這個好像要多存一個個數,而後很倦生,而後就棄療了。spa

賽後發現,其實標算跟個人作法差很少,多存一維個數能夠用上升序列的性質直接算。
而後打着打着又發現了一個問題。
f[i][j]<-g[i][k]+g[k][j]的合併有點奇怪。
而後水過了以後再看標,恍然大悟。
靠這樣轉移就能夠把兩個g拼接起來了。code

Code

#include<bits/stdc++.h>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;i++)
#define fd(i,x,y) for(int i=x;i>=y;i--)
using namespace std;

const int maxn=407,Inf=0x7fffffff;

int n,Val[maxn],a[maxn];
int Up[maxn][maxn],Dn[maxn][maxn];

void getUpDn(){
    fo(i,1,n){
        Up[i][i]=0;
        fo(j,i+1,n){
            if (Up[i][j-1]) Up[i][j]=Up[i][j-1];
            else if (a[j]==a[j-1]+1) Up[i][j]=0;
            else Up[i][j]=j;
        }
    }
    fo(i,1,n){
        Dn[i][i]=0;
        fo(j,i+1,n){
            if (Dn[i][j-1]) Dn[i][j]=Dn[i][j-1];
            else if (a[j]==a[j-1]-1) Dn[i][j]=0;
            else Dn[i][j]=j;
        }
    }
}
void Init(){
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",&Val[i]);
    fo(i,1,n) scanf("%d",&a[i]);
    getUpDn();
}

int Check(int l,int r,int L=0,int R=0){
    if (L){
        if (r-l+1==0) return Check(L,R);
        else if (R-L+1==0)  return Check(l,r);
        else{
            int x=Up[l][r];
            if (x==0){
                if (a[r]+1==a[L]) return Check(L,R);
                else if (a[r]-1==a[L]) return (Dn[L][R]==0)*2;
                else return 0;
            }else{
                if (Dn[x-1][r]==0 && a[r]-1==a[L]) return (Dn[L][R]==0)*2;
                else return 0;
            }
        }
    }else{
        if (l==r) return 1;
        else{
            int x=Up[l][r];
            if (x==0) return 1;
            else return (Dn[x-1][r]==0)*2;
        }
    }
}
int f[maxn][maxn],g[maxn],h[maxn][maxn][2],Ans=0;
void getF(){
    fo(Len,2,n)
        fo(i,1,n){
            int j=i+Len-1;
            if (j>n) break;
            fo(k,i,j-1){
                if (a[k]+1==a[j]) h[i][j][0]=max(h[i][j][0],h[i][k][0]+f[k+1][j-1]);
                if (a[k]-1==a[j]) h[i][j][1]=max(h[i][j][1],h[i][k][1]+f[k+1][j-1]);
            }
            fo(k,i,j-1){
                f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);
            }
            fo(k,i,j-1)
                if (h[i][k][0]>-1e8 && h[k][j][1]>-1e8)
                            f[i][j]=max(f[i][j],h[i][k][0]+h[k][j][1]+Val[a[k]-a[i]+a[k]-a[j]+1]);
            if (h[i][j][0]>-1e8) f[i][j]=max(f[i][j],h[i][j][0]+Val[a[j]-a[i]+1]);
            if (h[i][j][1]>-1e8) f[i][j]=max(f[i][j],h[i][j][1]+Val[a[i]-a[j]+1]);
        }
}
void getG(){
    g[0]=0;
    fo(i,1,n){
        g[i]=g[i-1];
        fo(j,0,i-1){
            g[i]=max(g[i],g[j]+f[j+1][i]);
        }
    }
}
void Solve(){
    fo(i,1,n) fo(j,1,n) f[i][j]=h[i][j][0]=h[i][j][1]=-1e9;
    fo(i,1,n){
        f[i][i-1]=0;
        fo(j,i,n){
            if (Check(i,j)) f[i][j]=Val[j-i+1];
            else f[i][j]=-1e9;
            if (j>i) h[i][j][0]=h[i][j][1]=-1e9;
            else h[i][j][0]=h[i][j][1]=0;
        }
    }
    getF();
    getG();
    Ans=g[n];
}

void Print(){
    printf("%d\n",Ans);
}

int main(){
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    Init();
    Solve();
    Print();
    return 0;
}
相關文章
相關標籤/搜索