Problem 802: 遞增數列

Problem 802: 遞增數列
Time Limit: 1000 ms Memory Limit: 262144 KB

Problem Description
給定一個包含n個元素的數組,每次你可以把一個元素+1或-1,操作後的數可以爲負數,零,正數。你需要操作最小次數,使得該數組嚴格遞增。

Input

第一行一個整數n表示數組大小
接下來一行n個數字表示數組元素

40% 1 <= n <= 100, 1 <= ai <= 1000.
100% 1 <= n <= 3000, 1 <= ai <= 1e9.

Output

一個整數,表示答案。

Sample Input
7
2 1 5 11 5 9 11

Sample Output

9

hint
數組會變成如下:
2 3 5 6 7 9 11
|2-2|+|1-3|+|5-5|+|11-6|+|5-7|+|9-9|+|11-11|=9

題解

一開始的時候連暴力都想不出,就有人想出正解了QAQ
後來想到可以dp
dp【i】【j】表示做到第i個數,單調數組最後一個數是j的最小操作次數
然後得到轉移方程(僞代碼)

for(int i=1;i<=n;i++){
        for(int j=mi;j<=ma;j++){//mi表示下界,ma表示上界
            dp[i][j]=INF;
            for(int k=0;k<j;k++)
                dp[i][j]=min(dp[i][j],dp[i-1][k]+(LL)abs(a[i]-j));
        }
    }

發現ma是10^9
而且時間O(n*(ma^2))
空間也開不下

原數組減去標號
a【i】-=i
將問題轉化爲不下降
然後離散化(題解說要去重,但是我覺得如果是不下降的話就可以不去重)
用g【i】【j】表示做到第i個數,單調數組最後一個數是b【j】的最小操作次數

轉移方程

for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            dp[i][j]=g[j]+(LL)abs(a[i]-b[j]);
        for(int j=1;j<=n;j++)g[j]=min(g[j-1],dp[i][j]);
    }

時間O(n^2)

ac代碼 O(n^2)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#define INF 9000000000000000000
#define N 3010
using namespace std;
typedef long long LL;
int a[N],n,b[N];
LL dp[N][N],g[N],ans;
int main(){
    freopen("data.txt","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[i]-=i;
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    g[0]=INF;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            dp[i][j]=g[j]+(LL)abs(a[i]-b[j]);
        for(int j=1;j<=n;j++)g[j]=min(g[j-1],dp[i][j]);
    }
    ans=INF;
    for(int i=1;i<=n;i++)ans=min(ans,dp[n][i]);
    cout<<ans<<'\n';
}

暴力 O(n*(ma^2))

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#define N 3010
#define inf 2147483647
#define INF 9000000000000000000
using namespace std;
typedef long long LL;
int n,a[N],mi,ma;
LL ans,dp[N][N+1010];
int main(){
    freopen("data.txt","r",stdin);
    scanf("%d",&n);
    mi=inf;
    ans=INF;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        ma=max(ma,a[i]);
        mi=min(mi,a[i]);
    }
    ma+=n-1;
    mi=max(0,mi-(n-1));
//  printf("%d %d\n",mi,ma);
    for(int i=1;i<=n;i++){
        for(int j=mi;j<=ma;j++){
            dp[i][j]=INF;
            for(int k=0;k<j;k++)
                dp[i][j]=min(dp[i][j],dp[i-1][k]+(LL)abs(a[i]-j));
        }
    }
//  for(int i=1;i<=n;i++){
//      for(int j=mi;j<=ma;j++)
//          if(dp[i][j]==INF)printf("-1 ");
//          else printf("%d ",dp[i][j]);
//      printf("\n");
//  }
    for(int i=mi;i<=ma;i++)ans=min(ans,dp[n][i]);
    cout<<ans<<'\n';
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章