1. 程式人生 > 其它 >P2501 [HAOI2006]數字序列 題解(dp+構造)

P2501 [HAOI2006]數字序列 題解(dp+構造)

題目連結

題目思路

第一問就是構造\(b[i]=a[i]-i\),然後求最長上升子序列

第二問就是假如\(b[l],b[r]\)滿足,而且中間沒有滿足的情況,那麼就前一段變為\(b[l]\)後一段變為\(b[r]\)

結論還是很顯然的,但是寫起來沒那麼方便,細節++

發現好多省選題目程式碼細節多的一批

程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
//typedef pair<int,int> pii;
#define fi first
#define se second
#define debug printf("aaaaaaaaaaa\n");
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const ll INF=0x3f3f3f3f3f3f3f3f;
int n,a[maxn];
int b[maxn];
ll dp[maxn];
vector<int> vec[maxn];
ll pre[maxn],suf[maxn];
int len;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[i]=a[i]-i;
    }
    a[n+1]=inf-1;
    memset(dp,0x3f,sizeof(dp));
    memset(b,0x3f,sizeof(b));
    for(int i=1;i<=n+1;i++){
        int pos=upper_bound(b+1,b+1+n+1,a[i])-b;
        b[pos]=a[i];
        vec[pos].push_back(i);
        // 以i結尾的最長非嚴格上升子序列
        len=max(len,pos);
    }
    vec[0].push_back(0);
    a[0]=-inf;
    dp[0]=0;
    printf("%d\n",n-(len-1));
    for(int i=1;i<=len;i++){
        for(int j=0;j<vec[i].size();j++){
            int y=vec[i][j];
            for(int k=0;k<vec[i-1].size();k++){
                int x=vec[i-1][k];
                if(x>y) break;
                if(a[x]>a[y]) continue;
                for(int u=x;u<=y;u++){
                    if(u==x) pre[u]=0;
                    else pre[u]=pre[u-1]+abs(a[u]-a[x]);
                }
                suf[y+1]=0;
                for(int u=y;u>=x;u--){
                   suf[u]=suf[u+1]+abs(a[u]-a[y]);
                }
                ll mi=INF;
                for(int u=x;u<=y;u++){
                    mi=min(mi,pre[u]+suf[u+1]);
                }
                dp[y]=min(dp[y],dp[x]+mi);
            }
        }
    }
    printf("%lld\n",dp[n+1]);
    return 0;
}
卷也卷不過,躺又躺不平