Codeforces_484D:Kindergarten(贪心/DP)

来源:互联网 发布:人工智能的基金有哪些 编辑:程序博客网 时间:2024/06/07 02:08

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

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

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

#include<iostream>#include<stdio.h>#include<string.h>#include<stdlib.h>using namespace std;#define ll long longconst 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
原创粉丝点击