레이블이 12846 무서운 아르바이트인 게시물을 표시합니다. 모든 게시물 표시
레이블이 12846 무서운 아르바이트인 게시물을 표시합니다. 모든 게시물 표시

2017년 11월 12일 일요일

2104 부분배열 고르기, 12846 무서운 아르바이트

2104 부분배열 고르기 https://www.acmicpc.net/problem/2104
12846 무서운 아르바이트 https://www.acmicpc.net/problem/12846

$(\sum_{i}^{N} A_{i})*(min_{i\in{N}} A_{i})$ 최댓값을 구하는 문제다.
따지고보면 둘다 같은 문제이다.

여러가지 방법으로 풀 수 있는데 나는 plane sweeping 방법으로 접근했다.
문제의 입력을 예시로 들겠다.
(사실 위의 그림을 보면 알겠지만 유명한 히스토그램 문제이다.)
여기서 높이가 높은 순으로 최대한 양쪽으로 퍼질 수 있는만큼 퍼지는 방식으로 구했다.

6과 5는 양쪽으로 퍼질 수 없지만 4는 양쪽 하나씩 퍼질 수 있다.
이것을 bucket(묶음)처럼 저장하여 이미 묶여져있는 bucket을 만났을 때 맨 끝쪽으로 이동시키도록
만들었다.

stack을 이용해 $O(N)$만에 해결하는 방식도 이와 유사하지만 이 방법에서는 stack을 쓰지 않고
정렬한다는 차이가있다.
#include <cstdio>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
struct Bucket { int l, r; };
int N;
int arr[100001];
long long sum[100001];
pair<int, int> save[1000001];
vector<pair<int,int>> v;
bool visit[100001];
Bucket bucket[100001];
long long ans = 0;
void update(Bucket &buck, int l, int r) {
    buck.l = l;
    buck.r = r;
}
int main() {
    scanf("%d", &N);
    for (int n = 0;n < N;n++) scanf("%d", &arr[n]), v.push_back({ arr[n],n });
    for (int n = 0;n < N;n++) {
        sum[n] += (long long)arr[n] + (n == 0 ? 0 : sum[n - 1]);
    }
    sort(v.begin(), v.end(), greater<pair<int, int>>());
    for (int n = 0;n < N;++n) {
        int here = v[n].first;
        int idx = v[n].second;
        int l = idx, r = idx;
        if (visit[idx]) continue;
        visit[idx] = true;
        while (l - 1 >= 0 && (arr[l - 1] >= here || visit[l - 1])) {
            if (visit[l - 1]) l = bucket[l - 1].l;
            else visit[--l] = true;
        }
        while (r + 1 < N && (arr[r + 1] >= here || visit[r + 1])) {
            if (visit[r + 1]) r = bucket[r + 1].r;
            else visit[++r] = true;
        }
        update(bucket[l], l, r);
        update(bucket[r], l, r);
        ans = max(ans, (sum[r] - (l == 0 ? 0 : sum[l - 1])) * (long long)here);
    }
    printf("%lld\n", ans);
    return 0;
}
cs

위에서 언급했듯이 히스토그램문제처럼 divide & conquer로 풀 수 있다.
#include <cstdio>
typedef long long ll;
inline ll min(ll a, ll b) { return a > b ? b : a; }
inline ll max(ll a, ll b) { return a < b ? b : a; }
int N;
ll arr[100001];
ll sum[100001];
ll solve(int l, int r) {
    if (l == r) return arr[l] * arr[l];
    int mid = (l + r) >> 1;
    ll ret = max(solve(l, mid), solve(mid + 1, r));
    int left = mid, right = mid + 1;
    ll Min = min(arr[left], arr[right]);
    ret = max(ret, (arr[left] + arr[right]) * Min);
    while (l < left || right < r) {
        if (l == left || (right < r && arr[right + 1] > arr[left - 1])) right++, Min = min(Min, arr[right]);
        else left--, Min = min(Min, arr[left]);
        ret = max(ret, (sum[right] - (left == 0 ? 0 : sum[left - 1]))*Min);
    }
    return ret;
}
int main() {
    scanf("%d", &N);
    for (int n = 0;n < N;n++) scanf("%lld", &arr[n]);
    for (int n = 0;n < N;n++) sum[n] += arr[n] + (n == 0 ? 0 : sum[n - 1]);
    printf("%lld\n", solve(0, N - 1));
    return 0;
}
cs