MAX Average Problem(斜率 dp,二分,终于找到了一个过的,但是看不懂~~~)

MAX Average Problem

给你一个数列,求大于给定长度的区间的最大平均值

可以过的
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int lis[maxn], head, tail;
char IO_BUF[maxn * 250];
char *buf = IO_BUF;
double dp[maxn];
ll sum[maxn], n, k;

inline void read(ll &a) {
    for (a = 0; *buf < 48; buf++);
    while (*buf > 47)
        a = a * 10 + *buf++ - 48;
}

ll getSum(int i, int j) {
    return sum[j] - sum[i];
}

ll getX(int i, int j) {
    return j - i;
}

void getY(int i, int j) {
    dp[j] = max(dp[j - 1], (sum[j] - sum[i]) * 1.0 / (j - i));
}

int main() {
    int tol = fread(IO_BUF, 1, maxn * 250, stdin);
    while (buf - IO_BUF + 1 < tol) {
        read(n);
        read(k);
        for (int i = 1; i <= n; i++) {
            read(sum[i]);
            sum[i] += sum[i - 1];
        }
        memset(dp, 0, sizeof(dp));
        head = tail = 0;
        for (int i = k; i <= n; i++) {
            while (head + 1 < tail && getSum(lis[tail - 1], i - k) * getX(lis[tail - 2], lis[tail - 1]) <=
                                      getX(lis[tail - 1], i - k) * getSum(lis[tail - 2], lis[tail - 1])) {
                --tail;
            }
            lis[tail++] = i - k;
            while (head + 1 < tail && getSum(lis[head], i) * getX(lis[head + 1], i) <=
                                      getX(lis[head], i) * getSum(lis[head + 1], i)) {
                ++head;
            }
            getY(lis[head], i);
        }
        printf("%.2f\n", dp[n]);
    }
    return 0;
}
直接套模板会超时
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>

using namespace std;
const int maxn = 1e5 + 100;
int n, k, head, tail;
double sum[maxn], dp[maxn];
int q[maxn], a[maxn];

//输入加速
int input() {
    char ch = ' ';
    int num = 0;
    while (ch < '0' || ch > '9') {
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        num = num * 10 + ch - '0';
        ch = getchar();
    }
    return num;
}

int gety(int y1, int y2) {
    return sum[y2] - sum[y1];
}

int getx(int x1, int x2) {
    return x2 - x1;
}

double solve() {
    for (int i = 1; i <= n; i++) {
        sum[i] = sum[i - 1] + a[i] * 1.0;
    }
    head = 0, tail = 1;
    q[head = 0];
    double ans = 0;
    for (int i = k; i <= n; i++) {
        int j = i - k;
        while (head + 1 < tail && gety(q[tail - 1], j) * getx(q[tail - 2], q[tail - 1]) <=
                                  gety(q[tail - 2], q[tail - 1]) * getx(q[tail - 1], j)) {
            tail--;
        }
        q[tail++] = j;
        while (head + 1 < tail && gety(q[head], i) * getx(q[head + 1], i) <= gety(q[head + 1], i) * getx(q[head], i)) {
            head++;
        }
        dp[i] = (sum[i] - sum[q[head]]) / (i - q[head]);
        ans = max(ans, dp[i]);
    }
    return ans;
}

int main() {
    while (scanf("%d%d", &n, &k) != EOF) {
        for (int i = 1; i <= n; i++) {
            a[i] = input();
        }
        printf("%0.2lf\n", solve());
    }
    return 0;
}
需要二分查找
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;
#define ll long long
const int maxn = 1e5 + 100;
int n, k, head, tail;
double sum[maxn], dp[maxn];
int q[maxn], a[maxn];

//输入加速
int input() {
    char ch = ' ';
    int num = 0;
    while (ch < '0' || ch > '9') {
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        num = num * 10 + ch - '0';
        ch = getchar();
    }
    return num;
}

int gety(int y1, int y2) {
    return sum[y2] - sum[y1];
}

int getx(int x1, int x2) {
    return x2 - x1;
}

ll check(int mid, int i) {
    return gety(q[mid + 1], i) * getx(q[mid], q[mid + 1]) - gety(q[mid], q[mid + 1]) * getx(q[mid + 1], i);
}

int search(int l, int r, int i) {
    //由于斜率单调递增
    /*int top=r;
    while(l<=r){//根据i与mid的斜率 和 i与mid+1的斜率之差求切点
        if(l == r && l == top)return q[l];//这里一定要注意如果切点是最后一个点需要另判,因为mid+1不存在会出错
        int mid=(l+r)>>1;
        if(check(mid,i)<0)r=mid-1;
        else l=mid+1;
    }*/
    while (l < r) {//根据i与mid的斜率 和 i与mid+1的斜率之差求切点
        int mid = (l + r) >> 1;
        if (check(mid, i) < 0)r = mid;
        else l = mid + 1;
    }
    return q[l];
}

double solve() {
    for (int i = 1; i <= n; i++) {
        sum[i] = sum[i - 1] + a[i] * 1.0;
    }
    int p;//q[head]
    head = 0, tail = 1;
    q[head = 0];
    double ans = 0;
    for (int i = k; i <= n; i++) {
        int j = i - k;
        while (head + 1 < tail && gety(q[tail - 1], j) * getx(q[tail - 2], q[tail - 1]) <=
                                  gety(q[tail - 2], q[tail - 1]) * getx(q[tail - 1], j)) {
            tail--;
        }
        q[tail++] = j;
        p = search(head, tail - 1, i);
        dp[i] = (sum[i] - sum[p]) / (i - p);
        if (dp[i] > ans)ans = dp[i];
    }
    return ans;
}

int main() {
    while (scanf("%d%d", &n, &k) != EOF) {
        for (int i = 1; i <= n; i++) {
            a[i] = input();
        }
        printf("%0.2lf\n", solve());
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值