poj2018【均值比较技巧】【二分】【子段】

1.求一个子段,它的和最大。(动态规划)

ll dp[maxn], a[maxn];
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        dp[i] = a[i];
    }
    for(int i = 1; i <= n; i++)
    {
        dp[i] = max(dp[i], dp[i - 1] + a[i]);
    }
    ll ans = 0;
    for(int i = 1; i <= n; i++)ans = max(ans, dp[i]);
    cout<<ans<<endl;
    return 0;
}

2.求一个子段,他的和最大,子段的长度不小于L.

求一个子段它的和最大且长度不小与L。子段和可以转化为前缀和相减的形式,即设sumi表示ai~aj的和,则:技术分享图片

仔细观察上面的式子容易发现,随着i的增长,j的取值范围0~i-L每次只会增大1。换言之,每次只会有一个新的取值进入min{sumj}的候选集合,所以我们没有必要每次循环枚举j,只需要用一个变量记录当前的最小值,每次与新的取值sumi-L取min就可以了。

于是我们只需要看一下最大子段和是不是非负数就可以确定二分上下界的变化范围了。

(没看懂没事,第一遍我也没看懂,仔细看并对照代码,f**k真的简单巧妙!)

	double min1=1e10;
	double ans=-1e10;
	for(int i=l;i<n;i++){
		min1=min(min1,sum[i-l]);
		ans=max(ans,sum[i]-min1);
	}

3.给定一个正整数数列A,求一个平均数最大的、长度不小于L的子段。
思路

二分平均数为mid,把数组数组都减去mid,若最大和>0,就是平均数取小了。
这就涉及到均值比较的技巧:平均值大小的比较,可以转换为每对对应元素差值之和与0的比较。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
double a[100005],b[100005],sum[100005];

int main(){
    int n,L;
    cin>>n>>L;
    for(int i=0;i<n;i++) scanf("%lf",&a[i]);
    double eps=1e-5;
    double l=-1e6,r=1e6;
    while(r-l>eps){
        double mid=(r+l)/2;
        for(int i=0;i<n;i++) b[i]=a[i]-mid;
        for(int i=0;i<n;i++) sum[i]=sum[i-1]+b[i];
        double ans=-1e10,minval=1e10;
        for(int i=L-1;i<n;i++){
            minval=min(minval,sum[i-L]);
            ans=max(ans,sum[i]-minval);
        }
        if(ans>0) l=mid; else r=mid;
    }
    cout<<int(r*1000)<<endl;
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值