[Luogu2600]合并神犇(dp,贪心)

[Luogu2600]合并神犇

题目背景

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 均为随机生成。

乍一眼看题很容易错想成贪心,将序列一直合并到当前满足条件。
但是我们考虑这样一组数据4 1 3 2 6.....
如果按照我们贪心的思想,合并后的序列会变成4 4 8
但实际上我们考虑序列4 6 6 同样可以从原序列用一样的步骤合并来,而6<8,显然对于后面序列的影响更小。
所以这个贪心是错误的。
想到dp,用(dp[i])表示合并到(i)需用的最小次数,因为从前面的状态状态转移过来,而数据不满足我们用(O(n^2))做,由前面的错误贪心我们可以得知,我们要合并到比下一个数小,这样我们对后面的影响就更小,所以我们直接找到第一个可以更新当前状态的决策来更新即可。
最坏的时间复杂度到了(O(n^2)),但是数据比较水(手动滑稽),于是就顺利的A了
记得long long

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define lll long long
lll read()
{
	int x=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x*w;
}
lll a[200010],dp[200010],f[200010],sum[200010];
int main()
{
	lll n=read();
	for(lll i=1;i<=n;i++)
	{
		a[i]=read();
		sum[i]=sum[i-1]+a[i];
	}
	for(lll i=1;i<=n;i++)
	{
		lll j;
		for(j=i-1;j>=0;j--)
		if(sum[i]-sum[j]>=f[j]) break;
		dp[i]=dp[j]+i-j-1;
		f[i]=sum[i]-sum[j];
	}
	cout<<dp[n];
}
原文地址:https://www.cnblogs.com/lsgjcya/p/9143458.html