题目背景
loidc来到了NOI的赛场上,他在那里看到了好多神犇。
题目描述
神犇们现在正排成一排在刷题。每个神犇都有一个能力值p[i]。loidc认为坐在附近的金牌爷能力参差不齐非常难受。于是loidc便想方设法对神犇们进行人道主义合并。
loidc想把神犇的能力值排列成从左到右单调不减。他每次可以选择一个神犇,把他合并到两侧相邻的神犇上。合并后的新神犇能力值是以前两位犇的能力值之和。每次合并完成后,被合并的两个神犇就会消失。合并后的新神犇不能再分开(万一他俩有女朋友咋办)因此每次合并后神犇的总数会减1.
loidc想知道,想治好他的强迫症需要合并多少次
输入输出格式
输入格式:第一行一个整数 n。
第二行 n 个整数,第 i 个整数表示 p[i]。
输出格式:loidc需要合并的次数
输入输出样例
输入样例#1:
8
1 9 9 4 1 2 2 9
输出样例#1:
3
说明
对于 50%的数据,0< n <=5000。
对于 100%的数据,0< n <=200000,0< p[i] <=2147483647,p 均为随机生成。
Solution:
好久没写dp了啊,于是写一发dp。
本题要使一段序列合并成不下降序列,因为只能合并相邻的两个数,所以合并后的一个数必定是由原版序列中的一段进行数次合并得到的。考虑简单的贪心思路,对于第一个数,每次不停加入一个数直到它们的和大于第一个数停止,继续此操作,直到结束,但是这样显然是错误的,因为前面满足了条件不一定后面会最优(很简单思考懒的举例了)。由贪心思路引申到dp,因为是一段合并,考虑到前缀和sum[i],我们令f[i]表示到了第i个数为止所合并的次数,用一个辅助数组maxp[i]表示到了i为止合并后最大的一个数,于是得到状态转移方程:if(sum[i]-sum[j]>=maxp[j])f[i]=f[j]+j-i-1(其中i>j)
代码:
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 inline int gi() 5 { 6 int a=0;char x=getchar();bool f=0; 7 while((x<'0'||x>'9')&&x!='-')x=getchar(); 8 if(x=='-')x=getchar(),f=1; 9 while(x>='0'&&x<='9')a=a*10+x-48,x=getchar(); 10 return f?-a:a; 11 } 12 ll n,sum[200005],f[200005],maxp[200005]; 13 int main() 14 { 15 n=gi();int x,i,j; 16 for(i=1;i<=n;i++)x=gi(),sum[i]=sum[i-1]+x; 17 for(i=1;i<=n;i++){ 18 for(j=i-1;j>=0;j--) 19 if(sum[i]-sum[j]>=maxp[j])break; 20 maxp[i]=sum[i]-sum[j]; 21 f[i]=f[j]+i-j-1;maxp[i]=sum[i]-sum[j]; 22 } 23 cout<<f[n]; 24 return 0; 25 }