给出一个 1 ∼ N 的序列 A ( A 1 , A 2 , ..., A N ) 。你每次可以将两个相邻的元素合并,合并后的元素权值即为 这两个元素的权值之和。求将 A 变为一个非降序列,最少需要多少步操作。
Input
输入的第一行一个整数 N ( N ≤ 5000) 。
接下来一行 N 个整数,描述序列 A 。保证序列 A 中的每个元素的值不超过 1000 。
Output
输出一行一个整数,表示最少的操作数。
题解:
区间dp真的难,(。・∀・)ノ゙嗨!
好了,设dp[i][j]表示从区间把前j个数,把区间i~j合并后满足单调的元素数量,我们可以考虑,统计一个前缀和sum,然后枚举
三个端点i,j,k,我们是想把i~j压缩成一个数,然后k在i之前,找到元素数量最少的,进行转移,转移条件 是sum[i]-sum[k-1]<=sum[j]-sum[i]
代码:
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<algorithm> #include<cstring> const int MAXN=5100; using namespace std; int dp[MAXN][MAXN],sum[MAXN],ans=-1; void cl(){ memset(dp,0,sizeof(dp)); memset(sum,0,sizeof(sum)); } int main(){ cl(); int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&sum[i]); sum[i]+=sum[i-1]; } for(int i=1;i<=n;i++){ dp[i][1]=1; int k=i,res=-1; for(int j=i+1;j<=n;j++){ while(k&&sum[i]-sum[k-1]<=sum[j]-sum[i]) res=max(res,dp[i][k--]); if(res==-1) dp[j][i+1]=-1; else dp[j][i+1]=max(dp[j][i+1],res+1); //printf("i=%d j=%d k=%d ",i,j,k); } } for(int i=1;i<=n;i++) ans=max(ans,dp[n][i]); if(ans==-1) printf("-1"); else printf("%d",n-ans); }