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';
}