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';
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章