読者です 読者をやめる 読者になる 読者になる

srupのメモ帳

競プロで解いた問題や勉強したことを記録していくメモ帳

SRM 690 div2 medium GerrymanderEasy

SRM 累積和

問題

問題概要

AとBが与えられる。そのなかから、連続するK以上の部分のAの合計と、Bの合計をそれぞれ出して、(Bの合計)/(Aの合計) の最大値を求める。

解法

全探索で通る。単純にすべての区間について、(Bの合計)/(Aの合計)を出していけばいい。これだとn=1000でO(n3)になって通らない気がしたが、通った。よくわからない。
O(n2)に落とすことができる。すべての区間を求めていくときに、左の位置を決めて右に伸ばしていく。だから、毎回左の位置から右の位置までなまて合計を出さなくても、右に区間を伸ばしたときは、前に出した区間の合計に区間を伸ばしたところ値を足すだけで、次の区間の合計を求めることができる。

ミス

問題文を読み間違えていて、混乱した。連続した部分ではなく、どこでもよいから、K箇所以上選べばよいと思い、蟻本p132にある平均最大化を行うのかと思い、それを書いてしまったが、サンプル1でおかしくなって気づいた。
consecutive numbersは連続した数字。

コード

区間を伸ばすときに、最右辺のみを足したもの(AC)

#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
#define rep(i,n) for(int i=0;i<(n);i++)
const int INF = 1e9;

class GerrymanderEasy{
public:
    double getmax(vector <int> A, vector <int> B, int K){
        double ans = 0.0;
        //連続しk以上の部分での最大値
        for (int i = 0; i < A.size(); ++i){
            ll sumA = 0, sumB = 0;
            for (int j = i; j < A.size(); ++j){
                sumA += A[j];
                sumB += B[j];
                if(j - i + 1 < K) continue;//k以上
                ans = max(ans, (double)sumB / (double)sumA);
            }
        }
        return ans;
    }
};

ただ、全探索した計算量がとても危ない気がするコード(AC)

#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
#define rep(i,n) for(int i=0;i<(n);i++)
const int INF = 1e9;

class GerrymanderEasy{
public:
    double getmax(vector <int> A, vector <int> B, int K){
        double ans = 0.0;
        //連続しk以上の部分での最大値
        for (int i = 0; i < A.size(); ++i){
            ll sumA = 0, sumB = 0;
            for (int j = i; j < A.size(); ++j){
                sumA += A[j];
                sumB += B[j];
                if(j - i + 1 < K) continue;//k以上
                ans = max(ans, (double)sumB / (double)sumA);
            }
        }
        return ans;
    }
};