Codeforces_484D:Kindergarten(贪心/DP)

12 篇文章 0 订阅
8 篇文章 0 订阅

题目大意是说给定n个数字,要求你把它分成若干段连续的数列,每段有个贡献值,为这段数列中最大值与最小值之差,然后整个数列的贡献值为每段的贡献值之和,现在让你求该数列的最大贡献值.

由简单分析,便可得出每个分得的段落,一定满足:这段数字的最大值与最小值一定分别位于该段数字的边缘两侧,于是基于极值点做次贪心或dp就行...然后在调试了半天终于调对,再与好友的程序对比之后,才发现我的程序是辣么的长(害怕

下面附上我的丑陋的程序:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
using namespace std;
#define ll long long
const int N=1000008;
ll n,a[N];
ll maxx(ll x,ll y){if(x>y)y=x;return y;}
ll minx(ll x,ll y){if(x<y)y=x;return y;}
ll maxx(ll x,ll y,ll z){if(x>z)z=x;if(y>z)z=y;return z;}
ll minx(ll x,ll y,ll z){if(x<z)z=x;if(y<z)z=y;return z;}
int main(void)
{
	ll i,p1,p2,p3,p4,p,pre,mi,ma;
	scanf("%I64d",&n);
	for(i=1;i<=n;i++)scanf("%I64d",a+i);a[0]=a[1];a[n+1]=a[n];
	p1=p2=p=0;pre=1;mi=ma=a[1];
	for(i=1;i<=n;i++)
	if(a[i]<a[i+1])
	{
		if(p>=0){p=1;mi=minx(mi,a[i]);ma=maxx(ma,a[i]);}
		else
		{
			p3=maxx(p1+(maxx(a[pre],ma)-minx(a[pre],mi))*(pre+1!=i),p2+(ma-mi)*(pre+1!=i));
			p4=maxx(p1+maxx(a[pre],ma,a[i])-minx(a[pre],mi,a[i]),p2+maxx(ma,a[i])-minx(mi,a[i]));
			p1=p3;p2=p4;pre=i;p=1;mi=ma=a[i+1];
		}
	}
	else if(a[i]>a[i+1])
	{
		if(p<=0){p=-1;mi=minx(mi,a[i]);ma=maxx(ma,a[i]);}
		else
		{
			p3=maxx(p1+(maxx(a[pre],ma)-minx(a[pre],mi))*(pre+1!=i),p2+(ma-mi)*(pre+1!=i));
			p4=maxx(p1+maxx(a[pre],ma,a[i])-minx(a[pre],mi,a[i]),p2+maxx(ma,a[i])-minx(mi,a[i]));
			p1=p3;p2=p4;pre=i;p=-1;mi=ma=a[i+1];
		}
	}
	else{mi=minx(mi,a[i]);ma=maxx(ma,a[i]);}
	p2=maxx(p1+maxx(a[pre],ma,a[n])-minx(a[pre],mi,a[n]),p2+maxx(ma,a[n])-minx(mi,a[n]));
	cout<<p2;
	return 0;
}

然后再附上学长的短到吓人的程序:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
ll num[1000010],ans;
int main()
{
    int T,t,n,m,i,j,k;
    ll ans=0,a=-1e9,b=-1e9,val;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%I64d",&val);
        a=max(a,ans+val);
        b=max(b,ans-val);
        ans=max(ans,a-val);
        ans=max(ans,b+val);
    }
    printf("%I64d\n",ans);
}

在询问学长&又思考了蛮久之后,终于看懂了学长的程序orz

由之前推出的性质,可以得到每段数列一定是形如[本段最大值...本段最小值]或者[本段最小值...本段最大值]

这样第一种情况的段贡献值便是val左-val右,而第二种情况相应是-val左+val右

这样对于每个读入的val,a存的是最大的"ans+val"即是算出第一种情况的左半边最优值,由于对于每个作为段落最右侧的val来说并不需要知道它左边是怎么决策的,而只需要知道左侧局部最优值,所以只需用a-val即补全右半部分式子后再尝试去更新最后答案,或者换种说法,即a已经算出了"前面分得的若干段贡献值"与"多出来的那半段数列的左端数字"之和,并对于每个val假设当前val为那半段数列的右端值并去更新答案;b也同理.总的来说,相当于ab存储了两种情况下当前各自的最优策略,然后在O(n)枚举时来尝试补全并更新最后的答案.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值